tessl install tessl/golang-cloud-google-com--go--spanner@1.87.2Official Google Cloud Spanner client library for Go providing comprehensive database operations, transactions, and admin functionality
Comprehensive guide to Spanner transactions including read-only, read-write, batch, and statement-based transactions.
Spanner supports multiple transaction types:
Client.Single())For one-shot operations:
func (c *Client) Single() *ReadOnlyTransactionExample:
row, err := client.Single().ReadRow(ctx, "Users", key, columns)For multiple consistent reads:
func (c *Client) ReadOnlyTransaction() *ReadOnlyTransactionReadOnlyTransaction Methods:
func (t *ReadOnlyTransaction) Read(ctx context.Context, table string, keys KeySet, columns []string) *RowIterator
func (t *ReadOnlyTransaction) ReadRow(ctx context.Context, table string, key Key, columns []string) (*Row, error)
func (t *ReadOnlyTransaction) ReadUsingIndex(ctx context.Context, table, index string, keys KeySet, columns []string) *RowIterator
func (t *ReadOnlyTransaction) ReadRowUsingIndex(ctx context.Context, table, index string, key Key, columns []string) (*Row, error)
func (t *ReadOnlyTransaction) ReadWithOptions(ctx context.Context, table string, keys KeySet, columns []string, opts *ReadOptions) *RowIterator
func (t *ReadOnlyTransaction) ReadRowWithOptions(ctx context.Context, table string, key Key, columns []string, opts *ReadOptions) (*Row, error)
func (t *ReadOnlyTransaction) Query(ctx context.Context, statement Statement) *RowIterator
func (t *ReadOnlyTransaction) QueryWithOptions(ctx context.Context, statement Statement, opts QueryOptions) *RowIterator
func (t *ReadOnlyTransaction) QueryWithStats(ctx context.Context, statement Statement) *RowIterator
func (t *ReadOnlyTransaction) AnalyzeQuery(ctx context.Context, statement Statement) (*sppb.QueryPlan, error)
func (t *ReadOnlyTransaction) Close()
func (t *ReadOnlyTransaction) Timestamp() (time.Time, error)
func (t *ReadOnlyTransaction) WithTimestampBound(tb TimestampBound) *ReadOnlyTransaction
func (t *ReadOnlyTransaction) WithBeginTransactionOption(option BeginTransactionOption) *ReadOnlyTransactionExample:
txn := client.ReadOnlyTransaction()
defer txn.Close()
// All reads at same timestamp
row1, err := txn.ReadRow(ctx, "Users", key1, cols)
row2, err := txn.ReadRow(ctx, "Accounts", key2, cols)
ts, _ := txn.Timestamp()
fmt.Printf("Read at: %v\n", ts)Automatic retry for read-modify-write operations:
func (c *Client) ReadWriteTransaction(ctx context.Context, f func(context.Context, *ReadWriteTransaction) error) (commitTimestamp time.Time, err error)
func (c *Client) ReadWriteTransactionWithOptions(ctx context.Context, f func(context.Context, *ReadWriteTransaction) error, options TransactionOptions) (CommitResponse, error)ReadWriteTransaction Methods:
// Read operations
func (t *ReadWriteTransaction) Read(ctx context.Context, table string, keys KeySet, columns []string) *RowIterator
func (t *ReadWriteTransaction) ReadRow(ctx context.Context, table string, key Key, columns []string) (*Row, error)
func (t *ReadWriteTransaction) ReadUsingIndex(ctx context.Context, table, index string, keys KeySet, columns []string) *RowIterator
func (t *ReadWriteTransaction) ReadRowUsingIndex(ctx context.Context, table, index string, key Key, columns []string) (*Row, error)
func (t *ReadWriteTransaction) ReadWithOptions(ctx context.Context, table string, keys KeySet, columns []string, opts *ReadOptions) *RowIterator
func (t *ReadWriteTransaction) ReadRowWithOptions(ctx context.Context, table string, key Key, columns []string, opts *ReadOptions) (*Row, error)
func (t *ReadWriteTransaction) Query(ctx context.Context, statement Statement) *RowIterator
func (t *ReadWriteTransaction) QueryWithOptions(ctx context.Context, statement Statement, opts QueryOptions) *RowIterator
func (t *ReadWriteTransaction) QueryWithStats(ctx context.Context, statement Statement) *RowIterator
func (t *ReadWriteTransaction) AnalyzeQuery(ctx context.Context, statement Statement) (*sppb.QueryPlan, error)
// Write operations
func (t *ReadWriteTransaction) BufferWrite(ms []*Mutation) error
func (t *ReadWriteTransaction) Update(ctx context.Context, stmt Statement) (rowCount int64, err error)
func (t *ReadWriteTransaction) UpdateWithOptions(ctx context.Context, stmt Statement, opts QueryOptions) (rowCount int64, err error)
func (t *ReadWriteTransaction) BatchUpdate(ctx context.Context, stmts []Statement) ([]int64, error)
func (t *ReadWriteTransaction) BatchUpdateWithOptions(ctx context.Context, stmts []Statement, opts QueryOptions) ([]int64, error)Example:
_, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
// Read current balance
row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
if err != nil {
return err
}
var balance int64
if err := row.Column(0, &balance); err != nil {
return err
}
// Check funds
if balance < 100 {
return fmt.Errorf("insufficient funds")
}
// Deduct amount
balance -= 100
m := spanner.Update("Accounts",
[]string{"user", "balance"},
[]interface{}{"alice", balance})
return txn.BufferWrite([]*spanner.Mutation{m})
})type TransactionOptions struct {
CommitOptions CommitOptions
TransactionTag string
CommitPriority sppb.RequestOptions_Priority
ReadLockMode sppb.TransactionOptions_ReadWrite_ReadLockMode
ExcludeTxnFromChangeStreams bool
IsolationLevel sppb.TransactionOptions_IsolationLevel
BeginTransactionOption BeginTransactionOption
}
type CommitOptions struct {
ReturnCommitStats bool
MaxCommitDelay *time.Duration
}
type CommitResponse struct {
CommitTs time.Time
CommitStats *sppb.CommitResponse_CommitStats
}Example:
opts := spanner.TransactionOptions{
TransactionTag: "transfer-funds",
CommitOptions: spanner.CommitOptions{
ReturnCommitStats: true,
},
CommitPriority: sppb.RequestOptions_PRIORITY_HIGH,
}
resp, err := client.ReadWriteTransactionWithOptions(ctx,
func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
// Transaction logic
return txn.BufferWrite(mutations)
}, opts)
if err == nil {
fmt.Printf("Commit stats: %v\n", resp.CommitStats)
}For manual control (advanced users):
func NewReadWriteStmtBasedTransaction(ctx context.Context, c *Client) (*ReadWriteStmtBasedTransaction, error)
func NewReadWriteStmtBasedTransactionWithOptions(ctx context.Context, c *Client, options TransactionOptions) (*ReadWriteStmtBasedTransaction, error)
func NewReadWriteStmtBasedTransactionWithCallbackForOptions(ctx context.Context, c *Client, opts TransactionOptions, callback func() TransactionOptions) (*ReadWriteStmtBasedTransaction, error)ReadWriteStmtBasedTransaction Methods:
// All ReadWriteTransaction methods plus:
func (t *ReadWriteStmtBasedTransaction) Commit(ctx context.Context) (time.Time, error)
func (t *ReadWriteStmtBasedTransaction) CommitWithReturnResp(ctx context.Context) (CommitResponse, error)
func (t *ReadWriteStmtBasedTransaction) Rollback(ctx context.Context)
func (t *ReadWriteStmtBasedTransaction) ResetForRetry(ctx context.Context) (*ReadWriteStmtBasedTransaction, error)Example:
txn, err := spanner.NewReadWriteStmtBasedTransaction(ctx, client)
if err != nil {
return err
}
// Manually manage transaction
row, err := txn.ReadRow(ctx, "Users", key, cols)
if err != nil {
txn.Rollback(ctx)
return err
}
// Perform updates
rowCount, err := txn.Update(ctx, stmt)
if err != nil {
// Check if aborted
if spanner.ErrCode(err) == codes.Aborted {
// Manually retry
newTxn, err := txn.ResetForRetry(ctx)
if err != nil {
return err
}
txn = newTxn
// Retry logic...
}
txn.Rollback(ctx)
return err
}
// Commit
commitTS, err := txn.Commit(ctx)
if err != nil {
return err
}For partitioned reads across machines:
func (c *Client) BatchReadOnlyTransaction(ctx context.Context, tb TimestampBound) (*BatchReadOnlyTransaction, error)
func (c *Client) BatchReadOnlyTransactionFromID(tid BatchReadOnlyTransactionID) *BatchReadOnlyTransaction
type BatchReadOnlyTransaction struct {
ReadOnlyTransaction
ID BatchReadOnlyTransactionID
}Partition Methods:
func (t *BatchReadOnlyTransaction) PartitionRead(ctx context.Context, table string, keys KeySet, columns []string, opt PartitionOptions) ([]*Partition, error)
func (t *BatchReadOnlyTransaction) PartitionReadUsingIndex(ctx context.Context, table, index string, keys KeySet, columns []string, opt PartitionOptions) ([]*Partition, error)
func (t *BatchReadOnlyTransaction) PartitionReadWithOptions(ctx context.Context, table string, keys KeySet, columns []string, opt PartitionOptions, readOptions ReadOptions) ([]*Partition, error)
func (t *BatchReadOnlyTransaction) PartitionQuery(ctx context.Context, statement Statement, opt PartitionOptions) ([]*Partition, error)
func (t *BatchReadOnlyTransaction) PartitionQueryWithOptions(ctx context.Context, statement Statement, opt PartitionOptions, qOpts QueryOptions) ([]*Partition, error)
func (t *BatchReadOnlyTransaction) Execute(ctx context.Context, p *Partition) *RowIterator
func (t *BatchReadOnlyTransaction) Cleanup(ctx context.Context)
type PartitionOptions struct {
PartitionBytes int64
MaxPartitions int64
}
type Partition struct {
// Has unexported fields
}
func (p *Partition) MarshalBinary() (data []byte, err error)
func (p *Partition) UnmarshalBinary(data []byte) error
func (p *Partition) GetPartitionToken() []byteExample:
txn, err := client.BatchReadOnlyTransaction(ctx, spanner.StrongRead())
if err != nil {
return err
}
defer txn.Cleanup(ctx)
stmt := spanner.NewStatement("SELECT * FROM Users")
partitions, err := txn.PartitionQuery(ctx, stmt, spanner.PartitionOptions{
PartitionBytes: 100000000,
})
if err != nil {
return err
}
// Process partitions in parallel
for _, p := range partitions {
go func(partition *spanner.Partition) {
iter := txn.Execute(ctx, partition)
defer iter.Stop()
for {
row, err := iter.Next()
if err == iterator.Done {
break
}
// Process row
}
}(p)
}Control read timestamps:
func StrongRead() TimestampBound
func ReadTimestamp(t time.Time) TimestampBound
func ExactStaleness(d time.Duration) TimestampBound
func MinReadTimestamp(t time.Time) TimestampBound
func MaxStaleness(d time.Duration) TimestampBoundExamples:
// Strong read (default)
txn := client.Single().WithTimestampBound(spanner.StrongRead())
// Exact staleness
txn := client.Single().WithTimestampBound(spanner.ExactStaleness(15 * time.Second))
// Max staleness
txn := client.Single().WithTimestampBound(spanner.MaxStaleness(10 * time.Second))
// Specific timestamp
ts := time.Now().Add(-1 * time.Hour)
txn := client.Single().WithTimestampBound(spanner.ReadTimestamp(ts))
// Min read timestamp
minTs := time.Now().Add(-5 * time.Minute)
txn := client.Single().WithTimestampBound(spanner.MinReadTimestamp(minTs))import "google.golang.org/grpc/codes"
_, err := client.ReadWriteTransaction(ctx, f)
if err != nil {
switch spanner.ErrCode(err) {
case codes.Aborted:
// Automatically retried by ReadWriteTransaction
case codes.DeadlineExceeded:
// Transaction timeout
case codes.FailedPrecondition:
// Invalid state or constraint
case codes.InvalidArgument:
// Bad input
default:
// Other error
}
}type TransactionOutcomeUnknownError struct {
// Has unexported fields
}
func (e *TransactionOutcomeUnknownError) Error() string
func (e *TransactionOutcomeUnknownError) Unwrap() errorIndicates transaction outcome is unknown (timeout/cancel after commit sent).