Class: Google::Cloud::Spanner::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/google/cloud/spanner/client.rb

Overview

Client

A client is used to read and/or modify data in a Cloud Spanner database.

See Project#client.

Examples:

require "google/cloud"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

results = db.execute_query "SELECT * FROM users"

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Instance Method Summary collapse

Instance Method Details

#closeObject

Closes the client connection and releases resources.



2105
2106
2107
# File 'lib/google/cloud/spanner/client.rb', line 2105

def close
  @pool.close
end

#commit(commit_options: nil, request_options: nil, call_options: nil) {|commit| ... } ⇒ Time, CommitResponse

Creates and commits a transaction for writes that execute atomically at a single logical point in time across columns, rows, and tables in a database.

All changes are accumulated in memory until the block completes. Unlike #transaction, which can also perform reads, this operation accepts only mutations and makes a single API request.

Note: This method does not feature replay protection present in #transaction. This method makes a single RPC, whereas #transaction requires two RPCs (one of which may be performed in advance), and so this method may be appropriate for latency sensitive and/or high throughput blind changes.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

db.commit do |c|
  c.update "users", [{ id: 1, name: "Charlie", active: false }]
  c.insert "users", [{ id: 2, name: "Harvey",  active: true }]
end

Get commit stats

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

commit_options = { return_commit_stats: true }
commit_resp = db.commit commit_options: commit_options do |c|
  c.update "users", [{ id: 1, name: "Charlie", active: false }]
  c.insert "users", [{ id: 2, name: "Harvey",  active: true }]
end

puts commit_resp.timestamp
puts commit_resp.stats.mutation_count

With request options

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

db.commit request_options: { priority: :PRIORITY_MEDIUM } do |c|
  c.update "users", [{ id: 1, name: "Charlie", active: false }]
  c.insert "users", [{ id: 2, name: "Harvey",  active: true }]
end

Commit using tag for transaction statistics collection.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { tag: "BulkManipulate-Users" }
db.commit request_options: request_options do |c|
  c.update "users", [{ id: 1, name: "Charlie", active: false }]
  c.insert "users", [{ id: 2, name: "Harvey",  active: true }]
end

Yields:

  • (commit)

    The block for mutating the data.

Yield Parameters:

Raises:

  • (ArgumentError)


1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
# File 'lib/google/cloud/spanner/client.rb', line 1618

def commit commit_options: nil, request_options: nil,
           call_options: nil, &block
  raise ArgumentError, "Must provide a block" unless block_given?

  request_options = Convert.to_request_options \
    request_options, tag_type: :transaction_tag

  @pool.with_session do |session|
    session.commit(
      commit_options: commit_options, request_options: request_options,
      call_options: call_options, &block
    )
  end
end

#commit_timestampColumnValue

Creates a column value object representing setting a field's value to the timestamp of the commit. (See Google::Cloud::Spanner::ColumnValue.commit_timestamp)

This placeholder value can only be used for timestamp columns that have set the option "(allow_commit_timestamp=true)" in the schema.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

# create column value object
commit_timestamp = db.commit_timestamp

db.commit do |c|
  c.insert "users", [
    { id: 5, name: "Murphy", updated_at: commit_timestamp }
  ]
end


2098
2099
2100
# File 'lib/google/cloud/spanner/client.rb', line 2098

def commit_timestamp
  ColumnValue.commit_timestamp
end

#databaseDatabase

The Spanner database connected to.



97
98
99
# File 'lib/google/cloud/spanner/client.rb', line 97

def database
  @project.database instance_id, database_id
end

#database_idString

The unique identifier for the database.



79
80
81
# File 'lib/google/cloud/spanner/client.rb', line 79

def database_id
  @database_id
end

#database_roleString

The Spanner session creator role.



103
104
105
# File 'lib/google/cloud/spanner/client.rb', line 103

def database_role
  @database_role
end

#delete(table, keys = [], commit_options: nil, request_options: nil, call_options: nil) ⇒ Time, CommitResponse

Deletes rows from a table. Succeeds whether or not the specified rows were present.

