feat(dbapi): use inline begin to eliminate BeginTransaction RPC by waiho-gumloop · Pull Request #1502 · googleapis/python-spanner
bot
added
size: s
labels
Feb 26, 2026…ests Add test_dbapi_inline_begin.py with 7 mockserver tests that verify: - Read-write DBAPI transactions send no BeginTransactionRequest - First ExecuteSqlRequest uses TransactionSelector(begin=...) - Read + write + commit request sequence is correct - DML-only transactions use inline begin - Read-only transactions still use explicit BeginTransaction - Transaction retry after abort works with inline begin Update existing mockserver tests that expected BeginTransactionRequest for read-write DBAPI transactions: - test_tags.py: Remove BeginTransactionRequest from expected sequences for all read-write tag tests, adjust tag index offsets - test_dbapi_isolation_level.py: Verify isolation level on the inline begin field of ExecuteSqlRequest instead of BeginTransactionRequest Made-with: Cursor
- Consolidate 3 redundant single-read tests into one comprehensive test that verifies: no BeginTransactionRequest, inline begin on first ExecuteSqlRequest, correct request sequence, and correct query results - Rename test_second_statement_uses_transaction_id to test_read_then_write_full_lifecycle with additional assertions: CommitRequest.transaction_id matches the transaction ID from inline begin - Strengthen test_rollback to verify RollbackRequest is sent with a non-empty transaction_id (was only checking no BeginTransactionRequest) - Add CommitRequest assertions to abort retry test: both the aborted and successful commits carry valid transaction IDs - Assert cursor.fetchall() return values in read tests to verify inline begin doesn't corrupt result set metadata - Add RollbackRequest import Made-with: Cursor
olavloite
added
the
do not merge
label
Mar 13, 2026Add retries if the first statement in a read/write transaction fails, as the statement then does not return a transaction ID. In order to ensure that we get a transaction ID, we first execute an explicit BeginTransaction RPC and then retry the original statement. We return the response of the retry to the application, regardless whether the retry fails or succeeds. The reason that we do a retry with a BeginTransaction AND include the first statement, is to guarantee transaction consistency. If we were to leave the first statement out of the transaction, then it will not be guaranteed that the error condition that cause the failure in the first place is actually still true when the transaction commits. This would break the transaction guarantees. Example (pseudo-code): ```sql -- The following statement fails with ALREADY_EXISTS insert into some_table (id, value) values (1, 'One'); -- Execute an explicit BeginTransaction RPC. begin; -- Retry the initial statement. This ensures that -- whatever the response is, this response will be -- valid for the entire transaction. insert into some_table (id, value) values (1, 'One'); -- This is guaranteed to return a row. select * from some_table where id=1; -- ... execute the rest of the transaction ... commit; ``` If we had not included the initial insert statement in the retried transaction, then there is no guarantee that the select statement would actually return any rows, as other transactions could in theory have deleted it in the meantime.
olavloite
removed
the
do not merge
label
Mar 13, 2026This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters