google-cloud-spanner 0.21.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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/lib/google-cloud-spanner.rb +106 -0
  3. data/lib/google/cloud/spanner.rb +382 -0
  4. data/lib/google/cloud/spanner/admin/database/v1.rb +17 -0
  5. data/lib/google/cloud/spanner/admin/database/v1/database_admin_client.rb +703 -0
  6. data/lib/google/cloud/spanner/admin/database/v1/database_admin_client_config.json +73 -0
  7. data/lib/google/cloud/spanner/admin/database/v1/doc/google/iam/v1/policy.rb +139 -0
  8. data/lib/google/cloud/spanner/admin/database/v1/doc/google/protobuf/any.rb +114 -0
  9. data/lib/google/cloud/spanner/admin/database/v1/doc/google/rpc/status.rb +83 -0
  10. data/lib/google/cloud/spanner/admin/database/v1/doc/google/spanner/admin/database/v1/spanner_database_admin.rb +188 -0
  11. data/lib/google/cloud/spanner/admin/instance/v1.rb +17 -0
  12. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/iam/v1/policy.rb +139 -0
  13. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/any.rb +114 -0
  14. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/field_mask.rb +223 -0
  15. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/rpc/status.rb +83 -0
  16. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/spanner/admin/instance/v1/spanner_instance_admin.rb +268 -0
  17. data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client.rb +868 -0
  18. data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client_config.json +78 -0
  19. data/lib/google/cloud/spanner/client.rb +1034 -0
  20. data/lib/google/cloud/spanner/commit.rb +351 -0
  21. data/lib/google/cloud/spanner/convert.rb +311 -0
  22. data/lib/google/cloud/spanner/credentials.rb +32 -0
  23. data/lib/google/cloud/spanner/data.rb +199 -0
  24. data/lib/google/cloud/spanner/database.rb +377 -0
  25. data/lib/google/cloud/spanner/database/job.rb +179 -0
  26. data/lib/google/cloud/spanner/database/list.rb +171 -0
  27. data/lib/google/cloud/spanner/errors.rb +73 -0
  28. data/lib/google/cloud/spanner/fields.rb +252 -0
  29. data/lib/google/cloud/spanner/instance.rb +472 -0
  30. data/lib/google/cloud/spanner/instance/config.rb +99 -0
  31. data/lib/google/cloud/spanner/instance/config/list.rb +171 -0
  32. data/lib/google/cloud/spanner/instance/job.rb +197 -0
  33. data/lib/google/cloud/spanner/instance/list.rb +167 -0
  34. data/lib/google/cloud/spanner/policy.rb +201 -0
  35. data/lib/google/cloud/spanner/pool.rb +279 -0
  36. data/lib/google/cloud/spanner/project.rb +480 -0
  37. data/lib/google/cloud/spanner/range.rb +99 -0
  38. data/lib/google/cloud/spanner/results.rb +280 -0
  39. data/lib/google/cloud/spanner/service.rb +458 -0
  40. data/lib/google/cloud/spanner/session.rb +565 -0
  41. data/lib/google/cloud/spanner/snapshot.rb +260 -0
  42. data/lib/google/cloud/spanner/transaction.rb +533 -0
  43. data/lib/google/cloud/spanner/v1.rb +17 -0
  44. data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +77 -0
  45. data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +73 -0
  46. data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +81 -0
  47. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +148 -0
  48. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +80 -0
  49. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +120 -0
  50. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +175 -0
  51. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +206 -0
  52. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +351 -0
  53. data/lib/google/cloud/spanner/v1/spanner_client.rb +850 -0
  54. data/lib/google/cloud/spanner/v1/spanner_client_config.json +78 -0
  55. data/lib/google/cloud/spanner/version.rb +22 -0
  56. data/lib/google/spanner/admin/database/v1/spanner_database_admin_pb.rb +85 -0
  57. data/lib/google/spanner/admin/database/v1/spanner_database_admin_services_pb.rb +95 -0
  58. data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_pb.rb +106 -0
  59. data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_services_pb.rb +180 -0
  60. data/lib/google/spanner/v1/keys_pb.rb +33 -0
  61. data/lib/google/spanner/v1/mutation_pb.rb +38 -0
  62. data/lib/google/spanner/v1/query_plan_pb.rb +47 -0
  63. data/lib/google/spanner/v1/result_set_pb.rb +43 -0
  64. data/lib/google/spanner/v1/spanner_pb.rb +90 -0
  65. data/lib/google/spanner/v1/spanner_services_pb.rb +131 -0
  66. data/lib/google/spanner/v1/transaction_pb.rb +51 -0
  67. data/lib/google/spanner/v1/type_pb.rb +43 -0
  68. metadata +309 -0
