chore(firestore): optimize system tests runtime#17418
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces parallel execution for system tests using pytest-xdist and updates several fixtures in both synchronous and asynchronous system tests to be module-scoped. The review feedback highlights critical issues with these changes: first, changing async fixtures to module scope in test_system_async.py will cause a ScopeMismatch error unless a module-scoped event_loop is defined; second, a similar ScopeMismatch error may occur in test_system.py if the database fixture is function-scoped; and finally, to preserve the caching benefits of these module-scoped fixtures under pytest-xdist, the test distribution mode should be configured with --dist=loadfile instead of the default behavior.
|
|
||
|
|
||
| @pytest_asyncio.fixture | ||
| @pytest_asyncio.fixture(scope="module") |
There was a problem hiding this comment.
In pytest-asyncio (especially with the pinned version 0.21.2 in noxfile.py), the default event_loop fixture is function-scoped. Defining an async fixture with scope="module" (like query_docs, collection, and async_query) will raise a ScopeMismatch error during test collection or execution.
To resolve this, you must define a module-scoped event_loop fixture in this file or in conftest.py to override the default one.
@pytest.fixture(scope="module")
def event_loop():
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest_asyncio.fixture(scope="module")| "-n", | ||
| "auto", |
There was a problem hiding this comment.
When running system tests in parallel with pytest-xdist (-n auto), the default distribution mode is --dist=load, which distributes individual tests to any available worker. This can cause tests from the same module to run on different workers, meaning module-scoped fixtures (like query_docs which was changed to scope="module" in this PR) will be executed multiple times (once per worker), defeating the caching benefit.
To ensure that all tests within a single module run on the same worker and preserve the efficiency of module-scoped fixtures, consider using --dist=loadfile.
| "-n", | |
| "auto", | |
| "-n", | |
| "auto", | |
| "--dist", | |
| "loadfile", |
| "-n", | ||
| "auto", |
There was a problem hiding this comment.
When running system tests in parallel with pytest-xdist (-n auto), the default distribution mode is --dist=load, which distributes individual tests to any available worker. This can cause tests from the same module to run on different workers, meaning module-scoped fixtures (like query_docs which was changed to scope="module" in this PR) will be executed multiple times (once per worker), defeating the caching benefit.
To ensure that all tests within a single module run on the same worker and preserve the efficiency of module-scoped fixtures, consider using --dist=loadfile.
| "-n", | |
| "auto", | |
| "-n", | |
| "auto", | |
| "--dist", | |
| "loadfile", |
| @pytest.fixture(scope="module") | ||
| def query_docs(client, database): |
80d97d6 to
5ce929d
Compare
5ce929d to
a3b5b1d
Compare
| count: 1 | ||
| # TODO(https://github.com/googleapis/google-cloud-python/issues/17429): | ||
| # Temporary post-processing rule to add pytest-xdist dependency. | ||
| # Remove this once gapic-generator includes pytest-xdist by default. |
There was a problem hiding this comment.
I'm surprised firestore uses a generated noxfile. I thought most veneers had their own customized copies
It looks like bigtable has the entire noxfile contents in a post-processing script now? This seems like something we might want to revisit, but probably out of scope for this PR
There was a problem hiding this comment.
Yes. Firestore is not completely handwritten. We override things via post-processing. There's many ways to go about this. This is more so a design change i.e. we could make it completely handwritten or have the entire file in post processing script or implement something on the generator side too.
But yes, out of scope for this work.
| # TODO(https://github.com/googleapis/google-cloud-python/issues/17429): | ||
| # Temporary post-processing rule to inject `-n auto` for Firestore parallel tests. | ||
| # This rule should be removed once the generator template changes are released | ||
| # and the generator version is updated in librarian.yaml. |
There was a problem hiding this comment.
Is the plan to include -n auto for all system tests?
There was a problem hiding this comment.
Let's have this discussion on this PR: #17430
| DOCUMENT_EXISTS = "Document already exists: " | ||
| ENTERPRISE_MODE_ERROR = "only allowed on ENTERPRISE mode" | ||
| UNIQUE_RESOURCE_ID = unique_resource_id("-") | ||
| UNIQUE_RESOURCE_ID = unique_resource_id("-") + "-" + str(os.getpid()) |
There was a problem hiding this comment.
If unique_resource_id() isn't unique enough, this logic should probably be added there, instead of appended afterwards
There was a problem hiding this comment.
Good catch! We can address this as a follow up or determine why it won't work.
| after: | | ||
| "pytest-asyncio==0.21.2", | ||
| count: 2 | ||
| # TODO(https://github.com/googleapis/google-cloud-python/issues/17429): |
There was a problem hiding this comment.
What's the plan for these TODOs? Can you make sure we follow through with them, instead of leaving them in the backlog?
There was a problem hiding this comment.
Well! Depends if we want to work on flaky tests within this fixit and have the time for it. I'll sync on this one and if not a priority, we can close this bug.
Optimizes Firestore system tests execution time to 13 minutes (down from ~1 hour) under live GCP environments.
Parallelism: Added pytest-xdist using all cores (--dist load for maximum load balancing).
Isolation: Appended os.getpid() to test resource IDs to prevent parallel worker collisions.
Fixture Re-use: Promoted query setup fixtures to module scope to reduce database setups by ~97%.
Fast Polling: Reduced hardcoded delays in watch tests from 1.0s to 0.2s with early-exit polling.