Skip to content

chore: add test sharding to unit tests - #17438

Open
daniel-sanche wants to merge 94 commits into
mainfrom
ci_sharding
Open

chore: add test sharding to unit tests#17438
daniel-sanche wants to merge 94 commits into
mainfrom
ci_sharding

Conversation

@daniel-sanche

@daniel-sanche daniel-sanche commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Added test sharding for unit tests:

  • added initialize job to unit test CI. It analyzes the modified packages, and spawns up to MAX_SHARDS jobs for each supported Python runtime
  • long-running packages can have a multiplier added in PACKAGE_WEIGHTS. This will cause the system to reserve more time for long-running tests, to better distribute the workload across shards
  • shards are added according to the formula `shards_used = min(1, max(MAX_SHARDS, total_package_weight / 10))
  • adds an end unit test complete step, which is only green if all shards pass. This can be our new required check for unit tests
  • if any single unit test shard fails, the remaining end early to provide quick feedback

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces parallel execution for system tests and implements a sharding mechanism for CI jobs, including a new Python script to group packages and updates to the test runner script. Feedback on these changes focuses on improving reliability and safety: first, by using an EXIT trap in .kokoro/system.sh to guarantee cleanup of isolated gcloud configuration directories in case of test failures; and second, by avoiding global toggles of set -e in ci/run_conditional_tests.sh and instead capturing test exit codes using the || operator.

Comment thread .kokoro/system.sh Outdated
Comment thread .kokoro/system.sh Outdated
Comment thread ci/run_conditional_tests.sh Outdated
@daniel-sanche daniel-sanche changed the title [DRAFT] chore: add test sharding [DRAFT] experiment: add test sharding Jun 12, 2026
@daniel-sanche
daniel-sanche marked this pull request as ready for review July 7, 2026 21:01
@daniel-sanche
daniel-sanche requested review from a team as code owners July 7, 2026 21:01
@daniel-sanche daniel-sanche added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 7, 2026
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 7, 2026
@daniel-sanche daniel-sanche added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 7, 2026
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 7, 2026
Comment thread packages/sqlalchemy-spanner/.coveragerc Outdated
google/cloud/sqlalchemy_spanner/requirements.py

[report]
fail_under = 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? Is there really no code coverage with the existing tests?

Evaluating coverage for package: packages/sqlalchemy-spanner
============================================================
Package has fail_under = 0, passing instantly with 100% success (even without coverage files)

https://github.com/googleapis/google-cloud-python/tree/main/packages/sqlalchemy-spanner/tests/unit

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage wasn't being checked by this library before now, so this is representing the status quo. This is owned by a partner team, so I was thinking we could let them decide if/when to raise the bar.

But yeah, maybe it makes more sense to at least add a floor, so coverage doesn't accidentally drop further. The current value is 36, so I set the target to 35

matrix: ${{ steps.set-matrix.outputs.matrix }}
is_full_run: ${{ steps.check-label.outputs.is_full_run }}
env:
MAX_SHARDS: 20

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know the concurrency limit for our org?

In cases where we do 20*5 runtimes that we support, we might end up bloating the CI and see queued jobs in any PRs opened up in parallel.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know the exact numbers, but I think it's O(500). I set this as 20 to try to strike a good balance between time vs jobs, but we can tune it if needed.

(Note that the 20 shards will only be used for PRs touching 200 packages, so we shouldn't expect to have too many of those running in parallel)

Comment thread ci/report_coverage.sh Outdated

# Generate report
if [ -f ".coveragerc" ]; then
echo "Using package-specific configuration: ${pkg}/.coveragerc" > "${pkg_log}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this result in truncating the logs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, it looks like it could cut off the line before it. Fixed

Comment thread ci/get_package_shards.py
try:
res = subprocess.check_output(['git', 'diff', '--name-only', git_diff_arg]).decode('utf-8')
changed_files = res.splitlines()
except subprocess.CalledProcessError:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a brief comment here explaining the fallback behaviour?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

Comment thread ci/get_package_shards.py
if not os.path.exists(subdir):
continue
# Use the same sorting as the shell script
pkg_dirs = [os.path.join(subdir, d) + '/' for d in os.listdir(subdir) if os.path.isdir(os.path.join(subdir, d))]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to filter out hidden packages?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, do we? I thought all packages have unit tests. Which ones would be candidates to filter out?

@ohmayr

ohmayr commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

We're using a mix of bash / python scripting. Can we write it all up in Python? is there a reason not to?

@daniel-sanche

Copy link
Copy Markdown
Contributor Author

We're using a mix of bash / python scripting. Can we write it all up in Python? is there a reason not to?

They are different tools with different use-cases.

Bash is usually better for these GitHub Actions jobs, since they typically run on a lightweight linux container. It's simple and stable, and lets us avoid Python runtime/dependency issues. Bash is also what we use for the other scripts in the ci/ directory, so that's the default choice.

But Bash isn't very good for complex scripts (especially involving JSON/dictionaries), so I opted to use Python for get_package_shards, so we can better tune and reason about the sharding logic

If we wanted it to be consistent, I could convert get_package_shards to bash to match the other scripts. But I think Python is probably the right tool for the job here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: firestore Issues related to the Firestore API. unit_test:all_packages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants