-
Notifications
You must be signed in to change notification settings - Fork 0
Comparing changes
Open a pull request
base repository: waiho-gumloop/python-spanner
base: main
head repository: googleapis/python-spanner
compare: main
- 15 commits
- 250 files changed
- 11 contributors
Commits on Feb 11, 2026
-
docs(spanner): snippet for setting read lock mode (googleapis#1473)
Snippet shows how to set the read lock mode at the client-level and how to override the option at the transaction-level.
Configuration menu - View commit details
-
Copy full SHA for 7e79920 - Browse repository at this point
Copy the full SHA 7e79920View commit details -
fix(metrics): prevent thread leak by ensuring singleton initialization (
googleapis#1492) ****Summary:**** This PR fixes a critical memory and thread leak in the google-cloud-spanner client when built-in metrics are enabled (default behavior). Previously, the Client constructor unconditionally initialized a new OpenTelemetry MeterProvider and PeriodicExportingMetricReader on every instantiation. Each reader spawned a new background thread for metric exporting that was never cleaned up or reused. In environments where Client objects are frequently created (e.g., Cloud Functions, web servers, or data pipelines), this caused a linear accumulation of threads, leading to RuntimeError: can't start new thread and OOM crashes. ****Fix Implementation:**** ***Refactored Metrics Initialization (Thread Safety & Memory Leak Fix)***: Implemented a Singleton pattern for the OpenTelemetry MeterProvider using threading.Lock to prevent infinite background thread creation (memory leak). Moved metrics initialization logic to a cleaner helper function _initialize_metrics in client.py. Replaced global mutable state in SpannerMetricsTracerFactory with contextvars.ContextVar to ensure thread-safe, isolated metric tracing across concurrent requests. Updated MetricsInterceptor and MetricsCapture to correctly use the thread-local tracer. ***Fixed Batch.commit Idempotency (AlreadyExists Regression):*** Modified Batch.commit to initialize nth_request and the attempt counter outside the retry loop. This ensures that retries (e.g., on ABORTED) reuse the same Request ID, allowing Cloud Spanner to correctly deduplicate requests and preventing spurious AlreadyExists (409) errors. ***Verification:*** Added tests/unit/test_metrics_concurrency.py to verify tracer isolation and thread safety. Cleaned up tests/unit/test_metrics.py and consolidated mocks in conftest.py.
Configuration menu - View commit details
-
Copy full SHA for e792136 - Browse repository at this point
Copy the full SHA e792136View commit details
Commits on Feb 13, 2026
-
chore: librarian release pull request: 20260213T101303Z (googleapis#1497
) PR created by the Librarian CLI to initialize a release. Merging this PR will auto trigger a release. Librarian Version: v1.0.0 Language Image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209 <details><summary>google-cloud-spanner: 3.63.0</summary> ## [3.63.0](googleapis/python-spanner@v3.62.0...v3.63.0) (2026-02-13) ### Features * add requestID info in error exceptions (googleapis#1415) ([2c5eb96](googleapis@2c5eb96c)) ### Bug Fixes * prevent thread leak by ensuring singleton initialization (googleapis#1492) ([e792136](googleapis@e792136a)) ### Documentation * snippet for setting read lock mode (googleapis#1473) ([7e79920](googleapis@7e79920c)) </details>
Configuration menu - View commit details
-
Copy full SHA for 64f1dbf - Browse repository at this point
Copy the full SHA 64f1dbfView commit details
Commits on Feb 19, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 600c136 - Browse repository at this point
Copy the full SHA 600c136View commit details
Commits on Feb 26, 2026
-
feat: add TLS/mTLS support for experimental host (googleapis#1479)
Previously googleapis#1452 introduced changes to support python spanner client against spanner experimental host endpoints over insecure communication This PR extends those changes to support python spanner client connections to experimental host endpoints over TLS / mTLS connections as well. It also includes changes to run Integration Tests against experimental hosts across all 3 modes of network communication (plain-text, TLS, mTLS) To run IT tests against experimental host set below variables ``` export SPANNER_EXPERIMENTAL_HOST=localhost:15000 ``` For tls/mTLS set below additonal variables: - (mTLS/TLS) ``` export CA_CERTIFICATE=/tmp/experimental_host/ca-certificates/ca.crt ``` - (mTLS) ``` export CLIENT_CERTIFICATE=/tmp/experimental_host/certs/client.crt export CLIENT_KEY=/tmp/experimental_host/certs/client.key ``` Then we can run below command to tigger the tests: ``` python -m pytest -v -s --disable-warnings tests/system/ ``` --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: gcf-merge-on-green[bot] <60162190+gcf-merge-on-green[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for 12773d7 - Browse repository at this point
Copy the full SHA 12773d7View commit details
Commits on Mar 3, 2026
-
feat(spanner): add Client Context support to options (googleapis#1499)
Re-opening googleapis#1495 due to permissions issues. Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/python-spanner/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [ ] Ensure the tests and linter pass - [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) Fixes #<issue_number_goes_here> 🦕 --------- Co-authored-by: Knut Olav Løite <koloite@gmail.com>
Configuration menu - View commit details
-
Copy full SHA for 9fcb647 - Browse repository at this point
Copy the full SHA 9fcb647View commit details
Commits on Mar 13, 2026
-
feat(dbapi): use inline begin to eliminate BeginTransaction RPC (goog…
…leapis#1502) Fixes googleapis/google-cloud-python#15871 ## Summary `Connection.transaction_checkout()` currently calls `Transaction.begin()` explicitly before the first query, which sends a standalone `BeginTransaction` gRPC RPC. This is unnecessary because the `Transaction` class already supports **inline begin** — piggybacking `BeginTransaction` onto the first `ExecuteSql`/`ExecuteBatchDml` request via `TransactionSelector(begin=...)`. This PR removes the explicit `begin()` call, letting the existing inline begin logic in `execute_sql()`, `execute_update()`, and `batch_update()` handle transaction creation. This saves **one gRPC round-trip per transaction** (~16ms measured on the emulator). ### What changed **`google/cloud/spanner_dbapi/connection.py`** — `transaction_checkout()`: - Removed `self._transaction.begin()` on [L413](https://github.com/googleapis/python-spanner/blob/v3.63.0/google/cloud/spanner_dbapi/connection.py#L413) - The transaction is now returned with `_transaction_id=None` - Updated docstring to explain the inline begin behavior **`tests/unit/spanner_dbapi/test_connection.py`**: - Added `test_transaction_checkout_does_not_call_begin` to assert `begin()` is not called **`tests/mockserver_tests/test_dbapi_inline_begin.py`** (new): - 9 mockserver tests verifying inline begin behavior for read-write transactions - Covers: no `BeginTransactionRequest` sent, first `ExecuteSqlRequest` uses `TransactionSelector(begin=...)`, transaction ID reuse on second statement, rollback, read-only unaffected, retry after abort **`tests/mockserver_tests/test_tags.py`**: - Updated 4 read-write tag tests: removed `BeginTransactionRequest` from expected RPC sequences, adjusted tag index offsets **`tests/mockserver_tests/test_dbapi_isolation_level.py`**: - Updated 4 isolation level tests: verify isolation level on `ExecuteSqlRequest.transaction.begin.isolation_level` instead of `BeginTransactionRequest.options.isolation_level` ### Why this is safe The inline begin code path already exists and is battle-tested — `Session.run_in_transaction()` creates a `Transaction` without calling `begin()` and relies on the same inline begin logic ([session.py L566](https://github.com/googleapis/python-spanner/blob/v3.63.0/google/cloud/spanner_v1/session.py#L566)). Specific safety analysis: 1. **`transaction_checkout()` callers always execute SQL immediately**: It's only called from `run_statement()` → `execute_sql()` and `batch_dml_executor` → `batch_update()`. Both set `_transaction_id` via inline begin before any commit/rollback path. 2. **`execute_sql`/`execute_update`/`batch_update` handle `_transaction_id is None`**: They acquire a lock, use `_make_txn_selector()` which returns `TransactionSelector(begin=...)`, and store the returned `_transaction_id` ([transaction.py L612-L623](https://github.com/googleapis/python-spanner/blob/v3.63.0/google/cloud/spanner_v1/transaction.py#L612-L623)). 3. **`rollback()` handles `_transaction_id is None`**: Skips the RPC — correct when no server-side transaction exists ([transaction.py L163](https://github.com/googleapis/python-spanner/blob/v3.63.0/google/cloud/spanner_v1/transaction.py#L163)). 4. **`commit()` handles `_transaction_id is None`**: Falls back to `_begin_mutations_only_transaction()` for mutation-only transactions ([transaction.py L263-L267](https://github.com/googleapis/python-spanner/blob/v3.63.0/google/cloud/spanner_v1/transaction.py#L263-L267)). 5. **Retry mechanism is compatible**: `_set_connection_for_retry()` resets `_spanner_transaction_started=False`, so replayed statements go through `transaction_checkout()` again, create a fresh `Transaction`, and use inline begin. ### PEP 249 conformance This change is fully conformant with [PEP 249 (DB-API 2.0)](https://peps.python.org/pep-0249/). The spec does not define a `begin()` method — transactions are implicit. The PEP author [clarified on the DB-SIG mailing list](https://mail.python.org/pipermail/db-sig/2010-September/005645.html) that *"transactions start implicitly after you connect and after you call `.commit()` or `.rollback()`"*, and the mechanism by which the driver starts the server-side transaction is an implementation detail. Deferring the server-side begin to the first SQL execution (as psycopg2 and other mature DB-API drivers do) is the standard approach. The observable transactional semantics — atomicity between `commit()`/`rollback()` calls — are unchanged. ### Performance impact Before (4 RPCs per read-write transaction): ``` BeginTransaction → ExecuteSql (read) → ExecuteSql (write) → Commit ``` After (3 RPCs per read-write transaction): ``` ExecuteSql (read, with inline begin) → ExecuteSql (write) → Commit ``` Measured ~16ms savings per transaction on the Spanner emulator. ### Context This optimization was identified while profiling SQLAlchemy/DBAPI performance against Spanner. The DBAPI was created ~2 years before inline begin support was added to the Python client library (inline begin landed in [PR googleapis#740](googleapis#740), Dec 2022). The `run_in_transaction` path was updated to use inline begin, but `transaction_checkout` was not. ## Test plan - [x] All 198 DBAPI unit tests pass (`tests/unit/spanner_dbapi/`) - [x] New unit test verifies `begin()` is not called by `transaction_checkout()` - [x] 9 new mockserver tests verify inline begin RPC behavior (`tests/mockserver_tests/test_dbapi_inline_begin.py`) - [x] 8 existing mockserver tests updated for inline begin expectations (`test_tags.py`, `test_dbapi_isolation_level.py`) - [x] All 53 DBAPI system tests pass against Spanner emulator (`tests/system/test_dbapi.py`) - [ ] CI system tests (will run automatically) --------- Co-authored-by: rahul2393 <irahul@google.com> Co-authored-by: Knut Olav Løite <koloite@gmail.com>
Configuration menu - View commit details
-
Copy full SHA for 15eebdf - Browse repository at this point
Copy the full SHA 15eebdfView commit details
Commits on Mar 23, 2026
-
feat: implement native asyncio support via Cross-Sync (googleapis#1509)
****Description: Native Asyncio Support**** This PR introduces comprehensive, native asyncio support to the google-cloud-spanner library. It transitions the library into a "Cross-Sync" architecture, where the asynchronous implementation serves as the source of truth, and the synchronous implementation is automatically kept in parity. ****Key Technical Changes**** Core Library Porting * Asynchronous API: Introduced AsyncClient, AsyncInstance, and AsyncDatabase classes. * Session Management: Completely refactored pool.py to support asyncio. Replaced threading.Lock and queue.Queue with their asyncio counterparts via the CrossSync abstraction. * Transactions & Snapshots: Native async implementation of run_in_transaction, including robust retry logic for Aborted exceptions and proper lock management. ****Verification & Testing**** 9 New System Tests: Created a dedicated async system test suite (tests/system/_async/) covering: Rich data types (Timestamp, JSON, Protobuf, etc.) Transaction retry loops Partitioned DML operations Session pool lifecycle 100% Pass Rate: All new async tests and existing sync tests pass reliably against the Spanner Emulator. Mock Server Updates: Added 40+ mock server tests to verify specific async behaviors like result-set iteration and error handling.
Configuration menu - View commit details
-
Copy full SHA for 0aa1eda - Browse repository at this point
Copy the full SHA 0aa1edaView commit details
Commits on Mar 25, 2026
-
fix(sessions): resolve async deadlock in multiplexed session manager (g…
…oogleapis#1520) This PR resolves a critical deadlock issue when acquiring or maintaining a multiplexed session asynchronously. The bug occurs because DatabaseSessionsManager previously used a synchronous threading.Lock around self._get_multiplexed_session() and _maintain_multiplexed_session(). When a thread attempts to await the multiplexed session creation (return await ...) while holding a synchronous thread lock, the entire asyncio event loop becomes blocked for any other coroutine trying to access the lock.
Configuration menu - View commit details
-
Copy full SHA for c37532c - Browse repository at this point
Copy the full SHA c37532cView commit details -
chore: librarian generate pull request: 20260325T173440Z (googleapis#…
…1529) PR created by the Librarian CLI to generate Cloud Client Libraries code from protos. BEGIN_COMMIT BEGIN_NESTED_COMMIT docs: A comment for field `routing_hint` in messages `.google.spanner.v1.ResultSet` and `.google.spanner.v1.PartialResultSet` are changed PiperOrigin-RevId: 878019893 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@5c9602db](googleapis/googleapis@5c9602db) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: A comment in message `.google.spanner.v1.TransactionOptions.ReadWrite.ReadLockMode` is changed PiperOrigin-RevId: 878019893 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@5c9602db](googleapis/googleapis@5c9602db) END_NESTED_COMMIT BEGIN_NESTED_COMMIT feat: include cache updates and routing hint into BeginTransaction and Commit request/response respectively PiperOrigin-RevId: 878019893 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@5c9602db](googleapis/googleapis@5c9602db) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: A comment for field `params` in message `.google.spanner.v1.PartitionQueryRequest` is changed PiperOrigin-RevId: 865546011 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@8d30990f](googleapis/googleapis@8d30990f) END_NESTED_COMMIT BEGIN_NESTED_COMMIT feat: include cache updates into the ResultSet response PiperOrigin-RevId: 865546011 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@8d30990f](googleapis/googleapis@8d30990f) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: A comment for field `transaction_tag` in message `.google.spanner.v1.RequestOptions` is changed PiperOrigin-RevId: 865546011 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@8d30990f](googleapis/googleapis@8d30990f) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: A comment for field `param_types` in message `.google.spanner.v1.PartitionQueryRequest` is changed PiperOrigin-RevId: 865546011 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@8d30990f](googleapis/googleapis@8d30990f) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: A comment for field `commit_timestamp` in message `.google.spanner.v1.BatchWriteResponse` is changed PiperOrigin-RevId: 865546011 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@8d30990f](googleapis/googleapis@8d30990f) END_NESTED_COMMIT BEGIN_NESTED_COMMIT chore: adjust initial_poll_delay for CreateDatabase and UpdateDatabaseDdl LRO operations PiperOrigin-RevId: 853884590 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@136a2c43](googleapis/googleapis@136a2c43) END_NESTED_COMMIT BEGIN_NESTED_COMMIT feat: add a ClientContext field to Spanner requests PiperOrigin-RevId: 853323071 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@211d22fa](googleapis/googleapis@211d22fa) END_NESTED_COMMIT BEGIN_NESTED_COMMIT feat: add Secure Parameters to the ClientContext PiperOrigin-RevId: 853323071 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@211d22fa](googleapis/googleapis@211d22fa) END_NESTED_COMMIT BEGIN_NESTED_COMMIT feat: Exposing total CPU related fields in AutoscalingConfig PiperOrigin-RevId: 845819318 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@6e20492a](googleapis/googleapis@6e20492a) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: Update high_priority_cpu_utilization_percent in AutoscalingConfig to be Optional and clarify its behavior when not specified PiperOrigin-RevId: 845819318 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@6e20492a](googleapis/googleapis@6e20492a) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: minor update for Spanner Location API PiperOrigin-RevId: 834841888 Library-IDs: google-cloud-spanner Source-link: [googleapis/googleapis@ded7ed1e](googleapis/googleapis@ded7ed1e) END_NESTED_COMMIT END_COMMIT This pull request is generated with proto changes between [googleapis/googleapis@a17b84ad](googleapis/googleapis@a17b84a) (exclusive) and [googleapis/googleapis@5c9602db](googleapis/googleapis@5c9602d) (inclusive). Librarian Version: v1.0.2-0.20260309131826-42ac5c451239 Language Image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209 Fixes googleapis#1527 This PR also moves `ClientContext` under `RequestOptions` as per googleapis#1527 (comment). `ClientContext` was added in googleapis#1499 but the feature has not been released yet so it is safe to move.
Configuration menu - View commit details
-
Copy full SHA for 85443e4 - Browse repository at this point
Copy the full SHA 85443e4View commit details -
tests: use Python 3.10 in
Integration with Regular Sessionspresubm……it (googleapis#1530) In googleapis#1529, Presubmit `Kokoro - Test: Integration with Regular Sessions` failed because Python 3.9 is no longer supported in the Python Kokoro CI. This PR fixes the presubmit by using 3.10 instead.
Configuration menu - View commit details
-
Copy full SHA for 9494019 - Browse repository at this point
Copy the full SHA 9494019View commit details -
chore: librarian update image pull request: 20260325T185910Z (googlea…
…pis#1532) feat: update image to us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:667c9c407ace8caf2deba85187536df8a091e7b05c78eea63eb01255fe17a8f3
Configuration menu - View commit details
-
Copy full SHA for e5638ab - Browse repository at this point
Copy the full SHA e5638abView commit details
Commits on Mar 27, 2026
-
docs(async): mark new AsyncIO APIs as experimental (googleapis#1533)
Add experimental warning comments to all async Spanner Python components to inform users that the API is in the beta phase and subject to change.
Configuration menu - View commit details
-
Copy full SHA for 4065da7 - Browse repository at this point
Copy the full SHA 4065da7View commit details
Commits on Mar 30, 2026
-
fix(spanner): restore implicit database_dialect reload in sync client (…
…googleapis#1537) **Problem** Following the introduction of AsyncIO support via CrossSync, code generation for the synchronous client caused a regression in Database.database_dialect. Previously, if the property was accessed and returned DATABASE_DIALECT_UNSPECIFIED, the client would implicitly make a blocking self.reload() to retrieve the dialect from the server. This was lost during the shift to CrossSync as properties cannot be asynchronous in the _async source of truth. **Solution** Restore the lazy-loading reload behavior via an environment check: __async/database.py_: Added if not CrossSync.is_async: guard directly into the database_dialect getter. This makes it un-reachable code in the async runtime (where blocking properties or un-awaited coroutines are frowned upon). _database.py_: The generator strips the CrossSync context into a standard synchronous self.reload() call, successfully maintaining strict backwards compatibility for all existing synchronous libraries.
Configuration menu - View commit details
-
Copy full SHA for 81f9451 - Browse repository at this point
Copy the full SHA 81f9451View commit details
Commits on Mar 31, 2026
-
build: update README to indicate that source has moved (googleapis#1540)
Towards googleapis/google-cloud-python#10952.
Configuration menu - View commit details
-
Copy full SHA for e0b3f18 - Browse repository at this point
Copy the full SHA e0b3f18View commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff main...main