Changes are made immediately upon calling this method using a single-use transaction. To make multiple changes in the same single-use transaction use #commit. To make changes in a transaction that supports reads and automatic retry protection use #transaction.

Note: This method does not feature replay protection present in Transaction#delete (See #transaction). This method makes a single RPC, whereas Transaction#delete requires two RPCs (one of which may be performed in advance), and so this method may be appropriate for latency sensitive and/or high throughput blind deletions.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

db.delete "users", [1, 2, 3]

Get commit stats

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

commit_options = { return_commit_stats: true }
commit_resp = db.delete "users", [1, 2, 3], commit_options: commit_options

puts commit_resp.timestamp
puts commit_resp.stats.mutation_count

With request optinos

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { priority: :PRIORITY_MEDIUM }
db.delete "users", [1, 2, 3], request_options: request_options

Delete using tag for transaction statistics collection.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { tag: "BulkDelete-Users" }
db.delete "users", [1, 2, 3], request_options: request_options


1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
# File 'lib/google/cloud/spanner/client.rb', line 1500

def delete table, keys = [], commit_options: nil, request_options: nil,
           call_options: nil
  request_options = Convert.to_request_options \
    request_options, tag_type: :transaction_tag

  @pool.with_session do |session|
    session.delete table, keys, commit_options: commit_options,
                   request_options: request_options,
                   call_options: call_options
  end
end

#execute_partition_update(sql, params: nil, types: nil, query_options: nil, request_options: nil, call_options: nil) ⇒ Integer Also known as: execute_pdml

Executes a Partitioned DML SQL statement.

Partitioned DML is an alternate implementation with looser semantics to enable large-scale changes without running into transaction size limits or (accidentally) locking the entire table in one large transaction. At a high level, it partitions the keyspace and executes the statement on each partition in separate internal transactions.

Partitioned DML does not guarantee database-wide atomicity of the statement - it guarantees row-based atomicity, which includes updates to any indices. Additionally, it does not guarantee that it will execute exactly one time against each row - it guarantees "at least once" semantics.

Where DML statements must be executed using Transaction (see Transaction#execute_update), Partitioned DML statements are executed outside of a read/write transaction.

Not all DML statements can be executed in the Partitioned DML mode and the backend will return an error for the statements which are not supported.

DML statements must be fully-partitionable. Specifically, the statement must be expressible as the union of many statements which each access only a single row of the table. InvalidArgumentError is raised if the statement does not qualify.

The method will block until the update is complete. Running a DML statement with this method does not offer exactly once semantics, and therefore the DML statement should be idempotent. The DML statement must be fully-partitionable. Specifically, the statement must be expressible as the union of many statements which each access only a single row of the table. This is a Partitioned DML transaction in which a single Partitioned DML statement is executed. Partitioned DML partitions the and runs the DML statement over each partition in parallel using separate, internal transactions that commit independently. Partitioned DML transactions do not need to be committed.

Partitioned DML updates are used to execute a single DML statement with a different execution strategy that provides different, and often better, scalability properties for large, table-wide operations than DML in a Transaction#execute_update transaction. Smaller scoped statements, such as an OLTP workload, should prefer using Transaction#execute_update.

That said, Partitioned DML is not a drop-in replacement for standard DML used in Transaction#execute_update.

  • The DML statement must be fully-partitionable. Specifically, the statement must be expressible as the union of many statements which each access only a single row of the table.
  • The statement is not applied atomically to all rows of the table. Rather, the statement is applied atomically to partitions of the table, in independent internal transactions. Secondary index rows are updated atomically with the base table rows.
  • Partitioned DML does not guarantee exactly-once execution semantics against a partition. The statement will be applied at least once to each partition. It is strongly recommended that the DML statement should be idempotent to avoid unexpected results. For instance, it is potentially dangerous to run a statement such as UPDATE table SET column = column + 1 as it could be run multiple times against some rows.
  • The partitions are committed automatically - there is no support for Commit or Rollback. If the call returns an error, or if the client issuing the DML statement dies, it is possible that some rows had the statement executed on them successfully. It is also possible that statement was never executed against other rows.
  • If any error is encountered during the execution of the partitioned DML operation (for instance, a UNIQUE INDEX violation, division by zero, or a value that cannot be stored due to schema constraints), then the operation is stopped at that point and an error is returned. It is possible that at this point, some partitions have been committed (or even committed multiple times), and other partitions have not been run at all.

Given the above, Partitioned DML is good fit for large, database-wide, operations that are idempotent, such as deleting old rows from a very large table.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

row_count = db.execute_partition_update \
 "UPDATE users SET friends = NULL WHERE active = false"

Query using query parameters:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

row_count = db.execute_partition_update \
 "UPDATE users SET friends = NULL WHERE active = @active",
 params: { active: false }

Query using query options:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

row_count = db.execute_partition_update \
 "UPDATE users SET friends = NULL WHERE active = false",
 query_options: {
   optimizer_version: "1",
   optimizer_statistics_package: "auto_20191128_14_47_22UTC"
 }

Query using custom timeout and retry policy:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

timeout = 30.0
retry_policy = {
  initial_delay: 0.25,
  max_delay:     32.0,
  multiplier:    1.3,
  retry_codes:   ["UNAVAILABLE"]
}
call_options = { timeout: timeout, retry_policy: retry_policy }

row_count = db.execute_partition_update \
 "UPDATE users SET friends = NULL WHERE active = false",
 call_options: call_options

Using request options.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

request_options = { priority: :PRIORITY_MEDIUM }
row_count = db.execute_partition_update \
 "UPDATE users SET friends = NULL WHERE active = @active",
 params: { active: false }, request_options: request_options

Query using tag for request query statistics collection.


require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

request_options = { tag: "Update-Users" }
row_count = db.execute_partition_update \
  "UPDATE users SET friends = NULL WHERE active = false",
  request_options: request_options


703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
# File 'lib/google/cloud/spanner/client.rb', line 703

def execute_partition_update sql, params: nil, types: nil,
                             query_options: nil, request_options: nil,
                             call_options: nil
  ensure_service!

  params, types = Convert.to_input_params_and_types params, types
  request_options = Convert.to_request_options request_options,
                                               tag_type: :request_tag

  results = nil
  @pool.with_session do |session|
    results = session.execute_query \
      sql, params: params, types: types,
      transaction: pdml_transaction(session),
      query_options: query_options, request_options: request_options,
      call_options: call_options
  end
  # Stream all PartialResultSet to get ResultSetStats
  results.rows.to_a
  # Raise an error if there is not a row count returned
  if results.row_count.nil?
    raise Google::Cloud::InvalidArgumentError,
          "Partitioned DML statement is invalid."
  end
  results.row_count
end

#execute_query(sql, params: nil, types: nil, single_use: nil, query_options: nil, request_options: nil, call_options: nil) ⇒ Google::Cloud::Spanner::Results Also known as: execute, query, execute_sql

Executes a SQL query.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

results = db.execute_query "SELECT * FROM users"

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Query using query parameters:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

results = db.execute_query(
  "SELECT * FROM users WHERE active = @active",
  params: { active: true }
)

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Query with a SQL STRUCT query parameter as a Hash:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

user_hash = { id: 1, name: "Charlie", active: false }

results = db.execute_query(
  "SELECT * FROM users WHERE " \
  "ID = @user_struct.id " \
  "AND name = @user_struct.name " \
  "AND active = @user_struct.active",
  params: { user_struct: user_hash }
)

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Specify the SQL STRUCT type using Fields object:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

user_type = db.fields id: :INT64, name: :STRING, active: :BOOL
user_hash = { id: 1, name: nil, active: false }

results = db.execute_query(
  "SELECT * FROM users WHERE " \
  "ID = @user_struct.id " \
  "AND name = @user_struct.name " \
  "AND active = @user_struct.active",
  params: { user_struct: user_hash },
  types: { user_struct: user_type }
)

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Or, query with a SQL STRUCT as a typed Data object:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

user_type = db.fields id: :INT64, name: :STRING, active: :BOOL
user_data = user_type.struct id: 1, name: nil, active: false

results = db.execute_query(
  "SELECT * FROM users WHERE " \
  "ID = @user_struct.id " \
  "AND name = @user_struct.name " \
  "AND active = @user_struct.active",
  params: { user_struct: user_data }
)

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Query using query options:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

results = db.execute_query \
  "SELECT * FROM users", query_options: {
    optimizer_version: "1",
    optimizer_statistics_package: "auto_20191128_14_47_22UTC"
  }

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Query using custom timeout and retry policy:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

timeout = 30.0
retry_policy = {
  initial_delay: 0.25,
  max_delay:     32.0,
  multiplier:    1.3,
  retry_codes:   ["UNAVAILABLE"]
}
call_options = { timeout: timeout, retry_policy: retry_policy }

results = db.execute_query \
  "SELECT * FROM users", call_options: call_options

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Using request options.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { priority: :PRIORITY_MEDIUM }
results = db.execute_query "SELECT * FROM users",
                           request_options: request_options

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Query using tag for request query statistics collection.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { tag: "Read-Users" }
results = db.execute_query "SELECT * FROM users",
                           request_options: request_options

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end


432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/google/cloud/spanner/client.rb', line 432

def execute_query sql, params: nil, types: nil, single_use: nil,
                  query_options: nil, request_options: nil,
                  call_options: nil
  validate_single_use_args! single_use
  ensure_service!

  params, types = Convert.to_input_params_and_types params, types
  request_options = Convert.to_request_options request_options,
                                               tag_type: :request_tag
  single_use_tx = single_use_transaction single_use
  results = nil
  @pool.with_session do |session|
    results = session.execute_query \
      sql, params: params, types: types, transaction: single_use_tx,
      query_options: query_options, request_options: request_options,
      call_options: call_options
  end
  results
end

#fields(types) ⇒ Fields

Creates a configuration object (Fields) that may be provided to queries or used to create STRUCT objects. (The STRUCT will be represented by the Data class.) See #execute and/or Fields#struct.

For more information, see Data Types - Constructing a STRUCT.

Examples:

Create a STRUCT value with named fields using Fields object:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

named_type = db.fields(
  { id: :INT64, name: :STRING, active: :BOOL }
)
named_data = named_type.struct(
  { id: 42, name: nil, active: false }
)

Create a STRUCT value with anonymous field names:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

anon_type = db.fields [:INT64, :STRING, :BOOL]
anon_data = anon_type.struct [42, nil, false]

Create a STRUCT value with duplicate field names:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

dup_type = db.fields [[:x, :INT64], [:x, :STRING], [:x, :BOOL]]
dup_data = dup_type.struct [42, nil, false]


2010
2011
2012
# File 'lib/google/cloud/spanner/client.rb', line 2010

def fields types
  Fields.new types
end

#insert(table, rows, commit_options: nil, request_options: nil) ⇒ Time, CommitResponse

Inserts new rows in a table. If any of the rows already exist, the write or request fails with AlreadyExistsError.

Changes are made immediately upon calling this method using a single-use transaction. To make multiple changes in the same single-use transaction use #commit. To make changes in a transaction that supports reads and automatic retry protection use #transaction.

Note: This method does not feature replay protection present in Transaction#insert (See #transaction). This method makes a single RPC, whereas Transaction#insert requires two RPCs (one of which may be performed in advance), and so this method may be appropriate for latency sensitive and/or high throughput blind inserts.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

db.insert "users", [{ id: 1, name: "Charlie", active: false },
                    { id: 2, name: "Harvey",  active: true }]

Get commit stats

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

records = [{ id: 1, name: "Charlie", active: false },
           { id: 2, name: "Harvey",  active: true }]
commit_options = { return_commit_stats: true }
commit_resp = db.insert "users", records, commit_options: commit_options

puts commit_resp.timestamp
puts commit_resp.stats.mutation_count

Using request options.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { priority: :PRIORITY_MEDIUM }
db.insert "users", [{ id: 1, name: "Charlie", active: false }],
                   request_options: request_options

Insert using tag for transaction statistics collection.


require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { tag: "BulkInsert-Users" }
db.insert "users", [{ id: 1, name: "Charlie", active: false },
                    { id: 2, name: "Harvey",  active: true }],
                    request_options: request_options


1160
1161
1162
1163
1164
1165
1166
1167
1168
# File 'lib/google/cloud/spanner/client.rb', line 1160

def insert table, rows, commit_options: nil, request_options: nil
  request_options = Convert.to_request_options \
    request_options, tag_type: :transaction_tag

  @pool.with_session do |session|
    session.insert table, rows, commit_options: commit_options,
                   request_options: request_options
  end
end

#instanceInstance

The Spanner instance connected to.



91
92
93
# File 'lib/google/cloud/spanner/client.rb', line 91

def instance
  @project.instance instance_id
end

#instance_idString

The unique identifier for the instance.



73
74
75
# File 'lib/google/cloud/spanner/client.rb', line 73

def instance_id
  @instance_id
end

#projectProject

The Spanner project connected to.



85
86
87
# File 'lib/google/cloud/spanner/client.rb', line 85

def project
  @project
end

#project_idString

The unique identifier for the project.



67
68
69
# File 'lib/google/cloud/spanner/client.rb', line 67

def project_id
  @project.service.project
end

#query_optionsHash

A hash of values to specify the custom query options for executing SQL query.



110
111
112
# File 'lib/google/cloud/spanner/client.rb', line 110

def query_options
  @query_options
end

#range(beginning, ending, exclude_begin: false, exclude_end: false) ⇒ Google::Cloud::Spanner::Range

Creates a Spanner Range. This can be used in place of a Ruby Range when needing to exclude the beginning value.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

key_range = db.range 1, 100
results = db.read "users", [:id, :name], keys: key_range

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end


2067
2068
2069
2070
2071
# File 'lib/google/cloud/spanner/client.rb', line 2067

def range beginning, ending, exclude_begin: false, exclude_end: false
  Range.new beginning, ending,
            exclude_begin: exclude_begin,
            exclude_end: exclude_end
end

#read(table, columns, keys: nil, index: nil, limit: nil, single_use: nil, request_options: nil, call_options: nil) ⇒ Google::Cloud::Spanner::Results

Read rows from a database table, as a simple alternative to #execute_query.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

results = db.read "users", [:id, :name]

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Use the keys option to pass keys and/or key ranges to read.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

results = db.read "users", [:id, :name], keys: 1..5

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Read using custom timeout and retry.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

timeout = 30.0
retry_policy = {
  initial_delay: 0.25,
  max_delay:     32.0,
  multiplier:    1.3,
  retry_codes:   ["UNAVAILABLE"]
}
call_options = { timeout: timeout, retry_policy: retry_policy }

results = db.read "users", [:id, :name], call_options: call_options

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Using request options.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { priority: :PRIORITY_MEDIUM }
results = db.read "users", [:id, :name],
                  request_options: request_options

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end

Read using tag for read statistics collection.


require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { tag: "Read-Users-All" }
results = db.read "users", [:id, :name],
                  request_options: request_options

results.rows.each do |row|
  puts "User #{row[:id]} is #{row[:name]}"
end


909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
# File 'lib/google/cloud/spanner/client.rb', line 909

def read table, columns, keys: nil, index: nil, limit: nil,
         single_use: nil, request_options: nil, call_options: nil
  validate_single_use_args! single_use
  ensure_service!

  columns = Array(columns).map(&:to_s)
  keys = Convert.to_key_set keys
  single_use_tx = single_use_transaction single_use
  request_options = Convert.to_request_options request_options,
                                               tag_type: :request_tag

  results = nil
  @pool.with_session do |session|
    results = session.read \
      table, columns, keys: keys, index: index, limit: limit,
                      transaction: single_use_tx,
                      request_options: request_options,
                      call_options: call_options
  end
  results
end

#replace(table, rows, commit_options: nil, request_options: nil) ⇒ Time, CommitResponse

Inserts or replaces rows in a table. If any of the rows already exist, it is deleted, and the column values provided are inserted instead. Unlike #upsert, this means any values not explicitly written become NULL.

Changes are made immediately upon calling this method using a single-use transaction. To make multiple changes in the same single-use transaction use #commit. To make changes in a transaction that supports reads and automatic retry protection use #transaction.

Note: This method does not feature replay protection present in Transaction#replace (See #transaction). This method makes a single RPC, whereas Transaction#replace requires two RPCs (one of which may be performed in advance), and so this method may be appropriate for latency sensitive and/or high throughput blind replaces.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

db.replace "users", [{ id: 1, name: "Charlie", active: false },
                     { id: 2, name: "Harvey",  active: true }]

Get commit stats

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

records = [{ id: 1, name: "Charlie", active: false },
           { id: 2, name: "Harvey",  active: true }]
commit_options = { return_commit_stats: true }
commit_resp = db.replace "users", records, commit_options: commit_options

puts commit_resp.timestamp
puts commit_resp.stats.mutation_count

Using request options.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { priority: :PRIORITY_MEDIUM }
db.replace "users", [{ id: 1, name: "Charlie", active: false }],
                    request_options: request_options

Replace using tag for transaction statistics collection.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { tag: "BulkReplace-Users" }
db.replace "users", [{ id: 1, name: "Charlie", active: false },
                     { id: 2, name: "Harvey",  active: true }],
                    request_options: request_options


1396
1397
1398
1399
1400
1401
# File 'lib/google/cloud/spanner/client.rb', line 1396

def replace table, rows, commit_options: nil, request_options: nil
  @pool.with_session do |session|
    session.replace table, rows, commit_options: commit_options,
                    request_options: request_options
  end
end

#resetObject

Reset the client sessions.



2112
2113
2114
# File 'lib/google/cloud/spanner/client.rb', line 2112

def reset
  @pool.reset
end

#snapshot(strong: nil, timestamp: nil, read_timestamp: nil, staleness: nil, exact_staleness: nil, call_options: nil) {|snapshot| ... } ⇒ Object

Creates a snapshot read-only transaction for reads that execute atomically at a single logical point in time across columns, rows, and tables in a database. For transactions that only read, snapshot read-only transactions provide simpler semantics and are almost always faster than read-write transactions.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

db.snapshot do |snp|
  results = snp.execute_query "SELECT * FROM users"

  results.rows.each do |row|
    puts "User #{row[:id]} is #{row[:name]}"
  end
end

Yields:

  • (snapshot)

    The block for reading and writing data.

Yield Parameters:



1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
# File 'lib/google/cloud/spanner/client.rb', line 1914

def snapshot strong: nil, timestamp: nil, read_timestamp: nil,
             staleness: nil, exact_staleness: nil, call_options: nil
  validate_snapshot_args! strong: strong, timestamp: timestamp,
                          read_timestamp: read_timestamp,
                          staleness: staleness,
                          exact_staleness: exact_staleness

  ensure_service!
  unless Thread.current[:transaction_id].nil?
    raise "Nested snapshots are not allowed"
  end

  @pool.with_session do |session|
    snp_grpc = @project.service.create_snapshot \
      session.path, strong: strong,
                    timestamp: (timestamp || read_timestamp),
                    staleness: (staleness || exact_staleness),
                    call_options: call_options
    Thread.current[:transaction_id] = snp_grpc.id
    snp = Snapshot.from_grpc snp_grpc, session
    yield snp if block_given?
  ensure
    Thread.current[:transaction_id] = nil
  end
  nil
end

#transaction(deadline: 120, commit_options: nil, request_options: nil, call_options: nil) {|transaction| ... } ⇒ Time, CommitResponse

Creates a transaction for reads and writes that execute atomically at a single logical point in time across columns, rows, and tables in a database.

The transaction will always commit unless an error is raised. If the error raised is Rollback the transaction method will return without passing on the error. All other errors will be passed on.

All changes are accumulated in memory until the block completes. Transactions will be automatically retried when possible, until deadline is reached. This operation makes separate API requests to begin and commit the transaction.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

db.transaction do |tx|
  results = tx.execute_query "SELECT * FROM users"

  results.rows.each do |row|
    puts "User #{row[:id]} is #{row[:name]}"
  end

  tx.update "users", [{ id: 1, name: "Charlie", active: false }]
  tx.insert "users", [{ id: 2, name: "Harvey",  active: true }]
end

Manually rollback the transaction using Rollback:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

db.transaction do |tx|
  tx.update "users", [{ id: 1, name: "Charlie", active: false }]
  tx.insert "users", [{ id: 2, name: "Harvey",  active: true }]

  if something_wrong?
    # Rollback the transaction without passing on the error
    # outside of the transaction method.
    raise Google::Cloud::Spanner::Rollback
  end
end

Get commit stats

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

commit_options = { return_commit_stats: true }
commit_resp = db.transaction commit_options: commit_options do |tx|
  results = tx.execute_query "SELECT * FROM users"

  results.rows.each do |row|
    puts "User #{row[:id]} is #{row[:name]}"
  end

  tx.update "users", [{ id: 1, name: "Charlie", active: false }]
  tx.insert "users", [{ id: 2, name: "Harvey",  active: true }]
end

puts commit_resp.timestamp
puts commit_resp.stats.mutation_count

Using request options.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

db.transaction request_options: { priority: :PRIORITY_MEDIUM } do |tx|
  tx.update "users", [{ id: 1, name: "Charlie", active: false }]
  tx.insert "users", [{ id: 2, name: "Harvey",  active: true }]

  request_options = { priority: :PRIORITY_LOW }
  results = tx.execute_query "SELECT * FROM users",
                            request_options: request_options
end

Tags for request and transaction statistics collection.


require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new
db = spanner.client "my-instance", "my-database"

# Transaction tag will be set to "Users-Txn"
db.transaction request_options: { tag: "Users-Txn" } do |tx|
  # The transaction tag set as "Users-Txn"
  # The request tag set as "Users-Txn-1"
  request_options = { tag: "Users-Txn-1" }
  results = tx.execute_query "SELECT * FROM users",
                             request_options: request_options

  results.rows.each do |row|
    puts "User #{row[:id]} is #{row[:name]}"
  end

  # The transaction tag set as "Users-Txn"
  tx.update "users", [{ id: 1, name: "Charlie", active: false }]
  tx.insert "users", [{ id: 2, name: "Harvey",  active: true }]
end

Yields:

  • (transaction)

    The block for reading and writing data.

Yield Parameters:



1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
# File 'lib/google/cloud/spanner/client.rb', line 1788

def transaction deadline: 120, commit_options: nil,
                request_options: nil, call_options: nil
  ensure_service!
  unless Thread.current[:transaction_id].nil?
    raise "Nested transactions are not allowed"
  end

  deadline = validate_deadline deadline
  backoff = 1.0
  start_time = current_time

  request_options = Convert.to_request_options \
    request_options, tag_type: :transaction_tag

  @pool.with_transaction do |tx|
    if request_options
      tx.transaction_tag = request_options[:transaction_tag]
    end

    begin
      Thread.current[:transaction_id] = tx.transaction_id
      yield tx
      commit_resp = @project.service.commit \
        tx.session.path, tx.mutations,
        transaction_id: tx.transaction_id,
        commit_options: commit_options,
        request_options: request_options,
        call_options: call_options
      resp = CommitResponse.from_grpc commit_resp
      commit_options ? resp : resp.timestamp
    rescue GRPC::Aborted, Google::Cloud::AbortedError => e
      # Re-raise if deadline has passed
      if current_time - start_time > deadline
        if e.is_a? GRPC::BadStatus
          e = Google::Cloud::Error.from_error e
        end
        raise e
      end
      # Sleep the amount from RetryDelay, or incremental backoff
      sleep(delay_from_aborted(e) || backoff *= 1.3)
      # Create new transaction on the session and retry the block
      tx = tx.session.create_transaction
      retry
    rescue StandardError => e
      # Rollback transaction when handling unexpected error
      tx.session.rollback tx.transaction_id
      # Return nil if raised with rollback.
      return nil if e.is_a? Rollback
      # Re-raise error.
      raise e
    ensure
      Thread.current[:transaction_id] = nil
    end
  end
end

#update(table, rows, commit_options: nil, request_options: nil) ⇒ Time, CommitResponse

Updates existing rows in a table. If any of the rows does not already exist, the request fails with NotFoundError.

Changes are made immediately upon calling this method using a single-use transaction. To make multiple changes in the same single-use transaction use #commit. To make changes in a transaction that supports reads and automatic retry protection use #transaction.

Note: This method does not feature replay protection present in Transaction#update (See #transaction). This method makes a single RPC, whereas Transaction#update requires two RPCs (one of which may be performed in advance), and so this method may be appropriate for latency sensitive and/or high throughput blind updates.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

db.update "users", [{ id: 1, name: "Charlie", active: false },
                    { id: 2, name: "Harvey",  active: true }]

Get commit stats

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

records = [{ id: 1, name: "Charlie", active: false },
           { id: 2, name: "Harvey",  active: true }]
commit_options = { return_commit_stats: true }
commit_resp = db.update "users", records, commit_options: commit_options

puts commit_resp.timestamp
puts commit_resp.stats.mutation_count

Using request options.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { priority: :PRIORITY_MEDIUM }
db.update "users", [{ id: 1, name: "Charlie", active: false }],
                   request_options: request_options

Updte using tag for transaction statistics collection.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { tag: "BulkUpdate-Users" }
db.update "users", [{ id: 1, name: "Charlie", active: false },
                    { id: 2, name: "Harvey",  active: true }],
                   request_options: request_options


1277
1278
1279
1280
1281
1282
1283
1284
1285
# File 'lib/google/cloud/spanner/client.rb', line 1277

def update table, rows, commit_options: nil, request_options: nil
  request_options = Convert.to_request_options \
    request_options, tag_type: :transaction_tag

  @pool.with_session do |session|
    session.update table, rows, commit_options: commit_options,
                   request_options: request_options
  end
end

#upsert(table, rows, commit_options: nil, request_options: nil) ⇒ Time, CommitResponse Also known as: save

Inserts or updates rows in a table. If any of the rows already exist, then its column values are overwritten with the ones provided. Any column values not explicitly written are preserved.

Changes are made immediately upon calling this method using a single-use transaction. To make multiple changes in the same single-use transaction use #commit. To make changes in a transaction that supports reads and automatic retry protection use #transaction.

Note: This method does not feature replay protection present in Transaction#upsert (See #transaction). This method makes a single RPC, whereas Transaction#upsert requires two RPCs (one of which may be performed in advance), and so this method may be appropriate for latency sensitive and/or high throughput blind upserts.

Examples:

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

db.upsert "users", [{ id: 1, name: "Charlie", active: false },
                    { id: 2, name: "Harvey",  active: true }]

Get commit stats

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

records = [{ id: 1, name: "Charlie", active: false },
           { id: 2, name: "Harvey",  active: true }]
commit_options = { return_commit_stats: true }
commit_resp = db.upsert "users", records, commit_options: commit_options

puts commit_resp.timestamp
puts commit_resp.stats.mutation_count

Using request options.

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { priority: :PRIORITY_MEDIUM }
db.upsert "users", [{ id: 1, name: "Charlie", active: false }],
                   request_options: request_options

Upsert using tag for transaction statistics collection.


require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new

db = spanner.client "my-instance", "my-database"

request_options = { tag: "Bulk-Upsert" }
db.upsert "users", [{ id: 1, name: "Charlie", active: false },
                    { id: 2, name: "Harvey",  active: true }],
                    request_options: request_options


1041
1042
1043
1044
1045
1046
1047
1048
1049
# File 'lib/google/cloud/spanner/client.rb', line 1041

def upsert table, rows, commit_options: nil, request_options: nil
  request_options = Convert.to_request_options \
    request_options, tag_type: :transaction_tag

  @pool.with_session do |session|
    session.upsert table, rows, commit_options: commit_options,
                   request_options: request_options
  end
end