Repository navigation
Conversation
… handshake secret
…ule-level import Change-Id: I533b2c7e1bb78ff725a74db5b00bb701a082b1df
Change-Id: I33e67c9868b186353eff3857406b62ffbfa97eb2
…se for explicit-mode Change-Id: I030357650201f8681a31d3135cbbff726a4aa650
…rror Change-Id: I8d4bf5acf04b72af47e6d65829b41d7d8d800165
…User-Agent prefixing Pass --caller-user-agent python-v<version> when spawning the daemon so it can construct the full User-Agent prefix (e.g. python-v3.0.0a0-go-accelerator-v3.x.x) for accurate audit logging and traffic attribution. Change-Id: I621217335ad54377a84d44ab4b9af88770dd2958
…patch, and daemon lifecycle
- Remove _AcceleratorUnverified alias (no longer needed) - Remove duplicate User-Agent flags block from _init_accelerator_config; keep only the early block so the flag is forwarded even when the daemon is blocked - Move _AcceleratorIdentityError handling into _start_accelerator: close daemon, warn 'could not verify daemon identity', return without re-raising - Remove test_binary_path_param_bypasses_lookup (binary_path is a test-only knob already exercised implicitly) - Add test_close_clears_all_state to verify _proc/_tempdir/_uds_path/_log_path/_auth_secret are all None after close() - Add --app-profile to TestCliFlags.test_cli_flags_appended_to_argv - Add assert not t._accelerator_breaker.bypass() to test_non_grpc_exception_reraises in both TestMutateRowViaAccelerator and TestReadRowViaAccelerator Change-Id: Iff4489acc63a6e71211939c23db04ff1a5df10ba review(bigtable): address PR #18686 reviewer comments - Remove _AcceleratorUnverified alias (no longer needed) - Remove duplicate User-Agent flags block from _init_accelerator_config; keep only the early block so the flag is forwarded even when the daemon is blocked - Add local import google.cloud.bigtable inside _init_accelerator_config to avoid potential circular import at module level - Move _AcceleratorIdentityError handling into _start_accelerator: close daemon, warn 'could not verify daemon identity', return without re-raising - Remove test_binary_path_param_bypasses_lookup (binary_path is a test-only knob already exercised implicitly) - Add --app-profile to TestCliFlags.test_cli_flags_appended_to_argv - Add assert not t._accelerator_breaker.bypass() to test_non_grpc_exception_reraises in both TestMutateRowViaAccelerator and TestReadRowViaAccelerator Change-Id: I43b354ad6517a2c0b9f6a2294cdd2d1639d7fe8c test(bigtable): add TestCleanupTempdir to verify _cleanup_tempdir clears all state Change-Id: I6d6ec51cc039c78d884451502b368862fe8d4ae3
…e auth session Change-Id: I78f226004fdffe98d83f7d24b04557a24e9fc7a7
…o prevent orphaned daemon on BaseException Change-Id: I6f4c18f5b03da26efa9b70ed8c6b4d20f9d4a8df
Change-Id: Iad44827bd86e405524b5aad787e881e2cbf30d14
…rtup timeout Change-Id: I44684facdfe1f9a1b8807b78bbb4166f8c9ff26b
…ard and Windows skipif Change-Id: Iba3a175c070fb5f12db54136096f2792bb7f3b7f
…t grace window regardless of sweeper's env Change-Id: Idf216c34aa7924fd61f258c997c71c426a2cbe34
…ransient _socket_is_live false negatives Change-Id: If3a0174967e750f04c26b49ccf3e0a4faf4764e0
…sient OSErrors instead of double-probing Change-Id: Ie6e858f577904cda5a06684afdb9d3aff21dd5ae
…f socket probe Change-Id: Ifa69e49da685ec3e3e4cd51ec344fb78d5c8f417
… sources and tests Change-Id: I978a0c1e846f3b95aa2fe5d15dabafec4f0576f9
… checks fix(bigtable): lock all reads and writes in AcceleratorBreaker Change-Id: I9d1daffbe086788481d6802cec035a3d4d284729
…, clear stale auth_secret on teardown Change-Id: I02c7ecb2d9ac5aa082bec0dc3325938b621bd210
Change-Id: I6665fec71ca24efe8ec47f95a71de6dda59abe5e
…), and breaker lock Change-Id: I21cf617dbea7aa85dc7b0d093e15c1ea22c7f95c
Change-Id: I7a17dd9919dff6e9d38b0cdfb3c600ffc363900c
Change-Id: I6adb2982640fe2d6736ea63ed165c9ffd6feacb7
There was a problem hiding this comment.
Code Review
This pull request introduces integration and concurrency tests for the Google Cloud Bigtable accelerator, including pytest configurations, base test classes, a state-tracking test harness, and multi-process/multi-threaded concurrency tests. The review feedback suggests several key improvements: cleaning up empty row dictionaries from the state model in _harness.py to prevent memory retention, switching the multiprocessing start method from fork to spawn in test_concurrency_async.py to avoid gRPC-related deadlocks, and adopting a shared monotonic deadline pattern when joining background processes during cleanup to prevent excessive blocking.
| if not col: | ||
| self._rows[row_key].pop((family, qualifier), None) |
There was a problem hiding this comment.
When a column qualifier is completely deleted and the column dictionary becomes empty, it is popped from the row. However, if the row itself becomes empty (i.e., has no more column families/qualifiers), the row key is still left in self._rows as an empty dictionary. This causes keys() to return empty rows and leads to unnecessary memory retention. We should clean up the row key if the row dictionary becomes empty.
| if not col: | |
| self._rows[row_key].pop((family, qualifier), None) | |
| if not col: | |
| self._rows[row_key].pop((family, qualifier), None) | |
| if not self._rows[row_key]: | |
| self._rows.pop(row_key, None) |
| def delete_from_family(self, row_key: bytes, family: str) -> DeleteAllFromFamily: | ||
| row = self._rows.get(row_key) | ||
| if row is not None: | ||
| for key in [k for k in row if k[0] == family]: | ||
| del row[key] | ||
| return DeleteAllFromFamily(family) |
There was a problem hiding this comment.
Similarly to delete_range_from_column, if all families/qualifiers are deleted from a row during delete_from_family, the row key should be completely removed from self._rows to prevent empty dictionaries from lingering in the state.
| def delete_from_family(self, row_key: bytes, family: str) -> DeleteAllFromFamily: | |
| row = self._rows.get(row_key) | |
| if row is not None: | |
| for key in [k for k in row if k[0] == family]: | |
| del row[key] | |
| return DeleteAllFromFamily(family) | |
| def delete_from_family(self, row_key: bytes, family: str) -> DeleteAllFromFamily: | |
| row = self._rows.get(row_key) | |
| if row is not None: | |
| for key in [k for k in row if k[0] == family]: | |
| del row[key] | |
| if not row: | |
| self._rows.pop(row_key, None) | |
| return DeleteAllFromFamily(family) |
| self, instance_id, table_id, janitor | ||
| ): | ||
| project = os.getenv("GOOGLE_CLOUD_PROJECT") or None | ||
| ctx = multiprocessing.get_context("fork") |
There was a problem hiding this comment.
Using the fork start method in Python programs that use gRPC or other multi-threaded C-extensions can lead to deadlocks, crashes, or undefined behavior in child processes because background threads are not inherited. Since the child process workload is fully self-contained and all arguments are pickleable, using spawn is much safer and more robust across different platforms (including macOS and Windows).
| ctx = multiprocessing.get_context("fork") | |
| ctx = multiprocessing.get_context("spawn") |
| for proc in procs: | ||
| proc.join(timeout=30) | ||
| if proc.is_alive(): | ||
| proc.kill() | ||
| proc.join() |
There was a problem hiding this comment.
When joining multiple background processes during cleanup, using a fixed timeout per process can cause the cleanup block to block for an excessively long time if multiple processes hang (e.g., up to 120 seconds for 4 processes). Using a shared timeout/deadline pattern based on a monotonic clock ensures that the entire cleanup block respects the overall timeout limit.
| for proc in procs: | |
| proc.join(timeout=30) | |
| if proc.is_alive(): | |
| proc.kill() | |
| proc.join() | |
| import time | |
| deadline = time.monotonic() + 30.0 | |
| for proc in procs: | |
| timeout = max(0.0, deadline - time.monotonic()) | |
| proc.join(timeout=timeout) | |
| if proc.is_alive(): | |
| proc.kill() | |
| proc.join() |
References
- When joining multiple background threads during cleanup, use a shared timeout/deadline pattern (e.g., calculating remaining time based on a monotonic clock) instead of a fixed timeout per thread, to prevent the cleanup block from blocking for an excessively long time if multiple threads hang.
Add the shared Cloud Bigtable accelerator system-test harness (
_harness.py,_base_async.py,_base_autogen.py,conftest.py,pytest.ini) and the concurrent/multi-process workload system tests (test_concurrency_async.py,test_concurrency_autogen.py).Part of Stack #18691 (stacked on #18782).