google-cloud-spanner 2.28.0 → 2.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/lib/google/cloud/spanner/backup/job.rb +3 -1
- data/lib/google/cloud/spanner/batch_client.rb +17 -8
- data/lib/google/cloud/spanner/batch_snapshot.rb +18 -4
- data/lib/google/cloud/spanner/batch_update_results.rb +7 -2
- data/lib/google/cloud/spanner/client.rb +102 -84
- data/lib/google/cloud/spanner/commit.rb +3 -0
- data/lib/google/cloud/spanner/commit_response.rb +11 -5
- data/lib/google/cloud/spanner/instance.rb +2 -2
- data/lib/google/cloud/spanner/pool.rb +65 -6
- data/lib/google/cloud/spanner/project.rb +8 -22
- data/lib/google/cloud/spanner/results.rb +51 -16
- data/lib/google/cloud/spanner/service.rb +72 -11
- data/lib/google/cloud/spanner/session.rb +92 -46
- data/lib/google/cloud/spanner/session_cache.rb +125 -0
- data/lib/google/cloud/spanner/session_creation_options.rb +70 -0
- data/lib/google/cloud/spanner/snapshot.rb +60 -11
- data/lib/google/cloud/spanner/transaction.rb +124 -36
- data/lib/google/cloud/spanner/version.rb +1 -1
- metadata +3 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
require "concurrent"
|
|
16
|
+
require "google/cloud/spanner/session"
|
|
17
|
+
require "google/cloud/spanner/session_creation_options"
|
|
18
|
+
|
|
19
|
+
module Google
|
|
20
|
+
module Cloud
|
|
21
|
+
module Spanner
|
|
22
|
+
# Cache for the multiplex `{Google::Cloud::Spanner::Session}` instance.
|
|
23
|
+
# @private
|
|
24
|
+
class SessionCache
|
|
25
|
+
# Time in seconds before this SessionCache will refresh the session.
|
|
26
|
+
# Counted from the session creation time (not from last usage).
|
|
27
|
+
# This is specific to multiplex sessions.
|
|
28
|
+
# The backend can keep sessions alive for quite a bit longer (28 days) but
|
|
29
|
+
# we perform refresh after 7 days.
|
|
30
|
+
# @private
|
|
31
|
+
SESSION_REFRESH_SEC = 7 * 24 * 3600
|
|
32
|
+
|
|
33
|
+
# Create a single-session "cache" for multiplex sessions.
|
|
34
|
+
# @param service [::Google::Cloud::Spanner::Service] A `Spanner::Service` reference.
|
|
35
|
+
# @param session_creation_options [::Google::Cloud::Spanner::SessionCreationOptions] Required.
|
|
36
|
+
# @private
|
|
37
|
+
def initialize service, session_creation_options
|
|
38
|
+
@service = service
|
|
39
|
+
@session_creation_options = session_creation_options
|
|
40
|
+
@mutex = Mutex.new
|
|
41
|
+
@session = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Yields the current session to run an operation (or a series of operations) on.
|
|
45
|
+
# @yield session A session to run requests on
|
|
46
|
+
# @yieldparam session [::Google::Cloud::Spanner::Session]
|
|
47
|
+
# @private
|
|
48
|
+
# @yieldreturn [::Object] The result of the operation that ran on a session.
|
|
49
|
+
# @return [::Object] The value returned by the yielded block.
|
|
50
|
+
def with_session
|
|
51
|
+
ensure_session!
|
|
52
|
+
yield @session
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Re-initializes the session in the session cache.
|
|
56
|
+
# @private
|
|
57
|
+
# @return [::Boolean]
|
|
58
|
+
def reset!
|
|
59
|
+
@mutex.synchronize do
|
|
60
|
+
@session = create_new_session
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Closes the pool. This is a NOP for Multiplex Session Cache since
|
|
67
|
+
# multiplex sessions don't require cleanup.
|
|
68
|
+
# @private
|
|
69
|
+
# @return [::Boolean]
|
|
70
|
+
def close
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns the current session. For use in the `{Spanner::BatchClient}`
|
|
75
|
+
# where usage pattern is incompatible with `with_session`.
|
|
76
|
+
# For other uses please use `with_session` instead.
|
|
77
|
+
# @private
|
|
78
|
+
# @return [::Google::Cloud::Spanner::Session]
|
|
79
|
+
def session
|
|
80
|
+
ensure_session!
|
|
81
|
+
@session
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# Ensures that a single session exists and is current.
|
|
87
|
+
# @private
|
|
88
|
+
# @return [nil]
|
|
89
|
+
def ensure_session!
|
|
90
|
+
return unless @session.nil? || @session.existed_since?(SESSION_REFRESH_SEC)
|
|
91
|
+
|
|
92
|
+
@mutex.synchronize do
|
|
93
|
+
return unless @session.nil? || @session.existed_since?(SESSION_REFRESH_SEC)
|
|
94
|
+
@session = create_new_session
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Creates a new multiplexed `Spanner::Session`.
|
|
101
|
+
# @private
|
|
102
|
+
# @return [::Google::Cloud::Spanner::Session]
|
|
103
|
+
def create_new_session
|
|
104
|
+
ensure_service!
|
|
105
|
+
grpc = @service.create_session(
|
|
106
|
+
@session_creation_options.database_path,
|
|
107
|
+
labels: @session_creation_options.session_labels,
|
|
108
|
+
database_role: @session_creation_options.session_creator_role,
|
|
109
|
+
multiplexed: true
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
Session.from_grpc grpc, @service, query_options: @session_creation_options.query_options
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Raise an error unless an active connection to the service is available.
|
|
116
|
+
# @private
|
|
117
|
+
# @raise [::StandardError]
|
|
118
|
+
# @return [void]
|
|
119
|
+
def ensure_service!
|
|
120
|
+
raise "Must have active connection to service" unless @service
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
module Google
|
|
16
|
+
module Cloud
|
|
17
|
+
module Spanner
|
|
18
|
+
# Options for creating new sessions that clients use
|
|
19
|
+
# to parametrize Pool and SessionCache.
|
|
20
|
+
# Example: session labels.
|
|
21
|
+
# @private
|
|
22
|
+
class SessionCreationOptions
|
|
23
|
+
# The full path to the Spanner database. Values are of the form:
|
|
24
|
+
# `projects/<project_id>/instances/<instance_id>/databases/<database_id>.
|
|
25
|
+
# @private
|
|
26
|
+
# @return [::String]
|
|
27
|
+
attr_reader :database_path
|
|
28
|
+
|
|
29
|
+
# The labels to be applied to all sessions created by the client.
|
|
30
|
+
# Optional. Example: `"team" => "billing-service"`.
|
|
31
|
+
# @private
|
|
32
|
+
# @return [::Hash, nil]
|
|
33
|
+
attr_reader :session_labels
|
|
34
|
+
|
|
35
|
+
# The Spanner session creator role.
|
|
36
|
+
# Optional. Example: `analyst`.
|
|
37
|
+
# @return [::String, nil]
|
|
38
|
+
attr_reader :session_creator_role
|
|
39
|
+
|
|
40
|
+
# A hash of values to specify the custom query options for executing SQL query.
|
|
41
|
+
# Optional. Example option: `:optimizer_version`.
|
|
42
|
+
# @private
|
|
43
|
+
# @return [::Hash, nil]
|
|
44
|
+
attr_reader :query_options
|
|
45
|
+
|
|
46
|
+
# Creates a new SessionCreationOptions object.
|
|
47
|
+
# @param database_path [::String]
|
|
48
|
+
# The full path to the Spanner database. Values are of the form:
|
|
49
|
+
# `projects/<project_id>/instances/<instance_id>/databases/<database_id>.
|
|
50
|
+
# @param session_labels [::Hash, nil] Optional. The labels to be applied to all sessions
|
|
51
|
+
# created by the client. Example: `"team" => "billing-service"`.
|
|
52
|
+
# @param session_creator_role [::String, nil] Optional. The Spanner session creator role.
|
|
53
|
+
# Example: `analyst`.
|
|
54
|
+
# @param query_options [::Hash, nil] Optional. A hash of values to specify the custom
|
|
55
|
+
# query options for executing SQL query. Example option: `:optimizer_version`.
|
|
56
|
+
# @private
|
|
57
|
+
def initialize database_path:, session_labels: nil, session_creator_role: nil, query_options: nil
|
|
58
|
+
if database_path.nil? || database_path.empty?
|
|
59
|
+
raise ArgumentError, "database_path is required for session creation options"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@database_path = database_path
|
|
63
|
+
@session_labels = session_labels
|
|
64
|
+
@session_creator_role = session_creator_role
|
|
65
|
+
@query_options = query_options
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -41,9 +41,26 @@ module Google
|
|
|
41
41
|
# end
|
|
42
42
|
#
|
|
43
43
|
class Snapshot
|
|
44
|
-
#
|
|
44
|
+
# A `V1::Session` reference.
|
|
45
|
+
# @private
|
|
46
|
+
# @return [::Google::Cloud::Spanner::V1::Session]
|
|
45
47
|
attr_accessor :session
|
|
46
48
|
|
|
49
|
+
# Creates a new `Spanner::Snapshot` instance.
|
|
50
|
+
# @param grpc [::Google::Cloud::Spanner::V1::Transaction]
|
|
51
|
+
# Underlying `V1::Transaction` object.
|
|
52
|
+
# @param session [::Google::Cloud::Spanner::Session] A `Spanner::Session` reference.
|
|
53
|
+
# @param directed_read_options [::Hash, nil] Optional. Client options used to set
|
|
54
|
+
# the `directed_read_options` for all ReadRequests and ExecuteSqlRequests.
|
|
55
|
+
# Converts to `V1::DirectedReadOptions`. Example option: `:exclude_replicas`.
|
|
56
|
+
# @private
|
|
57
|
+
# @return [::Google::Cloud::Spanner::Snapshot]
|
|
58
|
+
def initialize grpc, session, directed_read_options: nil
|
|
59
|
+
@grpc = grpc
|
|
60
|
+
@session = session
|
|
61
|
+
@directed_read_options = directed_read_options
|
|
62
|
+
end
|
|
63
|
+
|
|
47
64
|
##
|
|
48
65
|
# Identifier of the transaction results were run in.
|
|
49
66
|
# @return [String] The transaction id.
|
|
@@ -131,6 +148,18 @@ module Google
|
|
|
131
148
|
# available optimizer version.
|
|
132
149
|
# * `:optimizer_statistics_package` (String) Statistics package to
|
|
133
150
|
# use. Empty to use the database default.
|
|
151
|
+
# @param [Hash] request_options Common request options.
|
|
152
|
+
#
|
|
153
|
+
# * `:priority` (Symbol) The relative priority for requests.
|
|
154
|
+
# The priority acts as a hint to the Cloud Spanner scheduler
|
|
155
|
+
# and does not guarantee priority or order of execution.
|
|
156
|
+
# Valid values are `:PRIORITY_LOW`, `:PRIORITY_MEDIUM`,
|
|
157
|
+
# `:PRIORITY_HIGH`. If priority not set then default is
|
|
158
|
+
# `PRIORITY_UNSPECIFIED` is equivalent to `:PRIORITY_HIGH`.
|
|
159
|
+
# * `:tag` (String) A per-request tag which can be applied to
|
|
160
|
+
# queries or reads, used for statistics collection. Tag must be a
|
|
161
|
+
# valid identifier of the form: `[a-zA-Z][a-zA-Z0-9_\-]` between 2
|
|
162
|
+
# and 64 characters in length.
|
|
134
163
|
# @param [Hash] call_options A hash of values to specify the custom
|
|
135
164
|
# call options, e.g., timeout, retries, etc. Call options are
|
|
136
165
|
# optional. The following settings can be provided:
|
|
@@ -304,13 +333,17 @@ module Google
|
|
|
304
333
|
# end
|
|
305
334
|
#
|
|
306
335
|
def execute_query sql, params: nil, types: nil, query_options: nil,
|
|
307
|
-
|
|
336
|
+
request_options: nil, call_options: nil,
|
|
337
|
+
directed_read_options: nil
|
|
308
338
|
ensure_session!
|
|
309
339
|
|
|
310
340
|
params, types = Convert.to_input_params_and_types params, types
|
|
341
|
+
request_options = Convert.to_request_options request_options,
|
|
342
|
+
tag_type: :request_tag
|
|
311
343
|
session.execute_query sql, params: params, types: types,
|
|
312
344
|
transaction: tx_selector,
|
|
313
345
|
query_options: query_options,
|
|
346
|
+
request_options: request_options,
|
|
314
347
|
call_options: call_options,
|
|
315
348
|
directed_read_options: directed_read_options || @directed_read_options
|
|
316
349
|
end
|
|
@@ -334,6 +367,18 @@ module Google
|
|
|
334
367
|
# Optional.
|
|
335
368
|
# @param [Integer] limit If greater than zero, no more than this number
|
|
336
369
|
# of rows will be returned. The default is no limit.
|
|
370
|
+
# @param [Hash] request_options Common request options.
|
|
371
|
+
#
|
|
372
|
+
# * `:priority` (Symbol) The relative priority for requests.
|
|
373
|
+
# The priority acts as a hint to the Cloud Spanner scheduler
|
|
374
|
+
# and does not guarantee priority or order of execution.
|
|
375
|
+
# Valid values are `:PRIORITY_LOW`, `:PRIORITY_MEDIUM`,
|
|
376
|
+
# `:PRIORITY_HIGH`. If priority not set then default is
|
|
377
|
+
# `PRIORITY_UNSPECIFIED` is equivalent to `:PRIORITY_HIGH`.
|
|
378
|
+
# * `:tag` (String) A per-request tag which can be applied to
|
|
379
|
+
# queries or reads, used for statistics collection. Tag must be a
|
|
380
|
+
# valid identifier of the form: `[a-zA-Z][a-zA-Z0-9_\-]` between 2
|
|
381
|
+
# and 64 characters in length.
|
|
337
382
|
# @param [Hash] call_options A hash of values to specify the custom
|
|
338
383
|
# call options, e.g., timeout, retries, etc. Call options are
|
|
339
384
|
# optional. The following settings can be provided:
|
|
@@ -380,7 +425,7 @@ module Google
|
|
|
380
425
|
# end
|
|
381
426
|
#
|
|
382
427
|
def read table, columns, keys: nil, index: nil, limit: nil,
|
|
383
|
-
call_options: nil, directed_read_options: nil
|
|
428
|
+
request_options: nil, call_options: nil, directed_read_options: nil
|
|
384
429
|
ensure_session!
|
|
385
430
|
|
|
386
431
|
columns = Array(columns).map(&:to_s)
|
|
@@ -388,6 +433,7 @@ module Google
|
|
|
388
433
|
|
|
389
434
|
session.read table, columns, keys: keys, index: index, limit: limit,
|
|
390
435
|
transaction: tx_selector,
|
|
436
|
+
request_options: request_options,
|
|
391
437
|
call_options: call_options,
|
|
392
438
|
directed_read_options: directed_read_options || @directed_read_options
|
|
393
439
|
end
|
|
@@ -507,15 +553,18 @@ module Google
|
|
|
507
553
|
exclude_end: exclude_end
|
|
508
554
|
end
|
|
509
555
|
|
|
510
|
-
|
|
511
|
-
# @private Creates a new Snapshot instance from a
|
|
556
|
+
# Creates a new `Spanner::Snapshot` instance from a
|
|
512
557
|
# `Google::Cloud::Spanner::V1::Transaction`.
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
558
|
+
# @param grpc [::Google::Cloud::Spanner::V1::Transaction]
|
|
559
|
+
# Underlying `V1::Transaction` object.
|
|
560
|
+
# @param session [::Google::Cloud::Spanner::Session] A `Spanner::Session` reference.
|
|
561
|
+
# @param directed_read_options [::Hash, nil] Optional. Client options used to set
|
|
562
|
+
# the `directed_read_options` for all ReadRequests and ExecuteSqlRequests.
|
|
563
|
+
# Converts to `V1::DirectedReadOptions`. Example option: `:exclude_replicas`.
|
|
564
|
+
# @private
|
|
565
|
+
# @return [::Google::Cloud::Spanner::Snapshot]
|
|
566
|
+
def self.from_grpc grpc, session, directed_read_options: nil
|
|
567
|
+
new grpc, session, directed_read_options: directed_read_options
|
|
519
568
|
end
|
|
520
569
|
|
|
521
570
|
protected
|
|
@@ -97,19 +97,42 @@ module Google
|
|
|
97
97
|
# @return [::Boolean]
|
|
98
98
|
attr_accessor :exclude_txn_from_change_streams
|
|
99
99
|
|
|
100
|
+
# A token that is required when committing an RW transaction over a multiplexed session
|
|
101
|
+
# It can be read when transaction is created (either by BeginTransaction or by inlined begin),
|
|
102
|
+
# or from a previous operation within existing transaction.
|
|
103
|
+
# @private
|
|
104
|
+
# @return [::Google::Cloud::Spanner::V1::MultiplexedSessionPrecommitToken, nil]
|
|
105
|
+
attr_accessor :precommit_token
|
|
106
|
+
|
|
107
|
+
# An id of the previous transaction, if this new transaction wrapper is being created
|
|
108
|
+
# as a part of a retry. Previous transaction id should be added to TransactionOptions
|
|
109
|
+
# of a new ReadWrite transaction when retry is attempted.
|
|
110
|
+
# @private
|
|
111
|
+
# @return [::String, nil]
|
|
112
|
+
attr_reader :previous_transaction_id
|
|
113
|
+
|
|
100
114
|
# Creates a new `Spanner::Transaction` instance from a `V1::Transaction` object.
|
|
101
115
|
# @param grpc [::Google::Cloud::Spanner::V1::Transaction] Underlying `V1::Transaction` object.
|
|
102
116
|
# @param session [::Google::Cloud::Spanner::Session] The session this transaction is running in.
|
|
103
117
|
# @param exclude_txn_from_change_streams [::Boolean]
|
|
104
118
|
# When `exclude_txn_from_change_streams` is set to `true`, it prevents read
|
|
105
119
|
# or write transactions from being tracked in change streams.
|
|
120
|
+
# @param previous_transaction_id [::String, nil] Optional.
|
|
121
|
+
# An id of the previous transaction, if this new transaction wrapper is being created
|
|
122
|
+
# as a part of a retry. Previous transaction id should be added to TransactionOptions
|
|
123
|
+
# of a new ReadWrite transaction when retry is attempted.
|
|
106
124
|
# @private
|
|
107
125
|
# @return [::Google::Cloud::Spanner::Transaction]
|
|
108
|
-
def initialize grpc, session, exclude_txn_from_change_streams
|
|
126
|
+
def initialize grpc, session, exclude_txn_from_change_streams, previous_transaction_id: nil
|
|
109
127
|
@grpc = grpc
|
|
110
128
|
@session = session
|
|
111
129
|
@exclude_txn_from_change_streams = exclude_txn_from_change_streams
|
|
112
130
|
|
|
131
|
+
# throwing away empty strings for simplicity
|
|
132
|
+
unless previous_transaction_id.nil? || previous_transaction_id.empty?
|
|
133
|
+
@previous_transaction_id = previous_transaction_id
|
|
134
|
+
end
|
|
135
|
+
|
|
113
136
|
@commit = Commit.new
|
|
114
137
|
@seqno = 0
|
|
115
138
|
@exclude_txn_from_change_streams = false
|
|
@@ -130,6 +153,14 @@ module Google
|
|
|
130
153
|
# create a transaction must be synchronized, and any logic that depends on
|
|
131
154
|
# the state of transaction creation must also be synchronized.
|
|
132
155
|
@mutex = Mutex.new
|
|
156
|
+
|
|
157
|
+
# Precommit token is a piece of server-side bookkeeping pushed onto client-side
|
|
158
|
+
# as a part of MultiplexedSession update. Briefly, for a given read-write transaction on a
|
|
159
|
+
# Multiplexed session the client library must:
|
|
160
|
+
# 1. From all read operations, store the most recently received token.
|
|
161
|
+
# 2. Include this final token in the CommitRequest.
|
|
162
|
+
# @type [::Google::Cloud::Spanner::V1::MultiplexedSessionPrecommitToken, nil]
|
|
163
|
+
@precommit_token = nil
|
|
133
164
|
end
|
|
134
165
|
|
|
135
166
|
##
|
|
@@ -397,8 +428,11 @@ module Google
|
|
|
397
428
|
query_options: query_options,
|
|
398
429
|
request_options: request_options,
|
|
399
430
|
call_options: call_options,
|
|
400
|
-
route_to_leader: route_to_leader
|
|
401
|
-
|
|
431
|
+
route_to_leader: route_to_leader,
|
|
432
|
+
precommit_token_notify: method(:update_precommit_token!)
|
|
433
|
+
|
|
434
|
+
update_wrapped_transaction! results.transaction
|
|
435
|
+
|
|
402
436
|
results
|
|
403
437
|
end
|
|
404
438
|
end
|
|
@@ -573,6 +607,11 @@ module Google
|
|
|
573
607
|
query_options: query_options,
|
|
574
608
|
request_options: request_options,
|
|
575
609
|
call_options: call_options
|
|
610
|
+
|
|
611
|
+
# Since this method is calling `execute_query`, the transaction is going to be updated,
|
|
612
|
+
# and the `results` object is going to be set up with precommit token notification reference,
|
|
613
|
+
# so we don't need to do anything special here.
|
|
614
|
+
|
|
576
615
|
# Stream all PartialResultSet to get ResultSetStats
|
|
577
616
|
results.rows.to_a
|
|
578
617
|
# Raise an error if there is not a row count returned
|
|
@@ -676,11 +715,59 @@ module Google
|
|
|
676
715
|
request_options: request_options,
|
|
677
716
|
call_options: call_options, &block
|
|
678
717
|
batch_update_results = BatchUpdateResults.new response
|
|
679
|
-
|
|
718
|
+
update_wrapped_transaction! batch_update_results.transaction
|
|
719
|
+
response.result_sets.each do |result_set|
|
|
720
|
+
update_precommit_token! result_set.precommit_token if result_set.precommit_token
|
|
721
|
+
end
|
|
680
722
|
batch_update_results.row_counts
|
|
681
723
|
end
|
|
682
724
|
end
|
|
683
725
|
|
|
726
|
+
# Updates this `Spanner::Transaction` with a new underlying `V1::Transaction` object.
|
|
727
|
+
# This happens when this `Spanner::Transaction` is in a empty-wrapper mode
|
|
728
|
+
# (it was created by `Google::Cloud::Spanner::Session#create_empty_transaction`).
|
|
729
|
+
# In that mode the inner wrapped `grpc` object representing the `V1::Transaction` is nil,
|
|
730
|
+
# and (almost all) service request run using the "inline-begin" transactions.
|
|
731
|
+
# As part of "inline-begin", a new `V1::Transaction` is created server-side, returned with the
|
|
732
|
+
# results, and in turn should be saved as the new `grpc` object.
|
|
733
|
+
#
|
|
734
|
+
# ! This method is expected to be called from within `safe_execute()` method's block!
|
|
735
|
+
#
|
|
736
|
+
# This method also updates the precommit token, if the new underlying `V1::Transaction` has it.
|
|
737
|
+
#
|
|
738
|
+
# This is a mutator method.
|
|
739
|
+
# @param new_transaction [::Google::Cloud::Spanner::V1::Transaction]
|
|
740
|
+
# `V1::Transaction` object that was created on the server-side.
|
|
741
|
+
# @private
|
|
742
|
+
# @return [void]
|
|
743
|
+
def update_wrapped_transaction! new_transaction
|
|
744
|
+
return unless @grpc.nil?
|
|
745
|
+
return if new_transaction.nil?
|
|
746
|
+
|
|
747
|
+
@grpc = new_transaction
|
|
748
|
+
update_precommit_token! new_transaction.precommit_token
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
# Updates this transaction's precommit token but only if:
|
|
752
|
+
# * new token exists
|
|
753
|
+
# * new token's seq_num is greater.
|
|
754
|
+
#
|
|
755
|
+
# ! This method is expected to be called from within `safe_execute()` method's block!
|
|
756
|
+
#
|
|
757
|
+
# This is a mutator method.
|
|
758
|
+
# @param new_precommit_token [::Google::Cloud::Spanner::V1::MultiplexedSessionPrecommitToken, nil]
|
|
759
|
+
# the new precommit token, if any, from the latest service operation (e.g. from a ResultSet from a read).
|
|
760
|
+
# @private
|
|
761
|
+
# @return [void]
|
|
762
|
+
def update_precommit_token! new_precommit_token
|
|
763
|
+
if !new_precommit_token.nil? && (
|
|
764
|
+
@precommit_token.nil? ||
|
|
765
|
+
new_precommit_token.seq_num > @precommit_token.seq_num
|
|
766
|
+
)
|
|
767
|
+
@precommit_token = new_precommit_token
|
|
768
|
+
end
|
|
769
|
+
end
|
|
770
|
+
|
|
684
771
|
##
|
|
685
772
|
# Read rows from a database table, as a simple alternative to
|
|
686
773
|
# {#execute_query}.
|
|
@@ -754,8 +841,9 @@ module Google
|
|
|
754
841
|
transaction: tx_selector,
|
|
755
842
|
request_options: request_options,
|
|
756
843
|
call_options: call_options,
|
|
757
|
-
route_to_leader: route_to_leader
|
|
758
|
-
|
|
844
|
+
route_to_leader: route_to_leader,
|
|
845
|
+
precommit_token_notify: method(:update_precommit_token!)
|
|
846
|
+
update_wrapped_transaction! results.transaction
|
|
759
847
|
results
|
|
760
848
|
end
|
|
761
849
|
end
|
|
@@ -1143,33 +1231,9 @@ module Google
|
|
|
1143
1231
|
ColumnValue.commit_timestamp
|
|
1144
1232
|
end
|
|
1145
1233
|
|
|
1146
|
-
##
|
|
1147
|
-
# @private
|
|
1148
|
-
# Keeps the transaction current by creating a new transaction.
|
|
1149
|
-
def keepalive!
|
|
1150
|
-
ensure_session!
|
|
1151
|
-
@grpc = session.create_transaction.instance_variable_get :@grpc
|
|
1152
|
-
end
|
|
1153
|
-
|
|
1154
|
-
##
|
|
1155
|
-
# @private
|
|
1156
|
-
# Permanently deletes the transaction and session.
|
|
1157
|
-
def release!
|
|
1158
|
-
ensure_session!
|
|
1159
|
-
session.release!
|
|
1160
|
-
end
|
|
1161
|
-
|
|
1162
|
-
##
|
|
1163
|
-
# @private
|
|
1164
|
-
# Determines if the transaction has been idle longer than the given
|
|
1165
|
-
# duration.
|
|
1166
|
-
def idle_since? duration
|
|
1167
|
-
session.idle_since? duration
|
|
1168
|
-
end
|
|
1169
|
-
|
|
1170
|
-
##
|
|
1171
|
-
# @private
|
|
1172
1234
|
# All of the mutations created in the transaction block.
|
|
1235
|
+
# @private
|
|
1236
|
+
# @return [Array<Google::Cloud::Spanner::V1::Mutation>]
|
|
1173
1237
|
def mutations
|
|
1174
1238
|
@commit.mutations
|
|
1175
1239
|
end
|
|
@@ -1180,10 +1244,14 @@ module Google
|
|
|
1180
1244
|
# @param exclude_txn_from_change_streams [::Boolean] Optional. Defaults to `false`.
|
|
1181
1245
|
# When `exclude_txn_from_change_streams` is set to `true`, it prevents read
|
|
1182
1246
|
# or write transactions from being tracked in change streams.
|
|
1247
|
+
# @param previous_transaction_id [::String, nil] Optional.
|
|
1248
|
+
# An id of the previous transaction, if this new transaction wrapper is being created
|
|
1249
|
+
# as a part of a retry. Previous transaction id should be added to TransactionOptions
|
|
1250
|
+
# of a new ReadWrite transaction when retry is attempted.
|
|
1183
1251
|
# @private
|
|
1184
1252
|
# @return [::Google::Cloud::Spanner::Transaction]
|
|
1185
|
-
def self.from_grpc grpc, session, exclude_txn_from_change_streams: false
|
|
1186
|
-
new grpc, session, exclude_txn_from_change_streams
|
|
1253
|
+
def self.from_grpc grpc, session, exclude_txn_from_change_streams: false, previous_transaction_id: nil
|
|
1254
|
+
new grpc, session, exclude_txn_from_change_streams, previous_transaction_id: previous_transaction_id
|
|
1187
1255
|
end
|
|
1188
1256
|
|
|
1189
1257
|
##
|
|
@@ -1211,16 +1279,27 @@ module Google
|
|
|
1211
1279
|
# @return [::Google::Cloud::Spanner::V1::Transaction, nil] The new transaction
|
|
1212
1280
|
# object, or `nil` if a transaction already exists.
|
|
1213
1281
|
def safe_begin_transaction! exclude_from_change_streams: false, request_options: nil, call_options: nil
|
|
1282
|
+
# If a read-write transaction on a multiplexed session commit mutations
|
|
1283
|
+
# without performing any reads or queries, one of the mutations from the mutation set
|
|
1284
|
+
# must be sent as a mutation key for `BeginTransaction`.
|
|
1285
|
+
# @type [::Google::Cloud::Spanner::V1::Mutation, nil]
|
|
1286
|
+
mutation_key = mutations[0] if mutations.any?
|
|
1287
|
+
|
|
1214
1288
|
@mutex.synchronize do
|
|
1215
1289
|
return if existing_transaction?
|
|
1216
1290
|
ensure_session!
|
|
1217
1291
|
route_to_leader = LARHeaders.begin_transaction true
|
|
1292
|
+
|
|
1293
|
+
# TODO: [virost@, 2025-10] fix this so it uses tx_selector
|
|
1294
|
+
# instead of re-creating it within `Service#begin_transaction`
|
|
1218
1295
|
@grpc = service.begin_transaction(
|
|
1219
1296
|
session.path,
|
|
1220
1297
|
exclude_txn_from_change_streams: exclude_from_change_streams,
|
|
1221
1298
|
request_options: request_options,
|
|
1222
1299
|
call_options: call_options,
|
|
1223
|
-
route_to_leader: route_to_leader
|
|
1300
|
+
route_to_leader: route_to_leader,
|
|
1301
|
+
mutation_key: mutation_key,
|
|
1302
|
+
previous_transaction_id: previous_transaction_id
|
|
1224
1303
|
)
|
|
1225
1304
|
end
|
|
1226
1305
|
end
|
|
@@ -1266,9 +1345,18 @@ module Google
|
|
|
1266
1345
|
# @return [::Google::Cloud::Spanner::V1::TransactionSelector]
|
|
1267
1346
|
def tx_selector exclude_txn_from_change_streams: false
|
|
1268
1347
|
return V1::TransactionSelector.new id: transaction_id if existing_transaction?
|
|
1348
|
+
|
|
1349
|
+
read_write = if @previous_transaction_id.nil?
|
|
1350
|
+
V1::TransactionOptions::ReadWrite.new
|
|
1351
|
+
else
|
|
1352
|
+
V1::TransactionOptions::ReadWrite.new(
|
|
1353
|
+
multiplexed_session_previous_transaction_id: @previous_transaction_id
|
|
1354
|
+
)
|
|
1355
|
+
end
|
|
1356
|
+
|
|
1269
1357
|
V1::TransactionSelector.new(
|
|
1270
1358
|
begin: V1::TransactionOptions.new(
|
|
1271
|
-
read_write:
|
|
1359
|
+
read_write: read_write,
|
|
1272
1360
|
exclude_txn_from_change_streams: exclude_txn_from_change_streams
|
|
1273
1361
|
)
|
|
1274
1362
|
)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: google-cloud-spanner
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.30.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Moore
|
|
@@ -161,6 +161,8 @@ files:
|
|
|
161
161
|
- lib/google/cloud/spanner/results.rb
|
|
162
162
|
- lib/google/cloud/spanner/service.rb
|
|
163
163
|
- lib/google/cloud/spanner/session.rb
|
|
164
|
+
- lib/google/cloud/spanner/session_cache.rb
|
|
165
|
+
- lib/google/cloud/spanner/session_creation_options.rb
|
|
164
166
|
- lib/google/cloud/spanner/snapshot.rb
|
|
165
167
|
- lib/google/cloud/spanner/status.rb
|
|
166
168
|
- lib/google/cloud/spanner/transaction.rb
|