@@ -0,0 +1,78 @@
1
+ {
2
+ "interfaces": {
3
+ "google.spanner.admin.instance.v1.InstanceAdmin": {
4
+ "retry_codes": {
5
+ "idempotent": [
6
+ "DEADLINE_EXCEEDED",
7
+ "UNAVAILABLE"
8
+ ],
9
+ "non_idempotent": [
10
+ "UNAVAILABLE"
11
+ ]
12
+ },
13
+ "retry_params": {
14
+ "default": {
15
+ "initial_retry_delay_millis": 1000,
16
+ "retry_delay_multiplier": 1.3,
17
+ "max_retry_delay_millis": 32000,
18
+ "initial_rpc_timeout_millis": 60000,
19
+ "rpc_timeout_multiplier": 1.0,
20
+ "max_rpc_timeout_millis": 60000,
21
+ "total_timeout_millis": 600000
22
+ }
23
+ },
24
+ "methods": {
25
+ "ListInstanceConfigs": {
26
+ "timeout_millis": 30000,
27
+ "retry_codes_name": "idempotent",
28
+ "retry_params_name": "default"
29
+ },
30
+ "GetInstanceConfig": {
31
+ "timeout_millis": 30000,
32
+ "retry_codes_name": "idempotent",
33
+ "retry_params_name": "default"
34
+ },
35
+ "ListInstances": {
36
+ "timeout_millis": 30000,
37
+ "retry_codes_name": "idempotent",
38
+ "retry_params_name": "default"
39
+ },
40
+ "GetInstance": {
41
+ "timeout_millis": 30000,
42
+ "retry_codes_name": "idempotent",
43
+ "retry_params_name": "default"
44
+ },
45
+ "CreateInstance": {
46
+ "timeout_millis": 30000,
47
+ "retry_codes_name": "non_idempotent",
48
+ "retry_params_name": "default"
49
+ },
50
+ "UpdateInstance": {
51
+ "timeout_millis": 30000,
52
+ "retry_codes_name": "non_idempotent",
53
+ "retry_params_name": "default"
54
+ },
55
+ "DeleteInstance": {
56
+ "timeout_millis": 30000,
57
+ "retry_codes_name": "idempotent",
58
+ "retry_params_name": "default"
59
+ },
60
+ "SetIamPolicy": {
61
+ "timeout_millis": 30000,
62
+ "retry_codes_name": "non_idempotent",
63
+ "retry_params_name": "default"
64
+ },
65
+ "GetIamPolicy": {
66
+ "timeout_millis": 30000,
67
+ "retry_codes_name": "idempotent",
68
+ "retry_params_name": "default"
69
+ },
70
+ "TestIamPermissions": {
71
+ "timeout_millis": 30000,
72
+ "retry_codes_name": "non_idempotent",
73
+ "retry_params_name": "default"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,1034 @@
1
+ # Copyright 2017 Google Inc. All rights reserved.
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
+ # http://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
+ require "google/cloud/spanner/errors"
17
+ require "google/cloud/spanner/project"
18
+ require "google/cloud/spanner/data"
19
+ require "google/cloud/spanner/pool"
20
+ require "google/cloud/spanner/session"
21
+ require "google/cloud/spanner/transaction"
22
+ require "google/cloud/spanner/snapshot"
23
+ require "google/cloud/spanner/range"
24
+ require "google/cloud/spanner/convert"
25
+
26
+ module Google
27
+ module Cloud
28
+ module Spanner
29
+ ##
30
+ # # Client
31
+ #
32
+ # A client is used to read and/or modify data in a Cloud Spanner database.
33
+ #
34
+ # See {Google::Cloud::Spanner::Project#client}.
35
+ #
36
+ # @example
37
+ # require "google/cloud"
38
+ #
39
+ # spanner = Google::Cloud::Spanner.new
40
+ #
41
+ # db = spanner.client "my-instance", "my-database"
42
+ #
43
+ # db.transaction do |tx|
44
+ # results = tx.execute "SELECT * FROM users"
45
+ #
46
+ # results.rows.each do |row|
47
+ # puts "User #{row[:id]} is #{row[:name]}"
48
+ # end
49
+ # end
50
+ #
51
+ class Client
52
+ ##
53
+ # @private Creates a new Spanner Client instance.
54
+ def initialize project, instance_id, database_id, min: 10, max: 100,
55
+ keepalive: 1800, write_ratio: 0.3, fail: true
56
+ @project = project
57
+ @instance_id = instance_id
58
+ @database_id = database_id
59
+ @pool = Pool.new self, min: min, max: max, keepalive: keepalive,
60
+ write_ratio: write_ratio, fail: fail
61
+ end
62
+
63
+ # The unique identifier for the project.
64
+ # @return [String]
65
+ def project_id
66
+ @project.service.project
67
+ end
68
+
69
+ # The unique identifier for the instance.
70
+ # @return [String]
71
+ def instance_id
72
+ @instance_id
73
+ end
74
+
75
+ # The unique identifier for the database.
76
+ # @return [String]
77
+ def database_id
78
+ @database_id
79
+ end
80
+
81
+ # The Spanner project connected to.
82
+ # @return [Project]
83
+ def project
84
+ @project
85
+ end
86
+
87
+ # The Spanner instance connected to.
88
+ # @return [Instance]
89
+ def instance
90
+ @project.instance instance_id
91
+ end
92
+
93
+ # The Spanner database connected to.
94
+ # @return [Database]
95
+ def database
96
+ @project.database instance_id, database_id
97
+ end
98
+
99
+ ##
100
+ # Executes a SQL query.
101
+ #
102
+ # Arguments can be passed using `params`, Ruby types are mapped to
103
+ # Spanner types as follows:
104
+ #
105
+ # | Spanner | Ruby | Notes |
106
+ # |-------------|----------------|---|
107
+ # | `BOOL` | `true`/`false` | |
108
+ # | `INT64` | `Integer` | |
109
+ # | `FLOAT64` | `Float` | |
110
+ # | `STRING` | `String` | |
111
+ # | `DATE` | `Date` | |
112
+ # | `TIMESTAMP` | `Time`, `DateTime` | |
113
+ # | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
114
+ # | `ARRAY` | `Array` | Nested arrays are not supported. |
115
+ #
116
+ # See [Data
117
+ # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
118
+ #
119
+ # @param [String] sql The SQL query string. See [Query
120
+ # syntax](https://cloud.google.com/spanner/docs/query-syntax).
121
+ #
122
+ # The SQL query string can contain parameter placeholders. A parameter
123
+ # placeholder consists of "@" followed by the parameter name.
124
+ # Parameter names consist of any combination of letters, numbers, and
125
+ # underscores.
126
+ # @param [Hash] params SQL parameters for the query string. The
127
+ # parameter placeholders, minus the "@", are the the hash keys, and
128
+ # the literal values are the hash values. If the query string contains
129
+ # something like "WHERE id > @msg_id", then the params must contain
130
+ # something like `:msg_id => 1`.
131
+ # @param [Hash] types Types of the SQL parameters in `params`. It is not
132
+ # always possible for Cloud Spanner to infer the right SQL type from a
133
+ # value in `params`. In these cases, the `types` hash can be used to
134
+ # specify the exact SQL type for some or all of the SQL query
135
+ # parameters.
136
+ #
137
+ # The keys of the hash should be query string parameter placeholders,
138
+ # minus the "@". The values of the hash should be Cloud Spanner type
139
+ # codes from the following list:
140
+ #
141
+ # * `:BOOL`
142
+ # * `:BYTES`
143
+ # * `:DATE`
144
+ # * `:FLOAT64`
145
+ # * `:INT64`
146
+ # * `:STRING`
147
+ # * `:TIMESTAMP`
148
+ #
149
+ # Arrays are specified by providing the type code in an array. For
150
+ # example, an array of integers are specified as `[:INT64]`.
151
+ #
152
+ # Structs are not yet supported in query parameters.
153
+ #
154
+ # Types are optional.
155
+ # @param [Hash] single_use Perform the read with a single-use snapshot
156
+ # (read-only transaction). (See
157
+ # [TransactionOptions](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#transactionoptions).)
158
+ # The snapshot can be created by providing exactly one of the
159
+ # following options in the hash:
160
+ #
161
+ # * **Strong**
162
+ # * `:strong` (true, false) Read at a timestamp where all previously
163
+ # committed transactions are visible.
164
+ # * **Exact**
165
+ # * `:timestamp`/`:read_timestamp` (Time, DateTime) Executes all
166
+ # reads at the given timestamp. Unlike other modes, reads at a
167
+ # specific timestamp are repeatable; the same read at the same
168
+ # timestamp always returns the same data. If the timestamp is in
169
+ # the future, the read will block until the specified timestamp,
170
+ # modulo the read's deadline.
171
+ #
172
+ # Useful for large scale consistent reads such as mapreduces, or
173
+ # for coordinating many reads against a consistent snapshot of the
174
+ # data.
175
+ # * `:staleness`/`:exact_staleness` (Numeric) Executes all reads at
176
+ # a timestamp that is exactly the number of seconds provided old.
177
+ # The timestamp is chosen soon after the read is started.
178
+ #
179
+ # Guarantees that all writes that have committed more than the
180
+ # specified number of seconds ago are visible. Because Cloud
181
+ # Spanner chooses the exact timestamp, this mode works even if the
182
+ # client's local clock is substantially skewed from Cloud Spanner
183
+ # commit timestamps.
184
+ #
185
+ # Useful for reading at nearby replicas without the distributed
186
+ # timestamp negotiation overhead of single-use
187
+ # `bounded_staleness`.
188
+ # * **Bounded**
189
+ # * `:bounded_timestamp`/`:min_read_timestamp` (Time, DateTime)
190
+ # Executes all reads at a timestamp greater than the value
191
+ # provided.
192
+ #
193
+ # This is useful for requesting fresher data than some previous
194
+ # read, or data that is fresh enough to observe the effects of
195
+ # some previously committed transaction whose timestamp is known.
196
+ # * `:bounded_staleness`/`:max_staleness` (Numeric) Read data at a
197
+ # timestamp greater than or equal to the number of seconds
198
+ # provided. Guarantees that all writes that have committed more
199
+ # than the specified number of seconds ago are visible. Because
200
+ # Cloud Spanner chooses the exact timestamp, this mode works even
201
+ # if the client's local clock is substantially skewed from Cloud
202
+ # Spanner commit timestamps.
203
+ #
204
+ # Useful for reading the freshest data available at a nearby
205
+ # replica, while bounding the possible staleness if the local
206
+ # replica has fallen behind.
207
+ #
208
+ # @return [Google::Cloud::Spanner::Results] The results of the query
209
+ # execution.
210
+ #
211
+ # @example
212
+ # require "google/cloud/spanner"
213
+ #
214
+ # spanner = Google::Cloud::Spanner.new
215
+ #
216
+ # db = spanner.client "my-instance", "my-database"
217
+ #
218
+ # results = db.execute "SELECT * FROM users"
219
+ #
220
+ # results.rows.each do |row|
221
+ # puts "User #{row[:id]} is #{row[:name]}"
222
+ # end
223
+ #
224
+ # @example Query using query parameters:
225
+ # require "google/cloud/spanner"
226
+ #
227
+ # spanner = Google::Cloud::Spanner.new
228
+ #
229
+ # db = spanner.client "my-instance", "my-database"
230
+ #
231
+ # results = db.execute "SELECT * FROM users WHERE active = @active",
232
+ # params: { active: true }
233
+ #
234
+ # results.rows.each do |row|
235
+ # puts "User #{row[:id]} is #{row[:name]}"
236
+ # end
237
+ #
238
+ def execute sql, params: nil, types: nil, single_use: nil
239
+ validate_single_use_args! single_use
240
+ ensure_service!
241
+
242
+ single_use_tx = single_use_transaction single_use
243
+ results = nil
244
+ @pool.with_session do |session|
245
+ results = session.execute \
246
+ sql, params: params, types: types, transaction: single_use_tx
247
+ end
248
+ results
249
+ end
250
+ alias_method :query, :execute
251
+
252
+ ##
253
+ # Read rows from a database table, as a simple alternative to
254
+ # {#execute}.
255
+ #
256
+ # @param [String] table The name of the table in the database to be
257
+ # read.
258
+ # @param [Array<String, Symbol>] columns The columns of table to be
259
+ # returned for each row matching this request.
260
+ # @param [Object, Array<Object>] keys A single, or list of keys or key
261
+ # ranges to match returned data to. Values should have exactly as many
262
+ # elements as there are columns in the primary key.
263
+ # @param [String] index The name of an index to use instead of the
264
+ # table's primary key when interpreting `id` and sorting result rows.
265
+ # Optional.
266
+ # @param [Integer] limit If greater than zero, no more than this number
267
+ # of rows will be returned. The default is no limit.
268
+ # @param [Hash] single_use Perform the read with a single-use snapshot
269
+ # (read-only transaction). (See
270
+ # [TransactionOptions](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#transactionoptions).)
271
+ # The snapshot can be created by providing exactly one of the
272
+ # following options in the hash:
273
+ #
274
+ # * **Strong**
275
+ # * `:strong` (true, false) Read at a timestamp where all previously
276
+ # committed transactions are visible.
277
+ # * **Exact**
278
+ # * `:timestamp`/`:read_timestamp` (Time, DateTime) Executes all
279
+ # reads at the given timestamp. Unlike other modes, reads at a
280
+ # specific timestamp are repeatable; the same read at the same
281
+ # timestamp always returns the same data. If the timestamp is in
282
+ # the future, the read will block until the specified timestamp,
283
+ # modulo the read's deadline.
284
+ #
285
+ # Useful for large scale consistent reads such as mapreduces, or
286
+ # for coordinating many reads against a consistent snapshot of the
287
+ # data.
288
+ # * `:staleness`/`:exact_staleness` (Numeric) Executes all reads at
289
+ # a timestamp that is exactly the number of seconds provided old.
290
+ # The timestamp is chosen soon after the read is started.
291
+ #
292
+ # Guarantees that all writes that have committed more than the
293
+ # specified number of seconds ago are visible. Because Cloud
294
+ # Spanner chooses the exact timestamp, this mode works even if the
295
+ # client's local clock is substantially skewed from Cloud Spanner
296
+ # commit timestamps.
297
+ #
298
+ # Useful for reading at nearby replicas without the distributed
299
+ # timestamp negotiation overhead of single-use
300
+ # `bounded_staleness`.
301
+ # * **Bounded**
302
+ # * `:bounded_timestamp`/`:min_read_timestamp` (Time, DateTime)
303
+ # Executes all reads at a timestamp greater than the value
304
+ # provided.
305
+ #
306
+ # This is useful for requesting fresher data than some previous
307
+ # read, or data that is fresh enough to observe the effects of
308
+ # some previously committed transaction whose timestamp is known.
309
+ # * `:bounded_staleness`/`:max_staleness` (Numeric) Read data at a
310
+ # timestamp greater than or equal to the number of seconds
311
+ # provided. Guarantees that all writes that have committed more
312
+ # than the specified number of seconds ago are visible. Because
313
+ # Cloud Spanner chooses the exact timestamp, this mode works even
314
+ # if the client's local clock is substantially skewed from Cloud
315
+ # Spanner commit timestamps.
316
+ #
317
+ # Useful for reading the freshest data available at a nearby
318
+ # replica, while bounding the possible staleness if the local
319
+ # replica has fallen behind.
320
+ #
321
+ # @return [Google::Cloud::Spanner::Results] The results of the read.
322
+ #
323
+ # @example
324
+ # require "google/cloud/spanner"
325
+ #
326
+ # spanner = Google::Cloud::Spanner.new
327
+ #
328
+ # db = spanner.client "my-instance", "my-database"
329
+ #
330
+ # results = db.read "users", [:id, :name]
331
+ #
332
+ # results.rows.each do |row|
333
+ # puts "User #{row[:id]} is #{row[:name]}"
334
+ # end
335
+ #
336
+ # @example Use the `keys` option to pass keys and/or key ranges to read.
337
+ # require "google/cloud/spanner"
338
+ #
339
+ # spanner = Google::Cloud::Spanner.new
340
+ #
341
+ # db = spanner.client "my-instance", "my-database"
342
+ #
343
+ # results = db.read "users", [:id, :name], keys: 1..5
344
+ #
345
+ # results.rows.each do |row|
346
+ # puts "User #{row[:id]} is #{row[:name]}"
347
+ # end
348
+ #
349
+ def read table, columns, keys: nil, index: nil, limit: nil,
350
+ single_use: nil
351
+ validate_single_use_args! single_use
352
+ ensure_service!
353
+
354
+ single_use_tx = single_use_transaction single_use
355
+ results = nil
356
+ @pool.with_session do |session|
357
+ results = session.read \
358
+ table, columns, keys: keys, index: index, limit: limit,
359
+ transaction: single_use_tx
360
+ end
361
+ results
362
+ end
363
+
364
+ ##
365
+ # Inserts or updates rows in a table. If any of the rows already exist,
366
+ # then its column values are overwritten with the ones provided. Any
367
+ # column values not explicitly written are preserved.
368
+ #
369
+ # Changes are made immediately upon calling this method using a
370
+ # single-use transaction. To make multiple changes in the same
371
+ # single-use transaction use {#commit}. To make changes in a transaction
372
+ # that supports reads and automatic retry protection use {#transaction}.
373
+ #
374
+ # **Note:** This method does not feature replay protection present in
375
+ # {Transaction#upsert} (See {#transaction}). This method makes a single
376
+ # RPC, whereas {Transaction#upsert} requires two RPCs (one of which may
377
+ # be performed in advance), and so this method may be appropriate for
378
+ # latency sensitive and/or high throughput blind upserts.
379
+ #
380
+ # @param [String] table The name of the table in the database to be
381
+ # modified.
382
+ # @param [Array<Hash>] rows One or more hash objects with the hash keys
383
+ # matching the table's columns, and the hash values matching the
384
+ # table's values.
385
+ #
386
+ # Ruby types are mapped to Spanner types as follows:
387
+ #
388
+ # | Spanner | Ruby | Notes |
389
+ # |-------------|----------------|---|
390
+ # | `BOOL` | `true`/`false` | |
391
+ # | `INT64` | `Integer` | |
392
+ # | `FLOAT64` | `Float` | |
393
+ # | `STRING` | `String` | |
394
+ # | `DATE` | `Date` | |
395
+ # | `TIMESTAMP` | `Time`, `DateTime` | |
396
+ # | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
397
+ # | `ARRAY` | `Array` | Nested arrays are not supported. |
398
+ #
399
+ # See [Data
400
+ # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
401
+ #
402
+ # @return [Time] The timestamp at which the operation committed.
403
+ #
404
+ # @example
405
+ # require "google/cloud/spanner"
406
+ #
407
+ # spanner = Google::Cloud::Spanner.new
408
+ #
409
+ # db = spanner.client "my-instance", "my-database"
410
+ #
411
+ # db.upsert "users", [{ id: 1, name: "Charlie", active: false },
412
+ # { id: 2, name: "Harvey", active: true }]
413
+ #
414
+ def upsert table, *rows
415
+ @pool.with_session do |session|
416
+ session.upsert table, rows
417
+ end
418
+ end
419
+ alias_method :save, :upsert
420
+
421
+ ##
422
+ # Inserts new rows in a table. If any of the rows already exist, the
423
+ # write or request fails with {Google::Cloud::AlreadyExistsError}.
424
+ #
425
+ # Changes are made immediately upon calling this method using a
426
+ # single-use transaction. To make multiple changes in the same
427
+ # single-use transaction use {#commit}. To make changes in a transaction
428
+ # that supports reads and automatic retry protection use {#transaction}.
429
+ #
430
+ # **Note:** This method does not feature replay protection present in
431
+ # {Transaction#insert} (See {#transaction}). This method makes a single
432
+ # RPC, whereas {Transaction#insert} requires two RPCs (one of which may
433
+ # be performed in advance), and so this method may be appropriate for
434
+ # latency sensitive and/or high throughput blind inserts.
435
+ #
436
+ # @param [String] table The name of the table in the database to be
437
+ # modified.
438
+ # @param [Array<Hash>] rows One or more hash objects with the hash keys
439
+ # matching the table's columns, and the hash values matching the
440
+ # table's values.
441
+ #
442
+ # Ruby types are mapped to Spanner types as follows:
443
+ #
444
+ # | Spanner | Ruby | Notes |
445
+ # |-------------|----------------|---|
446
+ # | `BOOL` | `true`/`false` | |
447
+ # | `INT64` | `Integer` | |
448
+ # | `FLOAT64` | `Float` | |
449
+ # | `STRING` | `String` | |
450
+ # | `DATE` | `Date` | |
451
+ # | `TIMESTAMP` | `Time`, `DateTime` | |
452
+ # | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
453
+ # | `ARRAY` | `Array` | Nested arrays are not supported. |
454
+ #
455
+ # See [Data
456
+ # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
457
+ #
458
+ # @return [Time] The timestamp at which the operation committed.
459
+ #
460
+ # @example
461
+ # require "google/cloud/spanner"
462
+ #
463
+ # spanner = Google::Cloud::Spanner.new
464
+ #
465
+ # db = spanner.client "my-instance", "my-database"
466
+ #
467
+ # db.insert "users", [{ id: 1, name: "Charlie", active: false },
468
+ # { id: 2, name: "Harvey", active: true }]
469
+ #
470
+ def insert table, *rows
471
+ @pool.with_session do |session|
472
+ session.insert table, rows
473
+ end
474
+ end
475
+
476
+ ##
477
+ # Updates existing rows in a table. If any of the rows does not already
478
+ # exist, the request fails with {Google::Cloud::NotFoundError}.
479
+ #
480
+ # Changes are made immediately upon calling this method using a
481
+ # single-use transaction. To make multiple changes in the same
482
+ # single-use transaction use {#commit}. To make changes in a transaction
483
+ # that supports reads and automatic retry protection use {#transaction}.
484
+ #
485
+ # **Note:** This method does not feature replay protection present in
486
+ # {Transaction#update} (See {#transaction}). This method makes a single
487
+ # RPC, whereas {Transaction#update} requires two RPCs (one of which may
488
+ # be performed in advance), and so this method may be appropriate for
489
+ # latency sensitive and/or high throughput blind updates.
490
+ #
491
+ # @param [String] table The name of the table in the database to be
492
+ # modified.
493
+ # @param [Array<Hash>] rows One or more hash objects with the hash keys
494
+ # matching the table's columns, and the hash values matching the
495
+ # table's values.
496
+ #
497
+ # Ruby types are mapped to Spanner types as follows:
498
+ #
499
+ # | Spanner | Ruby | Notes |
500
+ # |-------------|----------------|---|
501
+ # | `BOOL` | `true`/`false` | |
502
+ # | `INT64` | `Integer` | |
503
+ # | `FLOAT64` | `Float` | |
504
+ # | `STRING` | `String` | |
505
+ # | `DATE` | `Date` | |
506
+ # | `TIMESTAMP` | `Time`, `DateTime` | |
507
+ # | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
508
+ # | `ARRAY` | `Array` | Nested arrays are not supported. |
509
+ #
510
+ # See [Data
511
+ # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
512
+ #
513
+ # @return [Time] The timestamp at which the operation committed.
514
+ #
515
+ # @example
516
+ # require "google/cloud/spanner"
517
+ #
518
+ # spanner = Google::Cloud::Spanner.new
519
+ #
520
+ # db = spanner.client "my-instance", "my-database"
521
+ #
522
+ # db.update "users", [{ id: 1, name: "Charlie", active: false },
523
+ # { id: 2, name: "Harvey", active: true }]
524
+ #
525
+ def update table, *rows
526
+ @pool.with_session do |session|
527
+ session.update table, rows
528
+ end
529
+ end
530
+
531
+ ##
532
+ # Inserts or replaces rows in a table. If any of the rows already exist,
533
+ # it is deleted, and the column values provided are inserted instead.
534
+ # Unlike #upsert, this means any values not explicitly written become
535
+ # `NULL`.
536
+ #
537
+ # Changes are made immediately upon calling this method using a
538
+ # single-use transaction. To make multiple changes in the same
539
+ # single-use transaction use {#commit}. To make changes in a transaction
540
+ # that supports reads and automatic retry protection use {#transaction}.
541
+ #
542
+ # **Note:** This method does not feature replay protection present in
543
+ # {Transaction#replace} (See {#transaction}). This method makes a single
544
+ # RPC, whereas {Transaction#replace} requires two RPCs (one of which may
545
+ # be performed in advance), and so this method may be appropriate for
546
+ # latency sensitive and/or high throughput blind replaces.
547
+ #
548
+ # @param [String] table The name of the table in the database to be
549
+ # modified.
550
+ # @param [Array<Hash>] rows One or more hash objects with the hash keys
551
+ # matching the table's columns, and the hash values matching the
552
+ # table's values.
553
+ #
554
+ # Ruby types are mapped to Spanner types as follows:
555
+ #
556
+ # | Spanner | Ruby | Notes |
557
+ # |-------------|----------------|---|
558
+ # | `BOOL` | `true`/`false` | |
559
+ # | `INT64` | `Integer` | |
560
+ # | `FLOAT64` | `Float` | |
561
+ # | `STRING` | `String` | |
562
+ # | `DATE` | `Date` | |
563
+ # | `TIMESTAMP` | `Time`, `DateTime` | |
564
+ # | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
565
+ # | `ARRAY` | `Array` | Nested arrays are not supported. |
566
+ #
567
+ # See [Data
568
+ # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
569
+ #
570
+ # @return [Time] The timestamp at which the operation committed.
571
+ #
572
+ # @example
573
+ # require "google/cloud/spanner"
574
+ #
575
+ # spanner = Google::Cloud::Spanner.new
576
+ #
577
+ # db = spanner.client "my-instance", "my-database"
578
+ #
579
+ # db.replace "users", [{ id: 1, name: "Charlie", active: false },
580
+ # { id: 2, name: "Harvey", active: true }]
581
+ #
582
+ def replace table, *rows
583
+ @pool.with_session do |session|
584
+ session.replace table, rows
585
+ end
586
+ end
587
+
588
+ ##
589
+ # Deletes rows from a table. Succeeds whether or not the specified rows
590
+ # were present.
591
+ #
592
+ # Changes are made immediately upon calling this method using a
593
+ # single-use transaction. To make multiple changes in the same
594
+ # single-use transaction use {#commit}. To make changes in a transaction
595
+ # that supports reads and automatic retry protection use {#transaction}.
596
+ #
597
+ # **Note:** This method does not feature replay protection present in
598
+ # {Transaction#delete} (See {#transaction}). This method makes a single
599
+ # RPC, whereas {Transaction#delete} requires two RPCs (one of which may
600
+ # be performed in advance), and so this method may be appropriate for
601
+ # latency sensitive and/or high throughput blind deletions.
602
+ #
603
+ # @param [String] table The name of the table in the database to be
604
+ # modified.
605
+ # @param [Object, Array<Object>] keys A single, or list of keys or key
606
+ # ranges to match returned data to. Values should have exactly as many
607
+ # elements as there are columns in the primary key.
608
+ #
609
+ # @return [Time] The timestamp at which the operation committed.
610
+ #
611
+ # @example
612
+ # require "google/cloud/spanner"
613
+ #
614
+ # spanner = Google::Cloud::Spanner.new
615
+ #
616
+ # db = spanner.client "my-instance", "my-database"
617
+ #
618
+ # db.delete "users", [1, 2, 3]
619
+ #
620
+ def delete table, keys = []
621
+ @pool.with_session do |session|
622
+ session.delete table, keys
623
+ end
624
+ end
625
+
626
+ ##
627
+ # Creates and commits a transaction for writes that execute atomically
628
+ # at a single logical point in time across columns, rows, and tables in
629
+ # a database.
630
+ #
631
+ # All changes are accumulated in memory until the block completes.
632
+ # Unlike {#transaction}, which can also perform reads, this operation
633
+ # accepts only mutations and makes a single API request.
634
+ #
635
+ # **Note:** This method does not feature replay protection present in
636
+ # {Transaction#commit} (See {#transaction}). This method makes a single
637
+ # RPC, whereas {Transaction#commit} requires two RPCs (one of which may
638
+ # be performed in advance), and so this method may be appropriate for
639
+ # latency sensitive and/or high throughput blind changes.
640
+ #
641
+ # @yield [commit] The block for mutating the data.
642
+ # @yieldparam [Google::Cloud::Spanner::Commit] commit The Commit object.
643
+ #
644
+ # @return [Time] The timestamp at which the operation committed.
645
+ #
646
+ # @example
647
+ # require "google/cloud/spanner"
648
+ #
649
+ # spanner = Google::Cloud::Spanner.new
650
+ #
651
+ # db = spanner.client "my-instance", "my-database"
652
+ #
653
+ # db.commit do |c|
654
+ # c.update "users", [{ id: 1, name: "Charlie", active: false }]
655
+ # c.insert "users", [{ id: 2, name: "Harvey", active: true }]
656
+ # end
657
+ #
658
+ def commit &block
659
+ fail ArgumentError, "Must provide a block" unless block_given?
660
+
661
+ @pool.with_session do |session|
662
+ session.commit(&block)
663
+ end
664
+ end
665
+
666
+ ##
667
+ # Creates a transaction for reads and writes that execute atomically at
668
+ # a single logical point in time across columns, rows, and tables in a
669
+ # database.
670
+ #
671
+ # The transaction will always commit unless an error is raised. If the
672
+ # error raised is {Rollback} the transaction method will return without
673
+ # passing on the error. All other errors will be passed on.
674
+ #
675
+ # All changes are accumulated in memory until the block completes.
676
+ # Transactions will be automatically retried when possible, until
677
+ # `deadline` is reached. This operation makes separate API requests to
678
+ # begin and commit the transaction.
679
+ #
680
+ # @param [Numeric] deadline The total amount of time in seconds the
681
+ # transaction has to succeed. The default is `120`.
682
+ #
683
+ # @yield [transaction] The block for reading and writing data.
684
+ # @yieldparam [Google::Cloud::Spanner::Transaction] transaction The
685
+ # Transaction object.
686
+ #
687
+ # @return [Time] The timestamp at which the transaction committed.
688
+ #
689
+ # @example
690
+ # require "google/cloud/spanner"
691
+ #
692
+ # spanner = Google::Cloud::Spanner.new
693
+ # db = spanner.client "my-instance", "my-database"
694
+ #
695
+ # db.transaction do |tx|
696
+ # results = tx.execute "SELECT * FROM users"
697
+ #
698
+ # results.rows.each do |row|
699
+ # puts "User #{row[:id]} is #{row[:name]}"
700
+ # end
701
+ #
702
+ # c.update "users", [{ id: 1, name: "Charlie", active: false }]
703
+ # c.insert "users", [{ id: 2, name: "Harvey", active: true }]
704
+ # end
705
+ #
706
+ # @example Manually rollback the transaction using {Rollback}:
707
+ # require "google/cloud/spanner"
708
+ #
709
+ # spanner = Google::Cloud::Spanner.new
710
+ # db = spanner.client "my-instance", "my-database"
711
+ #
712
+ # db.transaction do |tx|
713
+ # c.update "users", [{ id: 1, name: "Charlie", active: false }]
714
+ # c.insert "users", [{ id: 2, name: "Harvey", active: true }]
715
+ #
716
+ # if something_wrong?
717
+ # # Rollback the transaction without passing on the error
718
+ # # outside of the transaction method.
719
+ # raise Google::Cloud::Spanner::Rollback
720
+ # end
721
+ # end
722
+ #
723
+ def transaction deadline: 120, &block
724
+ ensure_service!
725
+
726
+ deadline = validate_deadline deadline
727
+ backoff = 1.0
728
+ start_time = current_time
729
+
730
+ @pool.with_transaction do |tx|
731
+ begin
732
+ block.call tx
733
+ commit_resp = @project.service.commit \
734
+ tx.session.path, tx.mutations, transaction_id: tx.transaction_id
735
+ return Convert.timestamp_to_time commit_resp.commit_timestamp
736
+ rescue Google::Cloud::AbortedError => err
737
+ # Re-raise if deadline has passed
738
+ raise err if current_time - start_time > deadline
739
+ # Sleep the amount from RetryDelay, or incremental backoff
740
+ sleep(delay_from_aborted(err) || backoff *= 1.3)
741
+ # Create new transaction on the session and retry the block
742
+ tx = tx.session.create_transaction
743
+ retry
744
+ rescue => err
745
+ # Rollback transaction when handling unexpected error
746
+ tx.session.rollback tx.transaction_id
747
+ # Return nil if raised with rollback.
748
+ return nil if err.is_a? Rollback
749
+ # Re-raise error.
750
+ raise err
751
+ end
752
+ end
753
+ end
754
+
755
+ ##
756
+ # Creates a snapshot read-only transaction for reads that execute
757
+ # atomically at a single logical point in time across columns, rows, and
758
+ # tables in a database. For transactions that only read, snapshot
759
+ # read-only transactions provide simpler semantics and are almost always
760
+ # faster than read-write transactions.
761
+ #
762
+ # @param [true, false] strong Read at a timestamp where all previously
763
+ # committed transactions are visible.
764
+ # @param [Time, DateTime] timestamp Executes all reads at the given
765
+ # timestamp. Unlike other modes, reads at a specific timestamp are
766
+ # repeatable; the same read at the same timestamp always returns the
767
+ # same data. If the timestamp is in the future, the read will block
768
+ # until the specified timestamp, modulo the read's deadline.
769
+ #
770
+ # Useful for large scale consistent reads such as mapreduces, or for
771
+ # coordinating many reads against a consistent snapshot of the data.
772
+ # (See
773
+ # [TransactionOptions](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#transactionoptions).)
774
+ # @param [Time, DateTime] read_timestamp Same as `timestamp`.
775
+ # @param [Numeric] staleness Executes all reads at a timestamp that is
776
+ # +staleness+ old. The timestamp is chosen soon after the read
777
+ # is started.
778
+ #
779
+ # Guarantees that all writes that have committed more than the
780
+ # specified number of seconds ago are visible. Because Cloud Spanner
781
+ # chooses the exact timestamp, this mode works even if the client's
782
+ # local clock is substantially skewed from Cloud Spanner commit
783
+ # timestamps.
784
+ #
785
+ # Useful for reading at nearby replicas without the distributed
786
+ # timestamp negotiation overhead of single-use +staleness+. (See
787
+ # [TransactionOptions](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#transactionoptions).)
788
+ # @param [Numeric] exact_staleness Same as `staleness`.
789
+ #
790
+ # @yield [snapshot] The block for reading and writing data.
791
+ # @yieldparam [Google::Cloud::Spanner::Snapshot] snapshot The Snapshot
792
+ # object.
793
+ #
794
+ # @example
795
+ # require "google/cloud/spanner"
796
+ #
797
+ # spanner = Google::Cloud::Spanner.new
798
+ # db = spanner.client "my-instance", "my-database"
799
+ #
800
+ # db.snapshot do |snp|
801
+ # results = snp.execute "SELECT * FROM users"
802
+ #
803
+ # results.rows.each do |row|
804
+ # puts "User #{row[:id]} is #{row[:name]}"
805
+ # end
806
+ # end
807
+ #
808
+ def snapshot strong: nil, timestamp: nil, read_timestamp: nil,
809
+ staleness: nil, exact_staleness: nil
810
+ validate_snapshot_args! strong: strong, timestamp: timestamp,
811
+ read_timestamp: read_timestamp,
812
+ staleness: staleness,
813
+ exact_staleness: exact_staleness
814
+ ensure_service!
815
+ @pool.with_session do |session|
816
+ snp_grpc = @project.service.create_snapshot \
817
+ session.path, strong: strong,
818
+ timestamp: (timestamp || read_timestamp),
819
+ staleness: (staleness || exact_staleness)
820
+ snp = Snapshot.from_grpc(snp_grpc, session)
821
+ yield snp if block_given?
822
+ end
823
+ nil
824
+ end
825
+
826
+ ##
827
+ # @private
828
+ # Creates fields object from types.
829
+ #
830
+ # @param [Array, Hash] types Accepts an array of types, array of type
831
+ # pairs, hash of positional types, hash of named types.
832
+ #
833
+ # @return [Fields] The fields of the given types.
834
+ #
835
+ # @example
836
+ # require "google/cloud/spanner"
837
+ #
838
+ # spanner = Google::Cloud::Spanner.new
839
+ #
840
+ # db = spanner.client "my-instance", "my-database"
841
+ # user_fields = db.fields id: :INT64, name: :STRING, active: :BOOL
842
+ #
843
+ # db.update "users", [user_fields.data(1, "Charlie", false),
844
+ # user_fields.data(2, "Harvey", true)]
845
+ #
846
+ def fields types
847
+ Fields.new types
848
+ end
849
+
850
+ ##
851
+ # @private
852
+ # Executes a query to retrieve the field names and types for a table.
853
+ #
854
+ # @param [String] table The name of the table in the database to
855
+ # retrieve fields for.
856
+ #
857
+ # @return [Fields] The fields of the given table.
858
+ #
859
+ # @example
860
+ # require "google/cloud/spanner"
861
+ #
862
+ # spanner = Google::Cloud::Spanner.new
863
+ #
864
+ # db = spanner.client "my-instance", "my-database"
865
+ #
866
+ # users_types = db.fields_for "users"
867
+ # db.insert "users", [{ id: 1, name: "Charlie", active: false },
868
+ # { id: 2, name: "Harvey", active: true }],
869
+ # types: users_types
870
+ #
871
+ def fields_for table
872
+ execute("SELECT * FROM #{table} WHERE 1 = 0").fields
873
+ end
874
+
875
+ ##
876
+ # Creates a Spanner Range. This can be used in place of a Ruby Range
877
+ # when needing to exclude the beginning value.
878
+ #
879
+ # @param [Object] beginning The object that defines the beginning of the
880
+ # range.
881
+ # @param [Object] ending The object that defines the end of the range.
882
+ # @param [Boolean] exclude_begin Determines if the range excludes its
883
+ # beginning value. Default is `false`.
884
+ # @param [Boolean] exclude_end Determines if the range excludes its
885
+ # ending value. Default is `false`.
886
+ #
887
+ # @return [Google::Cloud::Spanner::Range] The new Range instance.
888
+ #
889
+ # @example
890
+ # require "google/cloud/spanner"
891
+ #
892
+ # spanner = Google::Cloud::Spanner.new
893
+ #
894
+ # db = spanner.client "my-instance", "my-database"
895
+ #
896
+ # key_range = db.range 1, 100
897
+ # results = db.read "users", [:id, :name], keys: key_range
898
+ #
899
+ # results.rows.each do |row|
900
+ # puts "User #{row[:id]} is #{row[:name]}"
901
+ # end
902
+ #
903
+ def range beginning, ending, exclude_begin: false, exclude_end: false
904
+ Range.new beginning, ending,
905
+ exclude_begin: exclude_begin,
906
+ exclude_end: exclude_end
907
+ end
908
+
909
+ ##
910
+ # Closes the client connection and releases resources.
911
+ #
912
+ def close
913
+ @pool.close
914
+ end
915
+
916
+ ##
917
+ # @private
918
+ # Creates a new session object every time.
919
+ def create_new_session
920
+ ensure_service!
921
+ grpc = @project.service.create_session \
922
+ Admin::Database::V1::DatabaseAdminClient.database_path(
923
+ project_id, instance_id, database_id)
924
+ Session.from_grpc(grpc, @project.service)
925
+ end
926
+
927
+ # @private
928
+ def to_s
929
+ "(project_id: #{project_id}, instance_id: #{instance_id}, " \
930
+ "database_id: #{database_id})"
931
+ end
932
+
933
+ # @private
934
+ def inspect
935
+ "#<#{self.class.name} #{self}>"
936
+ end
937
+
938
+ protected
939
+
940
+ ##
941
+ # @private Raise an error unless an active connection to the service is
942
+ # available.
943
+ def ensure_service!
944
+ fail "Must have active connection to service" unless @project.service
945
+ end
946
+
947
+ ##
948
+ # Check for valid snapshot arguments
949
+ def validate_single_use_args! opts
950
+ return true if opts.nil? || opts.empty?
951
+ valid_keys = [:strong, :timestamp, :read_timestamp, :staleness,
952
+ :exact_staleness, :bounded_timestamp,
953
+ :min_read_timestamp, :bounded_staleness, :max_staleness]
954
+ if opts.keys.count == 1 && valid_keys.include?(opts.keys.first)
955
+ return true
956
+ end
957
+ fail ArgumentError,
958
+ "Must provide only one of the following single_use values: " \
959
+ "#{valid_keys}"
960
+ end
961
+
962
+ ##
963
+ # Create a single-use TransactionSelector
964
+ def single_use_transaction opts
965
+ return nil if opts.nil? || opts.empty?
966
+
967
+ exact_timestamp = Convert.time_to_timestamp \
968
+ opts[:timestamp] || opts[:read_timestamp]
969
+ exact_staleness = Convert.number_to_duration \
970
+ opts[:staleness] || opts[:exact_staleness]
971
+ bounded_timestamp = Convert.time_to_timestamp \
972
+ opts[:bounded_timestamp] || opts[:min_read_timestamp]
973
+ bounded_staleness = Convert.number_to_duration \
974
+ opts[:bounded_staleness] || opts[:max_staleness]
975
+
976
+ Google::Spanner::V1::TransactionSelector.new(single_use:
977
+ Google::Spanner::V1::TransactionOptions.new(read_only:
978
+ Google::Spanner::V1::TransactionOptions::ReadOnly.new({
979
+ strong: opts[:strong],
980
+ read_timestamp: exact_timestamp,
981
+ exact_staleness: exact_staleness,
982
+ min_read_timestamp: bounded_timestamp,
983
+ max_staleness: bounded_staleness,
984
+ return_read_timestamp: true
985
+ }.delete_if { |_, v| v.nil? })))
986
+ end
987
+
988
+ ##
989
+ # Check for valid snapshot arguments
990
+ def validate_snapshot_args! strong: nil,
991
+ timestamp: nil, read_timestamp: nil,
992
+ staleness: nil, exact_staleness: nil
993
+ valid_args_count = [strong, timestamp, read_timestamp, staleness,
994
+ exact_staleness].compact.count
995
+ return true if valid_args_count <= 1
996
+ fail ArgumentError,
997
+ "Can only provide one of the following arguments: " \
998
+ "(strong, timestamp, read_timestamp, staleness, exact_staleness)"
999
+ end
1000
+
1001
+ def validate_deadline deadline
1002
+ return 120 unless deadline.is_a? Numeric
1003
+ return 120 if deadline < 0
1004
+ deadline
1005
+ end
1006
+
1007
+ ##
1008
+ # Defer to this method so we have something to mock for tests
1009
+ def current_time
1010
+ Time.now
1011
+ end
1012
+
1013
+ ##
1014
+ # Retrieves the delay value from Google::Cloud::AbortedError
1015
+ def delay_from_aborted err
1016
+ return nil if err.nil?
1017
+ if err.respond_to?(:metadata) && err.metadata["retryDelay"]
1018
+ # a correct metadata will look like this:
1019
+ # "{\"retryDelay\":{\"seconds\":60}}"
1020
+ seconds = err.metadata["retryDelay"]["seconds"].to_i
1021
+ nanos = err.metadata["retryDelay"]["nanos"].to_i
1022
+ return seconds if nanos.zero?
1023
+ return seconds + (nanos / 1000000000.0)
1024
+ end
1025
+ # No metadata? Try the inner error
1026
+ delay_from_aborted(err.cause)
1027
+ rescue
1028
+ # Any error indicates the backoff should be handled elsewhere
1029
+ return nil
1030
+ end
1031
+ end
1032
+ end
1033
+ end
1034
+ end