Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: waiho-gumloop/python-spanner
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: googleapis/python-spanner
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 15 commits
  • 250 files changed
  • 11 contributors

Commits on Feb 11, 2026

  1. 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.
    skuruppu authored Feb 11, 2026
    Configuration menu
    Copy the full SHA
    7e79920 View commit details
    Browse the repository at this point in the history
  2. 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.
    sinhasubham authored Feb 11, 2026
    Configuration menu
    Copy the full SHA
    e792136 View commit details
    Browse the repository at this point in the history

Commits on Feb 13, 2026

  1. 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>
    rahul2393 authored Feb 13, 2026
    Configuration menu
    Copy the full SHA
    64f1dbf View commit details
    Browse the repository at this point in the history

Commits on Feb 19, 2026

  1. Configuration menu
    Copy the full SHA
    600c136 View commit details
    Browse the repository at this point in the history

Commits on Feb 26, 2026

  1. 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>
    3 people authored Feb 26, 2026
    Configuration menu
    Copy the full SHA
    12773d7 View commit details
    Browse the repository at this point in the history

Commits on Mar 3, 2026

  1. 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>
    aseering and olavloite authored Mar 3, 2026
    Configuration menu
    Copy the full SHA
    9fcb647 View commit details
    Browse the repository at this point in the history

Commits on Mar 13, 2026

  1. 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>
    3 people authored Mar 13, 2026
    Configuration menu
    Copy the full SHA
    15eebdf View commit details
    Browse the repository at this point in the history

Commits on Mar 23, 2026

  1. 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.
    sinhasubham authored Mar 23, 2026
    Configuration menu
    Copy the full SHA
    0aa1eda View commit details
    Browse the repository at this point in the history

Commits on Mar 25, 2026

  1. 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.
    sinhasubham authored Mar 25, 2026
    Configuration menu
    Copy the full SHA
    c37532c View commit details
    Browse the repository at this point in the history
  2. 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.
    parthea authored Mar 25, 2026
    Configuration menu
    Copy the full SHA
    85443e4 View commit details
    Browse the repository at this point in the history
  3. tests: use Python 3.10 in Integration with Regular Sessions presubm…

    …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.
    parthea authored Mar 25, 2026
    Configuration menu
    Copy the full SHA
    9494019 View commit details
    Browse the repository at this point in the history
  4. 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
    parthea authored Mar 25, 2026
    Configuration menu
    Copy the full SHA
    e5638ab View commit details
    Browse the repository at this point in the history

Commits on Mar 27, 2026

  1. 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.
    sinhasubham authored Mar 27, 2026
    Configuration menu
    Copy the full SHA
    4065da7 View commit details
    Browse the repository at this point in the history

Commits on Mar 30, 2026

  1. 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.
    sinhasubham authored Mar 30, 2026
    Configuration menu
    Copy the full SHA
    81f9451 View commit details
    Browse the repository at this point in the history

Commits on Mar 31, 2026

  1. Configuration menu
    Copy the full SHA
    e0b3f18 View commit details
    Browse the repository at this point in the history
Loading