feat(storage): add enable_bucket_metadata_cache opt-out - #18203
Conversation
googleapis#17489 made SSLError globally non-retryable, but _should_retry is the base predicate for every retry surface: a single transient TLS reset (SSLEOFError) now fails jobs.get / result() polling outright, where 3.40.1 retried it as a ConnectionError subclass. Remove SSLError from the global non-retryable set and keep the googleapis#17489 carve-out only on the insertAll path via a scoped predicate (INSERT_ROWS_DEFAULT_RETRY): malformed streaming payloads still fail fast, while job polling keeps retrying transient transport resets. Fixes googleapis#18178
The background bucket-metadata cache (ACO) probes storage.buckets.get on object-level operations, producing denied-audit-log noise for principals with object-only IAM roles and no supported way to disable it. Add a keyword-only constructor flag backed by the existing pattern: when False, no cache is instantiated, so the probe never fires. close() already guards None. Fixes googleapis#17650
|
Hi @zhixiangli @ohmayr — this adds the supported opt-out requested in #17650: Tests added (default-on, opt-out, close-with-disabled), 423 passed across client/bucket/cache suites, black-clean. CLA signed. Happy to adjust to maintainers' preferred shape (env var instead/also) if you'd rather. |
There was a problem hiding this comment.
Code Review
This pull request scopes the SSLError retry carve-out in google-cloud-bigquery specifically to the streaming-insert path, ensuring other operations continue to retry transient TLS resets. It also adds an option to disable the background bucket-metadata cache in google-cloud-storage. The review feedback recommends using a No-Op cache implementation or adding explicit None checks to avoid potential AttributeErrors when the cache is disabled, and suggests removing an unused import in the unit tests.
| self._bucket_metadata_cache = ( | ||
| BucketMetadataCache(self) if enable_bucket_metadata_cache else None | ||
| ) |
There was a problem hiding this comment.
Setting _bucket_metadata_cache to None when enable_bucket_metadata_cache is False can lead to AttributeErrors if other parts of the codebase (such as bucket operations or trace helpers) attempt to access its methods (e.g., get, set, clear) without checking for None. To prevent potential runtime crashes, consider using a No-Op cache implementation that conforms to the BucketMetadataCache interface but performs no operations, or add explicit None checks before all accesses to _bucket_metadata_cache across the codebase.
References
- Specifically enforce defensive programming: for languages that support nullable references (e.g., Go, Python, Java), ensure appropriate null/nil/None checks or other language-idiomatic guards exist before object property accesses.
| from types import MethodType | ||
| from google.cloud.bigquery.retry import INSERT_ROWS_DEFAULT_RETRY, _should_retry_insert_rows |
There was a problem hiding this comment.
The import from types import MethodType is unused in this test and should be removed to keep the code clean.
References
- Imports should be clean and unused imports should be removed (PEP 8). (link)
|
Thanks for the review. Verified the None-safety concern against the code before responding: All three access sites already guard against a missing/None cache (the codebase has done this since the cache was introduced):
So a Unused |
Fixes #17650
The background bucket-metadata cache (ACO) triggers
storage.buckets.geton object-only operations likelist_blobs/download_*, producing a continuous stream of denied-storage.buckets.getaudit-log entries (ERROR severity) for principals granted object-only IAM roles — with no supported opt-out (the only workaround is mutating the private_bucket_metadata_cacheattribute).Change: new keyword-only constructor flag
enable_bucket_metadata_cache: bool = True(mirroring the existingapi_keypattern). WhenFalse, noBucketMetadataCacheis instantiated, socreate_trace_span_helper's probe is inert — no background thread, no denied audit entries.close()already guards against a missing cache;transfer_manager's client reconstruction is unaffected (it only carries_initial_client_info/).Tests added (test_client.py):
_bucket_metadata_cacheto Noneclose()works with the cache disabledValidation: