google-cloud-spanner 2.16.1 → 2.18.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/AUTHENTICATION.md +1 -1
- data/CHANGELOG.md +12 -0
- data/lib/google/cloud/spanner/batch_snapshot.rb +18 -8
- data/lib/google/cloud/spanner/batch_update_results.rb +50 -0
- data/lib/google/cloud/spanner/client.rb +42 -19
- data/lib/google/cloud/spanner/commit.rb +4 -4
- data/lib/google/cloud/spanner/pool.rb +31 -97
- data/lib/google/cloud/spanner/project.rb +4 -8
- data/lib/google/cloud/spanner/results.rb +21 -53
- data/lib/google/cloud/spanner/service.rb +26 -15
- data/lib/google/cloud/spanner/session.rb +38 -17
- data/lib/google/cloud/spanner/transaction.rb +127 -19
- data/lib/google/cloud/spanner/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37f9332aedaf9d53b1eb13596dafa4b3571393449b85ce5e63750b5c57381e3f
|
4
|
+
data.tar.gz: b137394c7819e4795a98727e626772ece2404ef40bc7ef84153014768ddbfb89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ea41265627415904e066549390d59ad72a2be577c3b6f7ac67d8a47197e4f2df4a79f9c2c14735bcd383989b2f3bfa602fe68e3ebde1d7cc055252d8644f36f
|
7
|
+
data.tar.gz: 2cc9bf3bbe521e85c72ad2dd255eedf09225c0bbcc67bc4c319a4036aa7beac01c18f73a0fb6ff1fd203b96b15ff2b3bcbb82f2f8ddfd0f2aa72e1d2f31f03e9
|
data/AUTHENTICATION.md
CHANGED
@@ -32,7 +32,7 @@ client = Google::Cloud::Spanner.new
|
|
32
32
|
## Project and Credential Lookup
|
33
33
|
|
34
34
|
The google-cloud-spanner library aims to make authentication
|
35
|
-
as simple as possible
|
35
|
+
as simple as possible and provides several mechanisms to configure your system
|
36
36
|
without providing **Project ID** and **Service Account Credentials** directly in
|
37
37
|
code.
|
38
38
|
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 2.18.0 (2023-09-05)
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* Implement "Inline Begin Transaction" ([#54](https://github.com/googleapis/ruby-spanner/issues/54))
|
8
|
+
|
9
|
+
### 2.17.0 (2023-06-23)
|
10
|
+
|
11
|
+
#### Features
|
12
|
+
|
13
|
+
* support data_boost_enabled for partitioned query and read ([#46](https://github.com/googleapis/ruby-spanner/issues/46))
|
14
|
+
|
3
15
|
### 2.16.1 (2022-11-02)
|
4
16
|
|
5
17
|
#### Documentation
|
@@ -188,6 +188,9 @@ module Google
|
|
188
188
|
# * `:multiplier` (`Numeric`) - The incremental backoff multiplier.
|
189
189
|
# * `:retry_codes` (`Array<String>`) - The error codes that should
|
190
190
|
# trigger a retry.
|
191
|
+
# @param [Boolean] data_boost_enabled If this field is
|
192
|
+
# set `true`, the request will be executed via offline access.
|
193
|
+
# Defaults to `false`.
|
191
194
|
#
|
192
195
|
# @return [Array<Google::Cloud::Spanner::Partition>] The partitions
|
193
196
|
# created by the query partition.
|
@@ -211,7 +214,7 @@ module Google
|
|
211
214
|
#
|
212
215
|
def partition_query sql, params: nil, types: nil,
|
213
216
|
partition_size_bytes: nil, max_partitions: nil,
|
214
|
-
query_options: nil, call_options: nil
|
217
|
+
query_options: nil, call_options: nil, data_boost_enabled: false
|
215
218
|
ensure_session!
|
216
219
|
|
217
220
|
params, types = Convert.to_input_params_and_types params, types
|
@@ -231,8 +234,9 @@ module Google
|
|
231
234
|
param_types: types,
|
232
235
|
transaction: tx_selector,
|
233
236
|
partition_token: grpc.partition_token,
|
234
|
-
query_options: query_options
|
235
|
-
|
237
|
+
query_options: query_options,
|
238
|
+
data_boost_enabled: data_boost_enabled
|
239
|
+
}.compact
|
236
240
|
)
|
237
241
|
Partition.from_execute_sql_grpc execute_sql_grpc
|
238
242
|
end
|
@@ -277,6 +281,9 @@ module Google
|
|
277
281
|
# * `:multiplier` (`Numeric`) - The incremental backoff multiplier.
|
278
282
|
# * `:retry_codes` (`Array<String>`) - The error codes that should
|
279
283
|
# trigger a retry.
|
284
|
+
# @param [Boolean] data_boost_enabled If this field is
|
285
|
+
# set `true`, the request will be executed via offline access.
|
286
|
+
# Defaults to `false`.
|
280
287
|
#
|
281
288
|
# @return [Array<Google::Cloud::Spanner::Partition>] The partitions
|
282
289
|
# created by the read partition.
|
@@ -298,7 +305,7 @@ module Google
|
|
298
305
|
#
|
299
306
|
def partition_read table, columns, keys: nil, index: nil,
|
300
307
|
partition_size_bytes: nil, max_partitions: nil,
|
301
|
-
call_options: nil
|
308
|
+
call_options: nil, data_boost_enabled: false
|
302
309
|
ensure_session!
|
303
310
|
|
304
311
|
columns = Array(columns).map(&:to_s)
|
@@ -321,8 +328,9 @@ module Google
|
|
321
328
|
key_set: keys,
|
322
329
|
index: index,
|
323
330
|
transaction: tx_selector,
|
324
|
-
partition_token: grpc.partition_token
|
325
|
-
|
331
|
+
partition_token: grpc.partition_token,
|
332
|
+
data_boost_enabled: data_boost_enabled
|
333
|
+
}.compact
|
326
334
|
)
|
327
335
|
Partition.from_read_grpc read_grpc
|
328
336
|
end
|
@@ -804,7 +812,8 @@ module Google
|
|
804
812
|
transaction: partition.execute.transaction,
|
805
813
|
partition_token: partition.execute.partition_token,
|
806
814
|
query_options: query_options,
|
807
|
-
call_options: call_options
|
815
|
+
call_options: call_options,
|
816
|
+
data_boost_enabled: partition.execute.data_boost_enabled
|
808
817
|
end
|
809
818
|
|
810
819
|
def execute_partition_read partition, call_options: nil
|
@@ -814,7 +823,8 @@ module Google
|
|
814
823
|
index: partition.read.index,
|
815
824
|
transaction: partition.read.transaction,
|
816
825
|
partition_token: partition.read.partition_token,
|
817
|
-
call_options: call_options
|
826
|
+
call_options: call_options,
|
827
|
+
data_boost_enabled: partition.read.data_boost_enabled
|
818
828
|
end
|
819
829
|
end
|
820
830
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Copyright 2023 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
|
+
|
16
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Spanner
|
19
|
+
##
|
20
|
+
# @private Helper class to process BatchDML response
|
21
|
+
class BatchUpdateResults
|
22
|
+
## Object of type
|
23
|
+
# Google::Cloud::Spanner::V1::ExecuteBatchDmlResponse
|
24
|
+
attr_reader :grpc
|
25
|
+
|
26
|
+
def initialize grpc
|
27
|
+
@grpc = grpc
|
28
|
+
end
|
29
|
+
|
30
|
+
def row_counts
|
31
|
+
if @grpc.status.code.zero?
|
32
|
+
@grpc.result_sets.map { |rs| rs.stats.row_count_exact }
|
33
|
+
else
|
34
|
+
begin
|
35
|
+
raise Google::Cloud::Error.from_error @grpc.status
|
36
|
+
rescue Google::Cloud::Error
|
37
|
+
raise Google::Cloud::Spanner::BatchUpdateError.from_grpc @grpc
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Returns transaction if available. Otherwise returns nil
|
44
|
+
def transaction
|
45
|
+
@grpc&.result_sets&.first&.metadata&.transaction
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -49,6 +49,10 @@ module Google
|
|
49
49
|
# end
|
50
50
|
#
|
51
51
|
class Client
|
52
|
+
##
|
53
|
+
# @private
|
54
|
+
IS_TRANSACTION_RUNNING_KEY = "ruby_spanner_is_transaction_running".freeze
|
55
|
+
|
52
56
|
##
|
53
57
|
# @private Creates a new Spanner Client instance.
|
54
58
|
def initialize project, instance_id, database_id, session_labels: nil,
|
@@ -1634,6 +1638,7 @@ module Google
|
|
1634
1638
|
# rubocop:disable Metrics/MethodLength
|
1635
1639
|
# rubocop:disable Metrics/BlockLength
|
1636
1640
|
|
1641
|
+
|
1637
1642
|
##
|
1638
1643
|
# Creates a transaction for reads and writes that execute atomically at
|
1639
1644
|
# a single logical point in time across columns, rows, and tables in a
|
@@ -1788,7 +1793,7 @@ module Google
|
|
1788
1793
|
def transaction deadline: 120, commit_options: nil,
|
1789
1794
|
request_options: nil, call_options: nil
|
1790
1795
|
ensure_service!
|
1791
|
-
unless Thread.current[
|
1796
|
+
unless Thread.current[IS_TRANSACTION_RUNNING_KEY].nil?
|
1792
1797
|
raise "Nested transactions are not allowed"
|
1793
1798
|
end
|
1794
1799
|
|
@@ -1799,44 +1804,44 @@ module Google
|
|
1799
1804
|
request_options = Convert.to_request_options \
|
1800
1805
|
request_options, tag_type: :transaction_tag
|
1801
1806
|
|
1802
|
-
@pool.
|
1807
|
+
@pool.with_session do |session|
|
1808
|
+
tx = session.create_empty_transaction
|
1803
1809
|
if request_options
|
1804
1810
|
tx.transaction_tag = request_options[:transaction_tag]
|
1805
1811
|
end
|
1806
1812
|
|
1807
1813
|
begin
|
1808
|
-
Thread.current[
|
1814
|
+
Thread.current[IS_TRANSACTION_RUNNING_KEY] = true
|
1809
1815
|
yield tx
|
1816
|
+
transaction_id = nil
|
1817
|
+
transaction_id = tx.transaction_id if tx.existing_transaction?
|
1810
1818
|
commit_resp = @project.service.commit \
|
1811
1819
|
tx.session.path, tx.mutations,
|
1812
|
-
transaction_id:
|
1820
|
+
transaction_id: transaction_id,
|
1813
1821
|
commit_options: commit_options,
|
1814
1822
|
request_options: request_options,
|
1815
1823
|
call_options: call_options
|
1816
1824
|
resp = CommitResponse.from_grpc commit_resp
|
1817
1825
|
commit_options ? resp : resp.timestamp
|
1818
|
-
rescue GRPC::Aborted,
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1822
|
-
|
1823
|
-
end
|
1824
|
-
raise e
|
1825
|
-
end
|
1826
|
+
rescue GRPC::Aborted,
|
1827
|
+
Google::Cloud::AbortedError,
|
1828
|
+
GRPC::Internal,
|
1829
|
+
Google::Cloud::InternalError => e
|
1830
|
+
check_and_propagate_err! e, (current_time - start_time > deadline)
|
1826
1831
|
# Sleep the amount from RetryDelay, or incremental backoff
|
1827
1832
|
sleep(delay_from_aborted(e) || backoff *= 1.3)
|
1828
1833
|
# Create new transaction on the session and retry the block
|
1829
|
-
tx =
|
1834
|
+
tx = session.create_transaction
|
1830
1835
|
retry
|
1831
1836
|
rescue StandardError => e
|
1832
1837
|
# Rollback transaction when handling unexpected error
|
1833
|
-
tx.session.rollback tx.transaction_id
|
1838
|
+
tx.session.rollback tx.transaction_id if tx.existing_transaction?
|
1834
1839
|
# Return nil if raised with rollback.
|
1835
1840
|
return nil if e.is_a? Rollback
|
1836
1841
|
# Re-raise error.
|
1837
1842
|
raise e
|
1838
1843
|
ensure
|
1839
|
-
Thread.current[
|
1844
|
+
Thread.current[IS_TRANSACTION_RUNNING_KEY] = nil
|
1840
1845
|
end
|
1841
1846
|
end
|
1842
1847
|
end
|
@@ -1919,7 +1924,7 @@ module Google
|
|
1919
1924
|
exact_staleness: exact_staleness
|
1920
1925
|
|
1921
1926
|
ensure_service!
|
1922
|
-
unless Thread.current[
|
1927
|
+
unless Thread.current[IS_TRANSACTION_RUNNING_KEY].nil?
|
1923
1928
|
raise "Nested snapshots are not allowed"
|
1924
1929
|
end
|
1925
1930
|
|
@@ -1929,11 +1934,11 @@ module Google
|
|
1929
1934
|
timestamp: (timestamp || read_timestamp),
|
1930
1935
|
staleness: (staleness || exact_staleness),
|
1931
1936
|
call_options: call_options
|
1932
|
-
Thread.current[
|
1937
|
+
Thread.current[IS_TRANSACTION_RUNNING_KEY] = true
|
1933
1938
|
snp = Snapshot.from_grpc snp_grpc, session
|
1934
1939
|
yield snp if block_given?
|
1935
1940
|
ensure
|
1936
|
-
Thread.current[
|
1941
|
+
Thread.current[IS_TRANSACTION_RUNNING_KEY] = nil
|
1937
1942
|
end
|
1938
1943
|
nil
|
1939
1944
|
end
|
@@ -2215,7 +2220,7 @@ module Google
|
|
2215
2220
|
min_read_timestamp: bounded_timestamp,
|
2216
2221
|
max_staleness: bounded_staleness,
|
2217
2222
|
return_read_timestamp: true
|
2218
|
-
}.
|
2223
|
+
}.compact)))
|
2219
2224
|
end
|
2220
2225
|
|
2221
2226
|
def pdml_transaction session
|
@@ -2267,6 +2272,24 @@ module Google
|
|
2267
2272
|
# Any error indicates the backoff should be handled elsewhere
|
2268
2273
|
nil
|
2269
2274
|
end
|
2275
|
+
|
2276
|
+
##
|
2277
|
+
# Determines if a transaction error should be propagated to the user.
|
2278
|
+
# And re-raises the error accordingly
|
2279
|
+
def check_and_propagate_err! err, deadline_passed
|
2280
|
+
raise err if internal_error_and_not_retryable? err
|
2281
|
+
return unless deadline_passed
|
2282
|
+
if err.is_a? GRPC::BadStatus
|
2283
|
+
raise Google::Cloud::Error.from_error err
|
2284
|
+
end
|
2285
|
+
raise err
|
2286
|
+
end
|
2287
|
+
|
2288
|
+
def internal_error_and_not_retryable? error
|
2289
|
+
(error.instance_of?(Google::Cloud::InternalError) ||
|
2290
|
+
error.instance_of?(GRPC::Internal)) &&
|
2291
|
+
!@project.service.retryable?(error)
|
2292
|
+
end
|
2270
2293
|
end
|
2271
2294
|
end
|
2272
2295
|
end
|
@@ -94,7 +94,7 @@ module Google
|
|
94
94
|
def upsert table, *rows
|
95
95
|
rows = Array(rows).flatten
|
96
96
|
return rows if rows.empty?
|
97
|
-
rows.
|
97
|
+
rows.compact
|
98
98
|
rows.delete_if(&:empty?)
|
99
99
|
@mutations += rows.map do |row|
|
100
100
|
V1::Mutation.new(
|
@@ -153,7 +153,7 @@ module Google
|
|
153
153
|
def insert table, *rows
|
154
154
|
rows = Array(rows).flatten
|
155
155
|
return rows if rows.empty?
|
156
|
-
rows.
|
156
|
+
rows.compact
|
157
157
|
rows.delete_if(&:empty?)
|
158
158
|
@mutations += rows.map do |row|
|
159
159
|
V1::Mutation.new(
|
@@ -211,7 +211,7 @@ module Google
|
|
211
211
|
def update table, *rows
|
212
212
|
rows = Array(rows).flatten
|
213
213
|
return rows if rows.empty?
|
214
|
-
rows.
|
214
|
+
rows.compact
|
215
215
|
rows.delete_if(&:empty?)
|
216
216
|
@mutations += rows.map do |row|
|
217
217
|
V1::Mutation.new(
|
@@ -271,7 +271,7 @@ module Google
|
|
271
271
|
def replace table, *rows
|
272
272
|
rows = Array(rows).flatten
|
273
273
|
return rows if rows.empty?
|
274
|
-
rows.
|
274
|
+
rows.compact
|
275
275
|
rows.delete_if(&:empty?)
|
276
276
|
@mutations += rows.map do |row|
|
277
277
|
V1::Mutation.new(
|
@@ -29,26 +29,22 @@ module Google
|
|
29
29
|
# {Google::Cloud::Spanner::Session} instances.
|
30
30
|
#
|
31
31
|
class Pool
|
32
|
-
attr_accessor :
|
33
|
-
attr_accessor :
|
34
|
-
attr_accessor :transaction_queue
|
32
|
+
attr_accessor :sessions_available
|
33
|
+
attr_accessor :sessions_in_use
|
35
34
|
|
36
35
|
def initialize client, min: 10, max: 100, keepalive: 1800,
|
37
|
-
|
36
|
+
fail: true, threads: nil
|
38
37
|
@client = client
|
39
38
|
@min = min
|
40
39
|
@max = max
|
41
40
|
@keepalive = keepalive
|
42
|
-
@write_ratio = write_ratio
|
43
|
-
@write_ratio = 0 if write_ratio.negative?
|
44
|
-
@write_ratio = 1 if write_ratio > 1
|
45
41
|
@fail = fail
|
46
42
|
@threads = threads || [2, Concurrent.processor_count * 2].max
|
47
43
|
|
48
44
|
@mutex = Mutex.new
|
49
45
|
@resource = ConditionVariable.new
|
50
46
|
|
51
|
-
# initialize pool and availability
|
47
|
+
# initialize pool and availability stack
|
52
48
|
init
|
53
49
|
end
|
54
50
|
|
@@ -69,10 +65,11 @@ module Google
|
|
69
65
|
|
70
66
|
# Use LIFO to ensure sessions are used from backend caches, which
|
71
67
|
# will reduce the read / write latencies on user requests.
|
72
|
-
read_session =
|
73
|
-
|
74
|
-
|
75
|
-
|
68
|
+
read_session = sessions_available.pop # LIFO
|
69
|
+
if read_session
|
70
|
+
sessions_in_use << read_session
|
71
|
+
return read_session
|
72
|
+
end
|
76
73
|
|
77
74
|
if can_allocate_more_sessions?
|
78
75
|
action = :new
|
@@ -85,73 +82,25 @@ module Google
|
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@mutex.synchronize do
|
93
|
-
unless all_sessions.include? session
|
94
|
-
raise ArgumentError, "Cannot checkin session"
|
85
|
+
if action == :new
|
86
|
+
session = new_session!
|
87
|
+
@mutex.synchronize do
|
88
|
+
sessions_in_use << session
|
95
89
|
end
|
96
|
-
|
97
|
-
session_queue.push session
|
98
|
-
|
99
|
-
@resource.signal
|
90
|
+
return session
|
100
91
|
end
|
101
92
|
|
102
93
|
nil
|
103
94
|
end
|
104
95
|
|
105
|
-
def
|
106
|
-
tx = checkout_transaction
|
107
|
-
begin
|
108
|
-
yield tx
|
109
|
-
ensure
|
110
|
-
future do
|
111
|
-
# Create and checkin a new transaction
|
112
|
-
tx = tx.session.create_transaction
|
113
|
-
checkin_transaction tx
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def checkout_transaction
|
119
|
-
action = nil
|
120
|
-
@mutex.synchronize do
|
121
|
-
loop do
|
122
|
-
raise ClientClosedError if @closed
|
123
|
-
|
124
|
-
write_transaction = transaction_queue.pop # LIFO
|
125
|
-
return write_transaction if write_transaction
|
126
|
-
read_session = session_queue.pop
|
127
|
-
if read_session
|
128
|
-
action = read_session
|
129
|
-
break
|
130
|
-
end
|
131
|
-
|
132
|
-
if can_allocate_more_sessions?
|
133
|
-
action = :new
|
134
|
-
break
|
135
|
-
end
|
136
|
-
|
137
|
-
raise SessionLimitError if @fail
|
138
|
-
|
139
|
-
@resource.wait @mutex
|
140
|
-
end
|
141
|
-
end
|
142
|
-
if action.is_a? Google::Cloud::Spanner::Session
|
143
|
-
return action.create_transaction
|
144
|
-
end
|
145
|
-
return new_transaction! if action == :new
|
146
|
-
end
|
147
|
-
|
148
|
-
def checkin_transaction txn
|
96
|
+
def checkin_session session
|
149
97
|
@mutex.synchronize do
|
150
|
-
unless
|
98
|
+
unless sessions_in_use.include? session
|
151
99
|
raise ArgumentError, "Cannot checkin session"
|
152
100
|
end
|
153
101
|
|
154
|
-
|
102
|
+
sessions_available.push session
|
103
|
+
sessions_in_use.delete_if { |s| s.session_id == session.session_id }
|
155
104
|
|
156
105
|
@resource.signal
|
157
106
|
end
|
@@ -182,22 +131,20 @@ module Google
|
|
182
131
|
to_release = []
|
183
132
|
|
184
133
|
@mutex.synchronize do
|
185
|
-
available_count =
|
134
|
+
available_count = sessions_available.count
|
186
135
|
release_count = @min - available_count
|
187
136
|
release_count = 0 if release_count.negative?
|
188
137
|
|
189
|
-
to_keepalive +=
|
138
|
+
to_keepalive += sessions_available.select do |x|
|
190
139
|
x.idle_since? @keepalive
|
191
140
|
end
|
192
141
|
|
193
|
-
# Remove a random portion of the sessions
|
142
|
+
# Remove a random portion of the sessions
|
194
143
|
to_release = to_keepalive.sample release_count
|
195
144
|
to_keepalive -= to_release
|
196
145
|
|
197
146
|
# Remove those to be released from circulation
|
198
|
-
@
|
199
|
-
@session_queue -= to_release
|
200
|
-
@transaction_queue -= to_release
|
147
|
+
@sessions_available -= to_release
|
201
148
|
end
|
202
149
|
|
203
150
|
to_release.each { |x| future { x.release! } }
|
@@ -210,21 +157,13 @@ module Google
|
|
210
157
|
# init the thread pool
|
211
158
|
@thread_pool = Concurrent::ThreadPoolExecutor.new \
|
212
159
|
max_threads: @threads
|
213
|
-
# init the
|
160
|
+
# init the stacks
|
214
161
|
@new_sessions_in_process = 0
|
215
|
-
@transaction_queue = []
|
216
162
|
# init the keepalive task
|
217
163
|
create_keepalive_task!
|
218
|
-
# init session
|
219
|
-
@
|
220
|
-
|
221
|
-
num_transactions = (@min * @write_ratio).round
|
222
|
-
pending_transactions = sessions.shift num_transactions
|
223
|
-
# init transaction queue
|
224
|
-
pending_transactions.each do |transaction|
|
225
|
-
future { checkin_transaction transaction.create_transaction }
|
226
|
-
end
|
227
|
-
@session_queue = sessions
|
164
|
+
# init session stack
|
165
|
+
@sessions_available = @client.batch_create_new_sessions @min
|
166
|
+
@sessions_in_use = []
|
228
167
|
end
|
229
168
|
|
230
169
|
def shutdown
|
@@ -236,10 +175,10 @@ module Google
|
|
236
175
|
@resource.broadcast
|
237
176
|
# Delete all sessions
|
238
177
|
@mutex.synchronize do
|
239
|
-
|
240
|
-
|
241
|
-
@
|
242
|
-
@
|
178
|
+
sessions_available.each { |s| future { s.release! } }
|
179
|
+
sessions_in_use.each { |s| future { s.release! } }
|
180
|
+
@sessions_available = []
|
181
|
+
@sessions_in_use = []
|
243
182
|
end
|
244
183
|
# shutdown existing thread pool
|
245
184
|
@thread_pool.shutdown
|
@@ -261,19 +200,14 @@ module Google
|
|
261
200
|
|
262
201
|
@mutex.synchronize do
|
263
202
|
@new_sessions_in_process -= 1
|
264
|
-
all_sessions << session
|
265
203
|
end
|
266
204
|
|
267
205
|
session
|
268
206
|
end
|
269
207
|
|
270
|
-
def new_transaction!
|
271
|
-
new_session!.create_transaction
|
272
|
-
end
|
273
|
-
|
274
208
|
def can_allocate_more_sessions?
|
275
209
|
# This is expected to be called from within a synchronize block
|
276
|
-
|
210
|
+
sessions_available.size + sessions_in_use.size + @new_sessions_in_process < @max
|
277
211
|
end
|
278
212
|
|
279
213
|
def create_keepalive_task!
|
@@ -515,17 +515,14 @@ module Google
|
|
515
515
|
# before an attempt is made to prevent the idle sessions from being
|
516
516
|
# closed by the Cloud Spanner service. The default is 1800 (30
|
517
517
|
# minutes).
|
518
|
-
# * `:write_ratio` (Float) The ratio of sessions with pre-allocated
|
519
|
-
# transactions to those without. Pre-allocating transactions
|
520
|
-
# improves the performance of writes made by the client. The higher
|
521
|
-
# the value, the more transactions are pre-allocated. The value must
|
522
|
-
# be >= 0 and <= 1. The default is 0.3.
|
523
518
|
# * `:fail` (true/false) When `true` the client raises a
|
524
519
|
# {SessionLimitError} when the client has allocated the `max` number
|
525
520
|
# of sessions. When `false` the client blocks until a session
|
526
521
|
# becomes available. The default is `true`.
|
527
522
|
# * `:threads` (Integer) The number of threads in the thread pool. The
|
528
523
|
# default is twice the number of available CPUs.
|
524
|
+
# * `:write_ratio` (Float) Deprecated. This field is no longer needed
|
525
|
+
# and will be removed in a future release.
|
529
526
|
# @param [Hash] labels The labels to be applied to all sessions
|
530
527
|
# created by the client. Cloud Labels are a flexible and lightweight
|
531
528
|
# mechanism for organizing cloud resources into groups that reflect a
|
@@ -674,9 +671,8 @@ module Google
|
|
674
671
|
def valid_session_pool_options opts = {}
|
675
672
|
{
|
676
673
|
min: opts[:min], max: opts[:max], keepalive: opts[:keepalive],
|
677
|
-
|
678
|
-
|
679
|
-
}.delete_if { |_k, v| v.nil? }
|
674
|
+
fail: opts[:fail], threads: opts[:threads]
|
675
|
+
}.compact
|
680
676
|
end
|
681
677
|
end
|
682
678
|
end
|
@@ -41,8 +41,11 @@ module Google
|
|
41
41
|
# end
|
42
42
|
#
|
43
43
|
class Results
|
44
|
-
|
45
|
-
|
44
|
+
##
|
45
|
+
# @private Object of type
|
46
|
+
# Google::Cloud::Spanner::V1::ResultSetMetadata
|
47
|
+
attr_reader :metadata
|
48
|
+
|
46
49
|
##
|
47
50
|
# The read timestamp chosen for single-use snapshots (read-only
|
48
51
|
# transactions).
|
@@ -75,6 +78,13 @@ module Google
|
|
75
78
|
@fields ||= Fields.from_grpc @metadata.row_type.fields
|
76
79
|
end
|
77
80
|
|
81
|
+
##
|
82
|
+
# @private
|
83
|
+
# Returns a transaction if available
|
84
|
+
def transaction
|
85
|
+
@metadata&.transaction
|
86
|
+
end
|
87
|
+
|
78
88
|
# rubocop:disable all
|
79
89
|
|
80
90
|
##
|
@@ -177,7 +187,7 @@ module Google
|
|
177
187
|
|
178
188
|
if resumable?(resume_token)
|
179
189
|
should_resume_request = true
|
180
|
-
elsif retryable?(err)
|
190
|
+
elsif @service.retryable?(err)
|
181
191
|
should_retry_request = true
|
182
192
|
elsif err.is_a?(Google::Cloud::Error)
|
183
193
|
raise err
|
@@ -227,22 +237,6 @@ module Google
|
|
227
237
|
resume_token && !resume_token.empty?
|
228
238
|
end
|
229
239
|
|
230
|
-
##
|
231
|
-
# @private
|
232
|
-
# Checks if a request can be retried. This is based on the error returned.
|
233
|
-
# Retryable errors are:
|
234
|
-
# - Unavailable error
|
235
|
-
# - Internal EOS error
|
236
|
-
# - Internal RST_STREAM error
|
237
|
-
def retryable? err
|
238
|
-
err.instance_of?(Google::Cloud::UnavailableError) ||
|
239
|
-
err.instance_of?(GRPC::Unavailable) ||
|
240
|
-
(err.instance_of?(Google::Cloud::InternalError) && err.message.include?(EOS_INTERNAL_ERROR)) ||
|
241
|
-
(err.instance_of?(GRPC::Internal) && err.details.include?(EOS_INTERNAL_ERROR)) ||
|
242
|
-
(err.instance_of?(Google::Cloud::InternalError) && err.message.include?(RST_STREAM_INTERNAL_ERROR)) ||
|
243
|
-
(err.instance_of?(GRPC::Internal) && err.details.include?(RST_STREAM_INTERNAL_ERROR))
|
244
|
-
end
|
245
|
-
|
246
240
|
##
|
247
241
|
# @private
|
248
242
|
# Resumes a request, by re-executing it with a resume token.
|
@@ -315,46 +309,20 @@ module Google
|
|
315
309
|
end
|
316
310
|
|
317
311
|
# @private
|
318
|
-
|
319
|
-
|
320
|
-
types: nil, transaction: nil,
|
321
|
-
partition_token: nil, seqno: nil,
|
322
|
-
query_options: nil, request_options: nil,
|
323
|
-
call_options: nil
|
324
|
-
execute_query_options = {
|
325
|
-
transaction: transaction, params: params, types: types,
|
326
|
-
partition_token: partition_token, seqno: seqno,
|
327
|
-
query_options: query_options, request_options: request_options,
|
328
|
-
call_options: call_options
|
329
|
-
}
|
330
|
-
enum = service.execute_streaming_sql session_path, sql,
|
331
|
-
**execute_query_options
|
332
|
-
from_enum(enum, service).tap do |results|
|
312
|
+
def self.from_execute_query_response response, service, session_path, sql, execute_query_options
|
313
|
+
from_enum(response, service).tap do |results|
|
333
314
|
results.instance_variable_set :@session_path, session_path
|
334
|
-
results.instance_variable_set :@sql,
|
335
|
-
results.instance_variable_set :@execute_query_options,
|
336
|
-
execute_query_options
|
315
|
+
results.instance_variable_set :@sql, sql
|
316
|
+
results.instance_variable_set :@execute_query_options, execute_query_options
|
337
317
|
end
|
338
318
|
end
|
339
319
|
|
340
320
|
# @private
|
341
|
-
def self.
|
342
|
-
|
343
|
-
partition_token: nil, request_options: nil,
|
344
|
-
call_options: nil
|
345
|
-
read_options = {
|
346
|
-
keys: keys, index: index, limit: limit,
|
347
|
-
transaction: transaction,
|
348
|
-
partition_token: partition_token,
|
349
|
-
request_options: request_options,
|
350
|
-
call_options: call_options
|
351
|
-
}
|
352
|
-
enum = service.streaming_read_table \
|
353
|
-
session_path, table, columns, **read_options
|
354
|
-
from_enum(enum, service).tap do |results|
|
321
|
+
def self.from_read_response response, service, session_path, table, columns, read_options
|
322
|
+
from_enum(response, service).tap do |results|
|
355
323
|
results.instance_variable_set :@session_path, session_path
|
356
|
-
results.instance_variable_set :@table,
|
357
|
-
results.instance_variable_set :@columns,
|
324
|
+
results.instance_variable_set :@table, table
|
325
|
+
results.instance_variable_set :@columns, columns
|
358
326
|
results.instance_variable_set :@read_options, read_options
|
359
327
|
end
|
360
328
|
end
|
@@ -36,6 +36,9 @@ module Google
|
|
36
36
|
attr_accessor :lib_version
|
37
37
|
attr_accessor :quota_project
|
38
38
|
|
39
|
+
RST_STREAM_INTERNAL_ERROR = "Received RST_STREAM".freeze
|
40
|
+
EOS_INTERNAL_ERROR = "Received unexpected EOS on DATA frame from server".freeze
|
41
|
+
|
39
42
|
##
|
40
43
|
# Creates a new Service instance.
|
41
44
|
def initialize project, credentials, quota_project: nil,
|
@@ -141,7 +144,7 @@ module Google
|
|
141
144
|
display_name: name, config: instance_config_path(config),
|
142
145
|
node_count: nodes, processing_units: processing_units,
|
143
146
|
labels: labels
|
144
|
-
}.
|
147
|
+
}.compact)
|
145
148
|
|
146
149
|
request = {
|
147
150
|
parent: project_path,
|
@@ -326,7 +329,7 @@ module Google
|
|
326
329
|
params: nil, types: nil, resume_token: nil,
|
327
330
|
partition_token: nil, seqno: nil,
|
328
331
|
query_options: nil, request_options: nil,
|
329
|
-
call_options: nil
|
332
|
+
call_options: nil, data_boost_enabled: nil
|
330
333
|
opts = default_options session_name: session_name,
|
331
334
|
call_options: call_options
|
332
335
|
request = {
|
@@ -341,6 +344,7 @@ module Google
|
|
341
344
|
query_options: query_options,
|
342
345
|
request_options: request_options
|
343
346
|
}
|
347
|
+
request[:data_boost_enabled] = data_boost_enabled unless data_boost_enabled.nil?
|
344
348
|
service.execute_streaming_sql request, opts
|
345
349
|
end
|
346
350
|
|
@@ -356,23 +360,14 @@ module Google
|
|
356
360
|
seqno: seqno,
|
357
361
|
request_options: request_options
|
358
362
|
}
|
359
|
-
|
360
|
-
|
361
|
-
if results.status.code.zero?
|
362
|
-
results.result_sets.map { |rs| rs.stats.row_count_exact }
|
363
|
-
else
|
364
|
-
begin
|
365
|
-
raise Google::Cloud::Error.from_error results.status
|
366
|
-
rescue Google::Cloud::Error
|
367
|
-
raise Google::Cloud::Spanner::BatchUpdateError.from_grpc results
|
368
|
-
end
|
369
|
-
end
|
363
|
+
service.execute_batch_dml request, opts
|
370
364
|
end
|
371
365
|
|
372
366
|
def streaming_read_table session_name, table_name, columns, keys: nil,
|
373
367
|
index: nil, transaction: nil, limit: nil,
|
374
368
|
resume_token: nil, partition_token: nil,
|
375
|
-
request_options: nil, call_options: nil
|
369
|
+
request_options: nil, call_options: nil,
|
370
|
+
data_boost_enabled: nil
|
376
371
|
opts = default_options session_name: session_name,
|
377
372
|
call_options: call_options
|
378
373
|
request = {
|
@@ -381,6 +376,7 @@ module Google
|
|
381
376
|
limit: limit, resume_token: resume_token,
|
382
377
|
partition_token: partition_token, request_options: request_options
|
383
378
|
}
|
379
|
+
request[:data_boost_enabled] = data_boost_enabled unless data_boost_enabled.nil?
|
384
380
|
service.streaming_read request, opts
|
385
381
|
end
|
386
382
|
|
@@ -470,7 +466,7 @@ module Google
|
|
470
466
|
read_timestamp: Convert.time_to_timestamp(timestamp),
|
471
467
|
exact_staleness: Convert.number_to_duration(staleness),
|
472
468
|
return_read_timestamp: true
|
473
|
-
}.
|
469
|
+
}.compact
|
474
470
|
)
|
475
471
|
)
|
476
472
|
opts = default_options session_name: session_name,
|
@@ -580,6 +576,21 @@ module Google
|
|
580
576
|
databases.restore_database request, opts
|
581
577
|
end
|
582
578
|
|
579
|
+
##
|
580
|
+
# Checks if a request can be retried. This is based on the error returned.
|
581
|
+
# Retryable errors are:
|
582
|
+
# - Unavailable error
|
583
|
+
# - Internal EOS error
|
584
|
+
# - Internal RST_STREAM error
|
585
|
+
def retryable? err
|
586
|
+
err.instance_of?(Google::Cloud::UnavailableError) ||
|
587
|
+
err.instance_of?(GRPC::Unavailable) ||
|
588
|
+
(err.instance_of?(Google::Cloud::InternalError) && err.message.include?(EOS_INTERNAL_ERROR)) ||
|
589
|
+
(err.instance_of?(GRPC::Internal) && err.details.include?(EOS_INTERNAL_ERROR)) ||
|
590
|
+
(err.instance_of?(Google::Cloud::InternalError) && err.message.include?(RST_STREAM_INTERNAL_ERROR)) ||
|
591
|
+
(err.instance_of?(GRPC::Internal) && err.details.include?(RST_STREAM_INTERNAL_ERROR))
|
592
|
+
end
|
593
|
+
|
583
594
|
def inspect
|
584
595
|
"#{self.class}(#{@project})"
|
585
596
|
end
|
@@ -338,22 +338,25 @@ module Google
|
|
338
338
|
#
|
339
339
|
def execute_query sql, params: nil, types: nil, transaction: nil,
|
340
340
|
partition_token: nil, seqno: nil, query_options: nil,
|
341
|
-
request_options: nil, call_options: nil
|
341
|
+
request_options: nil, call_options: nil, data_boost_enabled: nil
|
342
342
|
ensure_service!
|
343
343
|
if query_options.nil?
|
344
344
|
query_options = @query_options
|
345
345
|
else
|
346
346
|
query_options = @query_options.merge query_options unless @query_options.nil?
|
347
347
|
end
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
348
|
+
|
349
|
+
execute_query_options = {
|
350
|
+
transaction: transaction, params: params, types: types,
|
351
|
+
partition_token: partition_token, seqno: seqno,
|
352
|
+
query_options: query_options, request_options: request_options,
|
353
|
+
call_options: call_options
|
354
|
+
}
|
355
|
+
execute_query_options[:data_boost_enabled] = data_boost_enabled unless data_boost_enabled.nil?
|
356
|
+
|
357
|
+
response = service.execute_streaming_sql path, sql, **execute_query_options
|
358
|
+
|
359
|
+
results = Results.from_execute_query_response response, service, path, sql, execute_query_options
|
357
360
|
@last_updated_at = Time.now
|
358
361
|
results
|
359
362
|
end
|
@@ -496,16 +499,26 @@ module Google
|
|
496
499
|
#
|
497
500
|
def read table, columns, keys: nil, index: nil, limit: nil,
|
498
501
|
transaction: nil, partition_token: nil, request_options: nil,
|
499
|
-
call_options: nil
|
502
|
+
call_options: nil, data_boost_enabled: nil
|
500
503
|
ensure_service!
|
501
504
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
505
|
+
read_options = {
|
506
|
+
keys: keys, index: index, limit: limit,
|
507
|
+
transaction: transaction,
|
508
|
+
partition_token: partition_token,
|
509
|
+
request_options: request_options,
|
510
|
+
call_options: call_options,
|
511
|
+
data_boost_enabled: data_boost_enabled
|
512
|
+
}
|
513
|
+
read_options[:data_boost_enabled] = data_boost_enabled unless data_boost_enabled.nil?
|
514
|
+
|
515
|
+
response = service.streaming_read_table \
|
516
|
+
path, table, columns, **read_options
|
517
|
+
|
518
|
+
results = Results.from_read_response response, service, path, table, columns, read_options
|
519
|
+
|
508
520
|
@last_updated_at = Time.now
|
521
|
+
|
509
522
|
results
|
510
523
|
end
|
511
524
|
|
@@ -1171,6 +1184,14 @@ module Google
|
|
1171
1184
|
Transaction.from_grpc tx_grpc, self
|
1172
1185
|
end
|
1173
1186
|
|
1187
|
+
##
|
1188
|
+
# @private
|
1189
|
+
# Creates a new transaction object without the grpc object
|
1190
|
+
# within it. Use it for inline-begin of a transaction.
|
1191
|
+
def create_empty_transaction
|
1192
|
+
Transaction.from_grpc nil, self
|
1193
|
+
end
|
1194
|
+
|
1174
1195
|
##
|
1175
1196
|
# Reloads the session resource. Useful for determining if the session is
|
1176
1197
|
# still valid on the Spanner API.
|
@@ -17,6 +17,7 @@ require "google/cloud/spanner/errors"
|
|
17
17
|
require "google/cloud/spanner/convert"
|
18
18
|
require "google/cloud/spanner/results"
|
19
19
|
require "google/cloud/spanner/commit"
|
20
|
+
require "google/cloud/spanner/batch_update_results"
|
20
21
|
|
21
22
|
module Google
|
22
23
|
module Cloud
|
@@ -74,6 +75,9 @@ module Google
|
|
74
75
|
# end
|
75
76
|
#
|
76
77
|
class Transaction
|
78
|
+
# @private The `Google::Cloud::Spanner::V1::Transaction` object.
|
79
|
+
attr_reader :grpc
|
80
|
+
|
77
81
|
# @private The Session object.
|
78
82
|
attr_accessor :session
|
79
83
|
|
@@ -83,13 +87,31 @@ module Google
|
|
83
87
|
def initialize
|
84
88
|
@commit = Commit.new
|
85
89
|
@seqno = 0
|
90
|
+
|
91
|
+
# Mutex to enfore thread safety for transaction creation and query executions.
|
92
|
+
#
|
93
|
+
# This mutex protects two things:
|
94
|
+
# (1) the generation of sequence numbers
|
95
|
+
# (2) the creation of transactions.
|
96
|
+
#
|
97
|
+
# Specifically, @seqno is protected so it always reflects the last sequence number
|
98
|
+
# generated and provided to an operation in any thread. Any acquisition of a
|
99
|
+
# sequence number must be synchronized.
|
100
|
+
#
|
101
|
+
# Furthermore, @grpc is protected such that it either is nil if the
|
102
|
+
# transaction has not yet been created, or references the transaction
|
103
|
+
# resource if the transaction has been created. Any operation that could
|
104
|
+
# create a transaction must be synchronized, and any logic that depends on
|
105
|
+
# the state of transaction creation must also be synchronized.
|
106
|
+
@mutex = Mutex.new
|
86
107
|
end
|
87
108
|
|
88
109
|
##
|
89
110
|
# Identifier of the transaction results were run in.
|
90
111
|
# @return [String] The transaction id.
|
91
112
|
def transaction_id
|
92
|
-
return
|
113
|
+
return @grpc.id if existing_transaction?
|
114
|
+
safe_begin_transaction
|
93
115
|
@grpc.id
|
94
116
|
end
|
95
117
|
|
@@ -337,15 +359,18 @@ module Google
|
|
337
359
|
request_options: nil, call_options: nil
|
338
360
|
ensure_session!
|
339
361
|
|
340
|
-
@seqno += 1
|
341
|
-
|
342
362
|
params, types = Convert.to_input_params_and_types params, types
|
343
363
|
request_options = build_request_options request_options
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
364
|
+
|
365
|
+
safe_execute do |seqno|
|
366
|
+
results = session.execute_query sql, params: params, types: types,
|
367
|
+
transaction: tx_selector, seqno: seqno,
|
368
|
+
query_options: query_options,
|
369
|
+
request_options: request_options,
|
370
|
+
call_options: call_options
|
371
|
+
@grpc ||= results.transaction
|
372
|
+
results
|
373
|
+
end
|
349
374
|
end
|
350
375
|
alias execute execute_query
|
351
376
|
alias query execute_query
|
@@ -612,12 +637,24 @@ module Google
|
|
612
637
|
#
|
613
638
|
def batch_update request_options: nil, call_options: nil, &block
|
614
639
|
ensure_session!
|
615
|
-
@seqno += 1
|
616
640
|
|
617
641
|
request_options = build_request_options request_options
|
618
|
-
|
619
|
-
|
620
|
-
|
642
|
+
safe_execute do |seqno|
|
643
|
+
batch_update_results = nil
|
644
|
+
begin
|
645
|
+
response = session.batch_update tx_selector, seqno,
|
646
|
+
request_options: request_options,
|
647
|
+
call_options: call_options, &block
|
648
|
+
batch_update_results = BatchUpdateResults.new response
|
649
|
+
row_counts = batch_update_results.row_counts
|
650
|
+
@grpc ||= batch_update_results.transaction
|
651
|
+
return row_counts
|
652
|
+
rescue Google::Cloud::Spanner::BatchUpdateError
|
653
|
+
@grpc ||= batch_update_results.transaction
|
654
|
+
# Re-raise after extracting transaction
|
655
|
+
raise
|
656
|
+
end
|
657
|
+
end
|
621
658
|
end
|
622
659
|
|
623
660
|
##
|
@@ -686,10 +723,15 @@ module Google
|
|
686
723
|
columns = Array(columns).map(&:to_s)
|
687
724
|
keys = Convert.to_key_set keys
|
688
725
|
request_options = build_request_options request_options
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
726
|
+
|
727
|
+
safe_execute do
|
728
|
+
results = session.read table, columns, keys: keys, index: index, limit: limit,
|
729
|
+
transaction: tx_selector,
|
730
|
+
request_options: request_options,
|
731
|
+
call_options: call_options
|
732
|
+
@grpc ||= results.transaction
|
733
|
+
results
|
734
|
+
end
|
693
735
|
end
|
694
736
|
|
695
737
|
##
|
@@ -1111,12 +1153,69 @@ module Google
|
|
1111
1153
|
end
|
1112
1154
|
end
|
1113
1155
|
|
1156
|
+
##
|
1157
|
+
# @private Checks if a transaction is already created.
|
1158
|
+
def existing_transaction?
|
1159
|
+
!no_existing_transaction?
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
##
|
1163
|
+
# @private Checks if transaction is not already created.
|
1164
|
+
def no_existing_transaction?
|
1165
|
+
@grpc.nil?
|
1166
|
+
end
|
1167
|
+
|
1114
1168
|
protected
|
1115
1169
|
|
1116
|
-
|
1170
|
+
##
|
1171
|
+
# @private Facilitates a thread-safe execution of an rpc
|
1172
|
+
# for inline-begin of a transaction. This method is optimised to
|
1173
|
+
# use mutexes only when necessary, while still acheiving thread-safety.
|
1174
|
+
#
|
1175
|
+
# Note: Do not use @seqno directly while using this method. Instead, use
|
1176
|
+
# the seqno variable passed to the block.
|
1177
|
+
def safe_execute
|
1178
|
+
loop do
|
1179
|
+
if existing_transaction?
|
1180
|
+
# Create a local copy of @seqno to avoid concurrent
|
1181
|
+
# operations overriding the incremented value.
|
1182
|
+
seqno = safe_next_seqno
|
1183
|
+
# If a transaction already exists, execute rpc without mutex
|
1184
|
+
return yield seqno
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
@mutex.synchronize do
|
1188
|
+
next if existing_transaction?
|
1189
|
+
@seqno += 1
|
1190
|
+
return yield @seqno
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
##
|
1196
|
+
# Create a new transaction in a thread-safe manner.
|
1197
|
+
def safe_begin_transaction
|
1198
|
+
@mutex.synchronize do
|
1199
|
+
return if existing_transaction?
|
1200
|
+
ensure_session!
|
1201
|
+
@grpc = service.begin_transaction session.path
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
##
|
1206
|
+
# @private The TransactionSelector to be used for queries. This method must
|
1207
|
+
# be called from within a synchronized block, since the value returned
|
1208
|
+
# depends on the state of @grpc field.
|
1209
|
+
#
|
1210
|
+
# This method is expected to be called from within `safe_execute()` method's block,
|
1211
|
+
# since it provides synchronization and gurantees thread safety.
|
1117
1212
|
def tx_selector
|
1118
|
-
return
|
1119
|
-
V1::TransactionSelector.new
|
1213
|
+
return V1::TransactionSelector.new id: transaction_id if existing_transaction?
|
1214
|
+
V1::TransactionSelector.new(
|
1215
|
+
begin: V1::TransactionOptions.new(
|
1216
|
+
read_write: V1::TransactionOptions::ReadWrite.new
|
1217
|
+
)
|
1218
|
+
)
|
1120
1219
|
end
|
1121
1220
|
|
1122
1221
|
##
|
@@ -1133,6 +1232,15 @@ module Google
|
|
1133
1232
|
options
|
1134
1233
|
end
|
1135
1234
|
|
1235
|
+
##
|
1236
|
+
# @private Generates the next seqno in a thread-safe manner.
|
1237
|
+
def safe_next_seqno
|
1238
|
+
@mutex.synchronize do
|
1239
|
+
@seqno += 1
|
1240
|
+
return @seqno
|
1241
|
+
end
|
1242
|
+
end
|
1243
|
+
|
1136
1244
|
##
|
1137
1245
|
# @private Raise an error unless an active connection to the service is
|
1138
1246
|
# available.
|
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.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Moore
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-09-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: google-cloud-core
|
@@ -295,6 +295,7 @@ files:
|
|
295
295
|
- lib/google/cloud/spanner/batch_client.rb
|
296
296
|
- lib/google/cloud/spanner/batch_snapshot.rb
|
297
297
|
- lib/google/cloud/spanner/batch_update.rb
|
298
|
+
- lib/google/cloud/spanner/batch_update_results.rb
|
298
299
|
- lib/google/cloud/spanner/client.rb
|
299
300
|
- lib/google/cloud/spanner/column_value.rb
|
300
301
|
- lib/google/cloud/spanner/commit.rb
|
@@ -347,7 +348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
347
348
|
- !ruby/object:Gem::Version
|
348
349
|
version: '0'
|
349
350
|
requirements: []
|
350
|
-
rubygems_version: 3.
|
351
|
+
rubygems_version: 3.4.19
|
351
352
|
signing_key:
|
352
353
|
specification_version: 4
|
353
354
|
summary: API Client library for Google Cloud Spanner API
|