Official Google Cloud Spanner client library for Go providing comprehensive database operations, transactions, and admin functionality
—
This document covers creating, configuring, and managing Spanner clients including session pool configuration and client options.
Create a client with default configuration:
func NewClient(ctx context.Context, database string, opts ...option.ClientOption) (*Client, error)Parameters:
ctx: Context for authentication and connection setupdatabase: Database name in format projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_IDopts: Optional client options (credentials, endpoints, etc.)Example:
ctx := context.Background()
client, err := spanner.NewClient(ctx, "projects/my-project/instances/my-instance/databases/my-db")
if err != nil {
log.Fatal(err)
}
defer client.Close()Create a client with custom session pool and query options:
func NewClientWithConfig(ctx context.Context, database string, config ClientConfig, opts ...option.ClientOption) (*Client, error)Example:
config := spanner.ClientConfig{
SessionPoolConfig: spanner.SessionPoolConfig{
MinOpened: 50,
MaxOpened: 200,
MaxIdle: 20,
},
QueryOptions: spanner.QueryOptions{
Priority: sppb.RequestOptions_PRIORITY_MEDIUM,
},
}
client, err := spanner.NewClientWithConfig(ctx, database, config)
if err != nil {
log.Fatal(err)
}
defer client.Close()Create a client with multiple endpoint support for failover:
func NewMultiEndpointClient(ctx context.Context, database string, gmeCfg *grpcgcp.GCPMultiEndpointOptions, opts ...option.ClientOption) (*Client, *grpcgcp.GCPMultiEndpoint, error)
func NewMultiEndpointClientWithConfig(ctx context.Context, database string, config ClientConfig, gmeCfg *grpcgcp.GCPMultiEndpointOptions, opts ...option.ClientOption) (*Client, *grpcgcp.GCPMultiEndpoint, error)These functions create a client with GCPMultiEndpoint support for:
type Client struct {
// Has unexported fields
}The Client is safe for concurrent use and provides methods for reading, writing, and managing transactions.
// Data operations
func (c *Client) Single() *ReadOnlyTransaction
func (c *Client) ReadOnlyTransaction() *ReadOnlyTransaction
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)
// Batch operations
func (c *Client) BatchReadOnlyTransaction(ctx context.Context, tb TimestampBound) (*BatchReadOnlyTransaction, error)
func (c *Client) BatchReadOnlyTransactionFromID(tid BatchReadOnlyTransactionID) *BatchReadOnlyTransaction
func (c *Client) BatchWrite(ctx context.Context, mgs []*MutationGroup) *BatchWriteResponseIterator
func (c *Client) BatchWriteWithOptions(ctx context.Context, mgs []*MutationGroup, opts BatchWriteOptions) *BatchWriteResponseIterator
// Write operations
func (c *Client) Apply(ctx context.Context, ms []*Mutation, opts ...ApplyOption) (commitTimestamp time.Time, err error)
func (c *Client) PartitionedUpdate(ctx context.Context, statement Statement) (count int64, err error)
func (c *Client) PartitionedUpdateWithOptions(ctx context.Context, statement Statement, opts QueryOptions) (count int64, err error)
// Management
func (c *Client) Close()
func (c *Client) DatabaseName() string
func (c *Client) ClientID() stringClient lifecycle:
Close() when done to release sessionsdefer client.Close() to ensure cleanupComplete configuration for client behavior:
type ClientConfig struct {
// NumChannels is deprecated - use option.WithGRPCConnectionPool instead
NumChannels int
// Session pool configuration
SessionPoolConfig SessionPoolConfig
// Labels attached to all sessions created by this client
SessionLabels map[string]string
// Default query execution options
QueryOptions QueryOptions
// Default read operation options
ReadOptions ReadOptions
// Default apply operation options
ApplyOptions []ApplyOption
// Default transaction options
TransactionOptions TransactionOptions
// Default batch write options
BatchWriteOptions BatchWriteOptions
// Custom retry settings (overrides defaults)
CallOptions *vkit.CallOptions
// User agent prefix for tracking
UserAgent string
// Database role to assume for all operations
DatabaseRole string
// Disable routing read-write and PDML to leader region
DisableRouteToLeader bool
// Logger for client logging (nil uses standard logger)
Logger *log.Logger
// Compression for gRPC calls ("gzip" or "identity")
Compression string
// Timeout for batch session operations
BatchTimeout time.Duration
// Direct read options for replica/region routing
DirectedReadOptions *sppb.DirectedReadOptions
// OpenTelemetry meter provider
OpenTelemetryMeterProvider metric.MeterProvider
// Enable end-to-end tracing spans at Spanner layer
EnableEndToEndTracing bool
// Disable native metrics emission
DisableNativeMetrics bool
// Internal: experimental host flag
IsExperimentalHost bool
}Configuration for the client's session pool:
type SessionPoolConfig struct {
// Maximum number of opened sessions (blocks when reached)
// Default: NumChannels * 100
MaxOpened uint64
// Minimum number of sessions to maintain
// Default: 100
MinOpened uint64
// Maximum number of idle sessions allowed
// Default: 0
MaxIdle uint64
// MaxBurst is deprecated and no longer used
MaxBurst uint64
// WriteSessions is deprecated and no longer used
WriteSessions float64
// Number of workers for health checking
// Default: 10
HealthCheckWorkers int
// Interval between health checks
// Default: 50 minutes
HealthCheckInterval time.Duration
// Interval for checking multiplexed sessions
// Default: 10 minutes
MultiplexSessionCheckInterval time.Duration
// Track stacktrace of goroutines taking sessions (debugging)
// Default: false
TrackSessionHandles bool
// Configuration for inactive transaction handling
InactiveTransactionRemovalOptions InactiveTransactionRemovalOptions
}Default Session Pool Config:
var DefaultSessionPoolConfig = SessionPoolConfig{
MinOpened: 100,
MaxOpened: numChannels * 100,
MaxBurst: 10,
WriteSessions: 0.2,
HealthCheckWorkers: 10,
HealthCheckInterval: 50 * time.Minute,
InactiveTransactionRemovalOptions: InactiveTransactionRemovalOptions{
ActionOnInactiveTransaction: Warn,
},
}config := spanner.ClientConfig{
SessionPoolConfig: spanner.SessionPoolConfig{
MinOpened: 50, // Keep at least 50 sessions warm
MaxOpened: 400, // Allow up to 400 sessions
MaxIdle: 50, // Expire idle sessions beyond 50
HealthCheckWorkers: 20, // Use 20 workers for health checks
HealthCheckInterval: 30 * time.Minute,
TrackSessionHandles: true, // Enable for debugging leaks
},
}
client, err := spanner.NewClientWithConfig(ctx, database, config)Configuration for handling inactive transactions:
type InactiveTransactionRemovalOptions struct {
// Action to take on inactive transactions
ActionOnInactiveTransaction ActionOnInactiveTransactionKind
// Has unexported fields for thresholds and frequencies
}
type ActionOnInactiveTransactionKind int
const (
NoAction ActionOnInactiveTransactionKind = iota // No action
Warn // Log inactive transactions
Close // Close without logging
WarnAndClose // Log and close
)Default options for SQL queries and DML:
type QueryOptions struct {
// Query execution mode
Mode *sppb.ExecuteSqlRequest_QueryMode
// Query optimizer options
Options *sppb.ExecuteSqlRequest_QueryOptions
// RPC priority for query execution
Priority sppb.RequestOptions_Priority
// Tag for tracking this request
RequestTag string
// Enable Data Boost for partitioned queries
DataBoostEnabled bool
// Direct read options for replica/region targeting
DirectedReadOptions *sppb.DirectedReadOptions
// Exclude partitioned update from change streams
ExcludeTxnFromChangeStreams bool
// Mark statement as last in transaction
LastStatement bool
}Default options for read operations:
type ReadOptions struct {
// Index to use for reading
Index string
// Maximum number of rows to read (0 = no limit)
Limit int
// RPC priority
Priority sppb.RequestOptions_Priority
// Request tag for tracking
RequestTag string
// Enable Data Boost for partitioned reads
DataBoostEnabled bool
// Direct read options for replica/region targeting
DirectedReadOptions *sppb.DirectedReadOptions
// Row ordering control
OrderBy sppb.ReadRequest_OrderBy
// Lock hint for read-write transactions
LockHint sppb.ReadRequest_LockHint
}Options for transaction execution:
type TransactionOptions struct {
// Options for commit operation
CommitOptions CommitOptions
// Tag automatically included with all statements and commit
TransactionTag string
// Priority for the Commit RPC
CommitPriority sppb.RequestOptions_Priority
// Read lock mode for read/write transactions
ReadLockMode sppb.TransactionOptions_ReadWrite_ReadLockMode
// Exclude transaction from change streams
ExcludeTxnFromChangeStreams bool
// Isolation level for read/write transaction
IsolationLevel sppb.TransactionOptions_IsolationLevel
// Controls BeginTransaction RPC behavior
BeginTransactionOption BeginTransactionOption
}
type BeginTransactionOption int
const (
// Use default for transaction type
DefaultBeginTransaction BeginTransactionOption = iota
// Include BeginTransaction with first statement (default for RW)
InlinedBeginTransaction
// Use separate BeginTransaction RPC (default for RO and stmt-based)
ExplicitBeginTransaction
)Options for committing transactions:
type CommitOptions struct {
// Request commit statistics in response
ReturnCommitStats bool
// Maximum delay before commit (for latency optimization)
MaxCommitDelay *time.Duration
}Options for batch write operations:
type BatchWriteOptions struct {
// RPC priority for batch write
Priority sppb.RequestOptions_Priority
// Transaction tag for all transactions in batch
TransactionTag string
// Exclude all transactions from change streams
ExcludeTxnFromChangeStreams bool
}Standard Google API client options can be passed to client constructors:
import "google.golang.org/api/option"
// Custom credentials
client, err := spanner.NewClient(ctx, database,
option.WithCredentialsFile("/path/to/credentials.json"))
// Custom endpoint
client, err := spanner.NewClient(ctx, database,
option.WithEndpoint("custom-endpoint:443"))
// gRPC connection pool
client, err := spanner.NewClient(ctx, database,
option.WithGRPCConnectionPool(8))
// Disable authentication (for emulator)
client, err := spanner.NewClient(ctx, database,
option.WithoutAuthentication())For high-throughput workloads:
config := spanner.ClientConfig{
SessionPoolConfig: spanner.SessionPoolConfig{
MinOpened: 200,
MaxOpened: 1000,
MaxIdle: 100,
HealthCheckWorkers: 50,
},
QueryOptions: spanner.QueryOptions{
Priority: sppb.RequestOptions_PRIORITY_HIGH,
},
Compression: "gzip",
}
client, err := spanner.NewClientWithConfig(ctx, database, config,
option.WithGRPCConnectionPool(16))For read-heavy workloads with stale reads:
config := spanner.ClientConfig{
SessionPoolConfig: spanner.SessionPoolConfig{
MinOpened: 100,
MaxOpened: 500,
MaxIdle: 50,
},
ReadOptions: spanner.ReadOptions{
Priority: sppb.RequestOptions_PRIORITY_MEDIUM,
},
DisableRouteToLeader: true, // Allow reads from replicas
}
client, err := spanner.NewClientWithConfig(ctx, database, config)For debugging session leaks:
config := spanner.ClientConfig{
SessionPoolConfig: spanner.SessionPoolConfig{
MinOpened: 10,
MaxOpened: 50,
TrackSessionHandles: true, // Track goroutines
},
Logger: log.New(os.Stdout, "spanner: ", log.LstdFlags),
}
client, err := spanner.NewClientWithConfig(ctx, database, config)Direct reads to specific regions:
directedReadOptions := &sppb.DirectedReadOptions{
Replicas: &sppb.DirectedReadOptions_IncludeReplicas_{
IncludeReplicas: &sppb.DirectedReadOptions_IncludeReplicas{
ReplicaSelections: []*sppb.DirectedReadOptions_ReplicaSelection{
{
Location: "us-central1",
Type: sppb.DirectedReadOptions_ReplicaSelection_READ_ONLY,
},
},
},
},
}
config := spanner.ClientConfig{
DirectedReadOptions: directedReadOptions,
}
client, err := spanner.NewClientWithConfig(ctx, database, config)Assume a specific database role for all operations:
config := spanner.ClientConfig{
DatabaseRole: "reporting_role",
}
client, err := spanner.NewClientWithConfig(ctx, database, config)
// All operations use reporting_role permissionsfunc EnableOpenTelemetryMetrics()
func IsOpenTelemetryMetricsEnabled() boolEnable before creating clients:
spanner.EnableOpenTelemetryMetrics()
// Configure meter provider
config := spanner.ClientConfig{
OpenTelemetryMeterProvider: myMeterProvider,
EnableEndToEndTracing: true,
}
client, err := spanner.NewClientWithConfig(ctx, database, config)These functions are deprecated - use OpenTelemetry instead:
func EnableStatViews() error
func EnableGfeLatencyView() error
func EnableGfeHeaderMissingCountView() error
func EnableGfeLatencyAndHeaderMissingCountViews() error
func DisableGfeLatencyAndHeaderMissingCountViews()defer client.Close() to avoid session leaksvar client *spanner.Client
func init() {
ctx := context.Background()
var err error
client, err = spanner.NewClient(ctx, database)
if err != nil {
log.Fatal(err)
}
}
func main() {
defer client.Close()
// Use client throughout application
}ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
client, err := spanner.NewClient(ctx, database)
if err != nil {
log.Fatal(err)
}
defer client.Close()import "os"
os.Setenv("SPANNER_EMULATOR_HOST", "localhost:9010")
client, err := spanner.NewClient(ctx, "projects/test/instances/test/databases/test")
// Client automatically connects to emulatorInstall with Tessl CLI
npx tessl i tessl/golang-cloud-google-com--go--spanner