Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

DBAPI: transaction_checkout() uses explicit BeginTransaction RPC instead of inline begin #15871

Copy link
Copy link

Description

@waiho-gumloop
Issue body actions

Environment details

  • OS: macOS (also reproducible on Linux)
  • Python version: 3.13.7
  • google-cloud-spanner version: 3.63.0
  • sqlalchemy-spanner version: 1.17.2

Problem

Connection.transaction_checkout() calls Transaction.begin() explicitly before the first query, which sends a standalone BeginTransaction gRPC RPC. This adds an unnecessary round-trip to every read-write transaction initiated through the DBAPI/SQLAlchemy path.

The Transaction class already supports inline begin — piggybacking BeginTransaction onto the first ExecuteSql request via TransactionSelector(begin=...). This is what Session.run_in_transaction() uses (it creates a Transaction without calling begin()). But the DBAPI's transaction_checkout() bypasses this by eagerly calling begin(), which sets _transaction_id before the first query and prevents the inline begin path in _make_txn_selector().

Current behavior (4 RPCs)

BeginTransaction → ExecuteSql (read) → ExecuteSql (write) → Commit

Expected behavior (3 RPCs)

ExecuteSql (read, with inline begin) → ExecuteSql (write) → Commit

Performance impact

Measured ~16ms overhead per transaction on the Spanner emulator. This affects every transaction_scope write operation when using the DBAPI or SQLAlchemy.

Root cause

Connection.transaction_checkout() calls self._transaction.begin() on L413. This was likely written before inline begin support was added to the Python client library. Inline begin landed in PR googleapis/python-spanner#740 (Dec 2022), but transaction_checkout was not updated to take advantage of it.

The fix is to remove the self._transaction.begin() call, letting execute_sql() / execute_update() / batch_update() use their existing inline begin logic via _make_txn_selector().

This is also fully conformant with PEP 249 (DB-API 2.0), which does not define a begin() method — transactions are implicit, and the mechanism by which the driver starts the server-side transaction is an implementation detail.

Proposed fix

Draft PR: googleapis/python-spanner#1502

Reactions are currently unavailable

Metadata

Metadata

Assignees

Labels

api: spannerIssues related to the Spanner API.Issues related to the Spanner API.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.