Skip to content

Apply TRANSACTION_TIMEOUT to the logical transaction across replay #482

Description

@apstndb

Current scope

Implement a logical read/write transaction deadline distinct from STATEMENT_TIMEOUT and user-idle timeout (#357). The old SET LOCAL prerequisite #400 is complete. The existing owner/attempt separation from SAVEPOINT must retain one budget across replacement.

Contract to implement

  • TRANSACTION_TIMEOUT accepts a duration or NULL; zero/NULL means no additional transaction deadline. Snapshot the configured duration for a logical transaction.

  • Define the clock explicitly: start at its first actual database operation, not a client-only BEGIN/SHOW, while preserving the originally selected duration. Do not reset it on a later statement, ABORTED retry or ROLLBACK TO.

  • A pending SET LOCAL may select the duration before first database use; reject attempts to extend/change the budget after activation. Session SET after BEGIN applies to a later logical owner, not the current one.

  • Every SQL/Batch DML/commit/replay RPC receives the minimum of caller, statement and remaining transaction deadline. A client-only SHOW must not activate or renew it.

  • On expiry cancel/retire the matching logical owner, stop its heartbeat and restore SET LOCAL exactly once. No stale timer may cancel a replacement owner.

Design clarification (2026-09-14)

  • Verify against the current commit and pinned SDK, not a stale coordinator checkout. At the audited baseline, mycli uses Go Spanner v1.95.0 ReadWriteStmtBasedTransaction. Its default constructor normally sends an explicit BeginTransaction RPC; generic pending BEGIN is client-only. The statement-based wrapper overrides the embedded callback-based transaction's begin decision. Start the budget before the actual transaction RPC, including constructor BeginTransaction when applicable. Merely taking a multiplexed session or buffering DML without a transaction RPC does not start it. A buffered-DML route that activates the owner first can nevertheless already have issued BeginTransaction.
  • A deadline must cancel an in-flight RPC without first waiting for TransactionManager.mu: some existing RPC paths hold that mutex. Retire only the matching logical owner under the mutex after cancellation. A captured owner pointer and unchanged total budget must survive physical attempt replacement.
  • Timer goroutines must not mutate the live registry through Registry.Set. Detach the owner's LOCAL undo for restoration at a serialized session/CLI safe point. Drain expired-owner undo before a later statement reads execution defaults or creates a new owner, and on Close; the ordinary end-of-statement defer also handles an in-flight operation returning after cancellation. Document this safe-point restoration boundary. A naked inExec boolean, even atomic, is not an atomic check-and-restore protocol.
  • Test cancellation of an RPC while the transaction mutex is held, idle expiry followed immediately by BEGIN for a new owner, explicit-constructor failure, delayed old-owner callbacks, and first activation through automatic DML. Prove errors/ownership, not merely elapsed time. Preserve the existing failed-construction and SAVEPOINT recovery invariants.

Acceptance

References: Go transaction timeout, Java property, mycli session_transaction_context.go and transaction_manager.go.

Verification and delivery

Plan refreshed on 2026-09-14 against a8fa2418b65af9b889cf92e02190366b3e6e02ea. This body supersedes obsolete implementation suggestions in earlier comments; history is retained. Tracked with #47.

For each implementation PR, capture focused test output to a file and check the exit code, then run make check before pushing; run make check-race for transaction concurrency/lifecycle changes. Keep the coverage gate at 80%. Update user docs and the compatibility matrix with the resulting behavior. No database/sql migration or unrelated dependency upgrade.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requeststatement-compatibilityClient-side statement and system variable compatibility with java-spanner/go-sql-spanner

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions