Skip to content

Conversation

@danielnorberg
Copy link
Contributor

Implements the TransactionManager API as described in issue #375. This allows manual control over transaction execution while maintaining session reuse across retries, which helps retain lock priority and improves commit success rates for ABORTED transactions.

Key changes:

  • Add TransactionManager struct that holds a session across multiple transaction attempts
  • Add Client::transaction_manager() method to create a TransactionManager
  • Add Debug implementation for BeginError to support unwrap() in user code
  • Add comprehensive integration tests for TransactionManager functionality

The API enables users to handle transaction retry logic manually while benefiting from session reuse:

let mut tm = client.transaction_manager().await?;
let retry = &mut TransactionRetry::new();
loop {
    let tx = tm.begin_read_write_transaction().await?;
    let result = run_in_transaction(tx).await;
    match tx.end(result, None).await {
        Ok((commit_result, success)) => return Ok(success),
        Err(err) => retry.next(err).await?
    }
}

Fixes #375

🤖 Generated with Claude Code

danielnorberg and others added 5 commits November 20, 2025 22:00
…ith session reuse

Implements the TransactionManager API as described in issue yoshidan#375. This allows manual control over transaction execution while maintaining session reuse across retries, which helps retain lock priority and improves commit success rates for ABORTED transactions.

Key changes:
- Add TransactionManager struct that holds a session across multiple transaction attempts
- Add Client::transaction_manager() method to create a TransactionManager
- Add Debug implementation for BeginError to support unwrap() in user code
- Add comprehensive integration tests for TransactionManager functionality

The API enables users to handle transaction retry logic manually while benefiting from session reuse:

```rust
let mut tm = client.transaction_manager().await?;
let retry = &mut TransactionRetry::new();
loop {
    let tx = tm.begin_read_write_transaction().await?;
    let result = run_in_transaction(tx).await;
    match tx.end(result, None).await {
        Ok((commit_result, success)) => return Ok(success),
        Err(err) => retry.next(err).await?
    }
}
```

Fixes yoshidan#375

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…fy Drop handling

Improvements:
- Use client::Error instead of BeginError for better API consistency with Client::begin_read_write_transaction()
- Handle session preservation internally when begin fails, storing it back for retry
- Remove redundant Drop implementation - ManagedSession::drop() already handles session pool return
- Remove Debug implementation from BeginError as it's no longer needed

Benefits:
- Consistent error types across the API
- Simpler, more idiomatic code
- Better encapsulation of session management
- Automatic cleanup through Rust's Drop chain

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…g common logic

Refactoring:
- Extract common transaction begin logic into private begin_internal() helper method
- Simplify public begin_read_write_transaction() methods to thin delegates
- Consolidates 31 lines of duplicate code (90% identical) into single implementation

Benefits:
- Single source of truth for transaction begin logic
- Bug fixes and changes only needed in one place
- 11 lines removed (11% reduction in file size)
- Improved maintainability with no behavior changes

Before: Two 23-line methods with duplicate logic
After: Two 2-line delegates + one 23-line helper

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…method

Refactoring:
- Remove begin_internal() private helper method
- Change begin_read_write_transaction() to directly delegate to begin_read_write_transaction_with_options()
- Move implementation logic into begin_read_write_transaction_with_options()

Benefits:
- Simpler, more idiomatic Rust pattern (convenience method → full method)
- Removes unnecessary indirection through private helper
- 8 lines removed (8% reduction)
- Clearer code flow with direct delegation

This follows the common Rust pattern where simple methods call more complex ones with default parameters, similar to Vec::new() → Vec::with_capacity(0).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Features:
- Add transaction() method that returns Option<&mut ReadWriteTransaction>
- Returns None if no transaction is active, Some if active
- Enables checking transaction state and accessing existing transaction
- Includes comprehensive test coverage

Use cases:
- Check if a transaction exists without starting one
- Access existing transaction for additional operations
- Conditional logic based on transaction state

Test added:
- test_transaction_accessor() validates behavior before/after begin
- Tests functional access through the accessor
- Verifies data written through accessor is committed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@danielnorberg danielnorberg force-pushed the dano/transaction-manager branch from 537b5b6 to b23f1f4 Compare November 20, 2025 21:01
Add disable_route_to_leader field to TransactionManager and pass it through
to ReadWriteTransaction::begin. This ensures that TransactionManager respects
the client's disable_route_to_leader setting when creating transactions.

Changes:
- Add disable_route_to_leader field to TransactionManager struct
- Update TransactionManager::new to accept disable_route_to_leader parameter
- Pass disable_route_to_leader to ReadWriteTransaction::begin in begin_read_write_transaction_with_options
- Update Client::transaction_manager to pass disable_route_to_leader from client config

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spanner: allow reusing sessions with begin_read_write_transaction

1 participant