google-cloud-spanner 2.29.0 → 2.31.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.
@@ -16,6 +16,7 @@
16
16
  require "concurrent"
17
17
  require "google/cloud/spanner/errors"
18
18
  require "google/cloud/spanner/session"
19
+ require "google/cloud/spanner/session_creation_options"
19
20
 
20
21
  module Google
21
22
  module Cloud
@@ -37,7 +38,9 @@ module Google
37
38
  attr_accessor :sessions_in_use
38
39
 
39
40
  # Creates a new Session pool that manages non-multiplexed sessions.
40
- # @param client [::Google::Cloud::Spanner::Client] A `Spanner::Client` reference
41
+ # @param service [::Google::Cloud::Spanner::Service] A `Spanner::Service` reference.
42
+ # @param session_creation_options [::Google::Cloud::Spanner::SessionCreationOptions] Required.
43
+ # Options used for session creation. E.g. session labels.
41
44
  # @param min [::Integer] Min number of sessions to keep
42
45
  # @param max [::Integer] Max number of sessions to keep
43
46
  # @param keepalive [::Numeric] How long after their last usage the sessions can be reclaimed
@@ -46,9 +49,10 @@ module Google
46
49
  # @param threads [::Integer, nil] Number of threads in the thread pool that is used for keepalive and
47
50
  # release session actions. If `nil` the Pool will choose a reasonable default.
48
51
  # @private
49
- def initialize client, min: 10, max: 100, keepalive: 1800,
52
+ def initialize service, session_creation_options, min: 10, max: 100, keepalive: 1800,
50
53
  fail: true, threads: nil
51
- @client = client
54
+ @service = service
55
+ @session_creation_options = session_creation_options
52
56
  @min = min
53
57
  @max = max
54
58
  @keepalive = keepalive
@@ -127,7 +131,7 @@ module Google
127
131
  nil
128
132
  end
129
133
 
130
- def reset
134
+ def reset!
131
135
  close
132
136
  init
133
137
 
@@ -137,6 +141,7 @@ module Google
137
141
 
138
142
  true
139
143
  end
144
+ alias reset reset!
140
145
 
141
146
  def close
142
147
  shutdown
@@ -181,7 +186,7 @@ module Google
181
186
  # init the keepalive task
182
187
  create_keepalive_task!
183
188
  # init session stack
184
- @sessions_available = @client.batch_create_new_sessions @min
189
+ @sessions_available = batch_create_new_sessions @min
185
190
  @sessions_in_use = {}
186
191
  end
187
192
 
@@ -209,7 +214,7 @@ module Google
209
214
  end
210
215
 
211
216
  begin
212
- session = @client.create_new_session
217
+ session = create_new_session
213
218
  rescue StandardError => e
214
219
  @mutex.synchronize do
215
220
  @new_sessions_in_process -= 1
@@ -239,6 +244,60 @@ module Google
239
244
  def future(&)
240
245
  Concurrent::Future.new(executor: @thread_pool, &).execute
241
246
  end
247
+
248
+ # Creates a new `Spanner::Session`.
249
+ # @private
250
+ # @return [::Google::Cloud::Spanner::Session]
251
+ def create_new_session
252
+ ensure_service!
253
+ grpc = @service.create_session(
254
+ @session_creation_options.database_path,
255
+ labels: @session_creation_options.session_labels,
256
+ database_role: @session_creation_options.session_creator_role
257
+ )
258
+
259
+ Session.from_grpc grpc, @service, query_options: @query_options
260
+ end
261
+
262
+ # Creates a batch of new session objects of size `total`.
263
+ # Makes multiple RPCs if necessary. Returns empty array if total is 0.
264
+ # @private
265
+ # @return [::Array<::Google::Cloud::Spanner::Session>]
266
+ def batch_create_new_sessions total
267
+ sessions = []
268
+ remaining = total
269
+ while remaining.positive?
270
+ sessions += batch_create_sessions remaining
271
+ remaining = total - sessions.count
272
+ end
273
+ sessions
274
+ end
275
+
276
+ # Tries to creates a batch of new session objects of size `session_count`.
277
+ # The response may have fewer sessions than requested in the RPC.
278
+ # @private
279
+ # @return [::Array<::Google::Cloud::Spanner::Session>]
280
+ def batch_create_sessions session_count
281
+ ensure_service!
282
+ resp = @service.batch_create_sessions(
283
+ @session_creation_options.database_path,
284
+ session_count,
285
+ labels: @session_creation_options.session_labels,
286
+ database_role: @session_creation_options.session_creator_role
287
+ )
288
+
289
+ resp.session.map do |grpc|
290
+ Session.from_grpc grpc, @service, query_options: @query_options
291
+ end
292
+ end
293
+
294
+ # Raise an error unless an active connection to the service is available.
295
+ # @private
296
+ # @raise [::StandardError]
297
+ # @return [void]
298
+ def ensure_service!
299
+ raise "Must have active connection to service" unless @service
300
+ end
242
301
  end
243
302
  end
244
303
  end
@@ -509,25 +509,9 @@ module Google
509
509
  # Required.
510
510
  # @param [String] database_id The unique identifier for the database.
511
511
  # Required.
512
- # @param [Hash] pool Settings to control how and when sessions are
513
- # managed by the client. The following settings can be provided:
514
- #
515
- # * `:min` (Integer) Minimum number of sessions that the client will
516
- # maintain at any point in time. The default is 10.
517
- # * `:max` (Integer) Maximum number of sessions that the client will
518
- # have at any point in time. The default is 100.
519
- # * `:keepalive` (Numeric) The amount of time a session can be idle
520
- # before an attempt is made to prevent the idle sessions from being
521
- # closed by the Cloud Spanner service. The default is 1800 (30
522
- # minutes).
523
- # * `:fail` (true/false) When `true` the client raises a
524
- # {SessionLimitError} when the client has allocated the `max` number
525
- # of sessions. When `false` the client blocks until a session
526
- # becomes available. The default is `true`.
527
- # * `:threads` (Integer) The number of threads in the thread pool. The
528
- # default is twice the number of available CPUs.
529
- # * `:write_ratio` (Float) Deprecated. This field is no longer needed
530
- # and will be removed in a future release.
512
+ # @param [Hash] pool Optional. Defaults to `{}`. Deprecated.
513
+ # @deprecated This parameter is non-functional since the multiplexed SessionCache does not require
514
+ # pool options.
531
515
  # @param [Hash] labels The labels to be applied to all sessions
532
516
  # created by the client. Cloud Labels are a flexible and lightweight
533
517
  # mechanism for organizing cloud resources into groups that reflect a
@@ -567,7 +551,7 @@ module Google
567
551
  # Spanner will wait for a replica in the list to become available,
568
552
  # requests may fail due to DEADLINE_EXCEEDED errors.
569
553
  #
570
- # @return [Client] The newly created client.
554
+ # @return [::Google::Cloud::Spanner::Client] The newly created client.
571
555
  #
572
556
  # @example
573
557
  # require "google/cloud/spanner"
@@ -594,9 +578,11 @@ module Google
594
578
  else
595
579
  query_options = query_options.merge @query_options unless @query_options.nil?
596
580
  end
581
+
582
+ _pool = pool # unused. Here only to avoid having to disable Rubocop's Lint/UnusedMethodArgument
583
+
597
584
  Client.new self, instance_id, database_id,
598
585
  session_labels: labels,
599
- pool_opts: valid_session_pool_options(pool),
600
586
  query_options: query_options,
601
587
  database_role: database_role,
602
588
  directed_read_options: directed_read_options
@@ -649,7 +635,7 @@ module Google
649
635
  # Spanner will wait for a replica in the list to become available,
650
636
  # requests may fail due to DEADLINE_EXCEEDED errors.
651
637
  #
652
- # @return [Client] The newly created client.
638
+ # @return [::Google::Cloud::Spanner::BatchClient] The newly created client.
653
639
  #
654
640
  # @example
655
641
  # require "google/cloud/spanner"
@@ -45,32 +45,42 @@ module Google
45
45
  # end
46
46
  #
47
47
  class Results
48
- # The `V1::ResultSetMetadata` protobuf object from the first
49
- # PartialResultSet.
50
- # @private
51
- # @return [::Google::Cloud::Spanner::V1::ResultSetMetadata]
52
- attr_reader :metadata
53
-
54
48
  # Creates a new Results instance.
55
49
  # @param service [::Google::Cloud::Spanner::Service] The `Spanner::Service` reference.
56
50
  # @param partial_result_sets [::Enumerable<::Google::Cloud::Spanner::V1::PartialResultSet>]
57
51
  # Raw enumerable from grpc `StreamingRead` call.
58
52
  # @param session_name [::String] Required.
59
- # The session in which the transaction to be committed is running.
53
+ # The name of the session for the operation that created these Results.
60
54
  # Values are of the form:
61
55
  # `projects/<project_id>/instances/<instance_id>/databases/<database_id>/sessions/<session_id>`.
62
56
  # @param metadata [::Google::Cloud::Spanner::V1::ResultSetMetadata] ParialResultSet metadata object
63
57
  # @param stats [::Google::Cloud::Spanner::V1::ResultSetStats] Query plan and execution statistics
64
58
  # for the statement that produced this streaming result set.
59
+ # @param precommit_token_notify [::Proc, nil] Optional.
60
+ # The notification function for the precommit token.
65
61
  # @private
66
- def initialize service, partial_result_sets, session_name, metadata, stats
62
+ def initialize service, partial_result_sets, session_name, metadata, stats, precommit_token_notify: nil
67
63
  @service = service
68
64
  @partial_result_sets = partial_result_sets
69
65
  @session_name = session_name
70
66
  @metadata = metadata
71
67
  @stats = stats
68
+
69
+ # The notification function for the precommit token.
70
+ # `Results` object will see precommit tokens while iterating if it were created
71
+ # in the context of an RW transaction on a multiplexed session.
72
+ # Precommit token is a passthrough parameter that that transaction will need to supply in order to Commit.
73
+ # There can be multiple precommit tokens in the stream
74
+ # (should be at least 2 -- with the first and last PartialResultSet).
75
+ @precommit_token_notify = precommit_token_notify
72
76
  end
73
77
 
78
+ # The `V1::ResultSetMetadata` protobuf object from the first
79
+ # PartialResultSet.
80
+ # @private
81
+ # @return [::Google::Cloud::Spanner::V1::ResultSetMetadata]
82
+ attr_reader :metadata
83
+
74
84
  ##
75
85
  # The read timestamp chosen for single-use snapshots (read-only
76
86
  # transactions).
@@ -162,13 +172,21 @@ module Google
162
172
  should_retry_request = false
163
173
  end
164
174
 
165
- # @type [::Google::Cloud::Spanner::V1::PartialResultsSet]
175
+ # @type [::Google::Cloud::Spanner::V1::PartialResultSet]
166
176
  grpc = @partial_result_sets.next
167
177
 
168
178
  # metadata should be set before the first iteration...
169
179
  @metadata ||= grpc.metadata
170
180
  @stats ||= grpc.stats
171
181
 
182
+ # The precommit token should be issued on first and last stream element.
183
+ # These two precommit tokens can be different.
184
+ # If these Results are created in the context of a `Spanner::Transaction`,
185
+ # that `Transaction` object is the one keeping track of the precommit token and should be notified.
186
+ if grpc.precommit_token && @precommit_token_notify
187
+ @precommit_token_notify.call(grpc.precommit_token)
188
+ end
189
+
172
190
  buffered_responses << grpc
173
191
 
174
192
  if (grpc.resume_token && grpc.resume_token != "") ||
@@ -327,17 +345,26 @@ module Google
327
345
  # Raw enumerable from underlying grpc call.
328
346
  # @param service [::Google::Cloud::Spanner::Service] The `Spanner::Service` reference.
329
347
  # @param session_name [::String] Required.
330
- # The session in which the transaction to be committed is running.
348
+ # The name of the session for the operation that created these Results.
331
349
  # Values are of the form:
332
350
  # `projects/<project_id>/instances/<instance_id>/databases/<database_id>/sessions/<session_id>`.
351
+ # @param precommit_token_notify [::Proc, nil] Optional.
352
+ # The notification function for the precommit token.
333
353
  # @private
334
354
  # @return [::Google::Cloud::Spanner::Results]
335
- def self.from_partial_result_sets partial_result_sets, service, session_name
355
+ def self.from_partial_result_sets partial_result_sets, service, session_name, precommit_token_notify: nil
336
356
  # @type [::Google::Cloud::Spanner::V1::PartialResultSet]
337
357
  partial_result_set = partial_result_sets.peek
338
358
  metadata = partial_result_set.metadata
339
359
  stats = partial_result_set.stats
340
- new service, partial_result_sets, session_name, metadata, stats
360
+ results = new service, partial_result_sets, session_name, metadata, stats,
361
+ precommit_token_notify: precommit_token_notify
362
+
363
+ if partial_result_set.precommit_token && precommit_token_notify
364
+ precommit_token_notify.call partial_result_set.precommit_token
365
+ end
366
+
367
+ results
341
368
  rescue GRPC::BadStatus => e
342
369
  raise Google::Cloud::Error.from_error(e)
343
370
  end
@@ -355,10 +382,14 @@ module Google
355
382
  # that were sent to the `service.execute_streaming_sql`. This hash joins params needed to
356
383
  # construct `::Gapic::CallOptions`, e.g. `call_options` and header-related `route_to_leader`
357
384
  # with params specific to `execute_streaming_sql`, such as `seqno`.
385
+ # @param precommit_token_notify [::Proc, nil] Optional.
386
+ # The notification function for the precommit token.
358
387
  # @private
359
388
  # @return [::Google::Cloud::Spanner::Results]
360
- def self.from_execute_query_response response, service, session_name, sql, execute_query_options
361
- from_partial_result_sets(response, service, session_name).tap do |results|
389
+ def self.from_execute_query_response response, service, session_name, sql, execute_query_options,
390
+ precommit_token_notify: nil
391
+ from_partial_result_sets(response, service, session_name,
392
+ precommit_token_notify: precommit_token_notify).tap do |results|
362
393
  execute_query_options_copy = execute_query_options.dup
363
394
  unless results.metadata.transaction.nil?
364
395
  execute_query_options_copy[:transaction] = V1::TransactionSelector.new id: results.metadata.transaction.id
@@ -384,10 +415,14 @@ module Google
384
415
  # that were sent to the `service.streaming_read_table`. This hash joins params needed to
385
416
  # construct `::Gapic::CallOptions`, e.g. `call_options` and header-related `route_to_leader`
386
417
  # with params specific to `streaming_read_table`, such as `keys`.
418
+ # @param precommit_token_notify [::Proc, nil] Optional.
419
+ # The notification function for the precommit token.
387
420
  # @private
388
421
  # @return [::Google::Cloud::Spanner::Results]
389
- def self.from_read_response response, service, session_name, table, columns, read_options
390
- from_partial_result_sets(response, service, session_name).tap do |results|
422
+ def self.from_read_response response, service, session_name, table, columns, read_options,
423
+ precommit_token_notify: nil
424
+ from_partial_result_sets(response, service, session_name,
425
+ precommit_token_notify: precommit_token_notify).tap do |results|
391
426
  read_options_copy = read_options.dup
392
427
  unless results.metadata.transaction.nil?
393
428
  read_options_copy[:transaction] = V1::TransactionSelector.new id: results.metadata.transaction.id
@@ -352,17 +352,17 @@ module Google
352
352
  # @param database_name [::String] The full name of the database.
353
353
  # @param labels [::Hash, nil] Optional. The labels to be applied to all sessions
354
354
  # created by the client. Example: `"team" => "billing-service"`.
355
- # @param call_options [::Hash, nil] Optional. A hash of values to specify the custom
356
- # call options. Example option `:timeout`.
357
355
  # @param database_role [::String, nil] Optional. The Spanner session creator role.
358
356
  # Example: `analyst`.
359
357
  # @param multiplexed [::Boolean] Optional. Default to `false`.
360
358
  # If `true`, specifies a multiplexed session.
359
+ # @param call_options [::Hash, nil] Optional. A hash of values to specify the custom
360
+ # call options. Example option `:timeout`.
361
361
  # @return [::Google::Cloud::Spanner::V1::Session]
362
362
  # @private
363
363
  def create_session database_name, labels: nil,
364
- call_options: nil, database_role: nil,
365
- multiplexed: false
364
+ database_role: nil, multiplexed: false,
365
+ call_options: nil
366
366
  route_to_leader = LARHeaders.create_session
367
367
  opts = default_options(
368
368
  session_name: database_name,
@@ -524,23 +524,30 @@ module Google
524
524
  # @param exclude_txn_from_change_streams [::Boolean] Optional. Defaults to `false`.
525
525
  # When `exclude_txn_from_change_streams` is set to `true`, it prevents read
526
526
  # or write transactions from being tracked in change streams.
527
+ # @param isolation_level [::Google::Cloud::Spanner::V1::TransactionOptions::IsolationLevel] Optional.
528
+ # The isolation level for the transaction.
527
529
  # @param commit_options [::Hash, nil] Optional. A hash of commit options.
528
530
  # Example option: `:return_commit_stats`.
529
531
  # @param request_options [::Hash, nil] Optional. Common request options.
530
532
  # Example option: `:priority`.
531
533
  # @param call_options [::Hash, nil] Optional. A hash of values to specify the custom
532
534
  # call options. Example option `:timeout`.
535
+ # @param precommit_token [::Google::Cloud::Spanner::V1::MultiplexedSessionPrecommitToken, nil] Optional.
536
+ # If the read-write transaction was executed on a multiplexed session, then a precommit token
537
+ # with the highest sequence number received in this transaction attempt must be included.
533
538
  # @private
534
539
  # @return [::Google::Cloud::Spanner::V1::CommitResponse]
535
540
  def commit session_name, mutations = [],
536
541
  transaction_id: nil, exclude_txn_from_change_streams: false,
537
- commit_options: nil, request_options: nil, call_options: nil
542
+ isolation_level: nil, commit_options: nil, request_options: nil,
543
+ call_options: nil, precommit_token: nil
538
544
  route_to_leader = LARHeaders.commit
539
545
  tx_opts = nil
540
546
  if transaction_id.nil?
541
547
  tx_opts = V1::TransactionOptions.new(
542
548
  read_write: V1::TransactionOptions::ReadWrite.new,
543
- exclude_txn_from_change_streams: exclude_txn_from_change_streams
549
+ exclude_txn_from_change_streams: exclude_txn_from_change_streams,
550
+ isolation_level: isolation_level
544
551
  )
545
552
  end
546
553
  opts = default_options session_name: session_name,
@@ -549,7 +556,7 @@ module Google
549
556
  request = {
550
557
  session: session_name, transaction_id: transaction_id,
551
558
  single_use_transaction: tx_opts, mutations: mutations,
552
- request_options: request_options
559
+ request_options: request_options, precommit_token: precommit_token
553
560
  }
554
561
 
555
562
  request = add_commit_options request, commit_options
@@ -617,24 +624,44 @@ module Google
617
624
  # @param route_to_leader [::String, nil] Optional. The value to be sent
618
625
  # as `x-goog-spanner-route-to-leader` header for leader aware routing.
619
626
  # Expected values: `"true"` or `"false"`.
627
+ # @param mutation_key [::Google::Cloud::Spanner::V1::Mutation, nil] Optional.
628
+ # If a read-write transaction on a multiplexed session commit mutations
629
+ # without performing any reads or queries, one of the mutations from the mutation set
630
+ # must be sent as a mutation key for `BeginTransaction`.
631
+ # @param previous_transaction_id [::String, nil] Optional.
632
+ # An id of the previous transaction, if this new transaction wrapper is being created
633
+ # as a part of a retry. Previous transaction id should be added to TransactionOptions
634
+ # of a new ReadWrite transaction when retry is attempted.
620
635
  # @private
621
636
  # @return [::Google::Cloud::Spanner::V1::Transaction]
622
637
  def begin_transaction session_name,
623
638
  exclude_txn_from_change_streams: false,
624
639
  request_options: nil,
625
640
  call_options: nil,
626
- route_to_leader: nil
641
+ route_to_leader: nil,
642
+ mutation_key: nil,
643
+ previous_transaction_id: nil
644
+ read_write = if previous_transaction_id.nil?
645
+ V1::TransactionOptions::ReadWrite.new
646
+ else
647
+ V1::TransactionOptions::ReadWrite.new(
648
+ multiplexed_session_previous_transaction_id: previous_transaction_id
649
+ )
650
+ end
651
+
627
652
  tx_opts = V1::TransactionOptions.new(
628
- read_write: V1::TransactionOptions::ReadWrite.new,
653
+ read_write: read_write,
629
654
  exclude_txn_from_change_streams: exclude_txn_from_change_streams
630
655
  )
656
+
631
657
  opts = default_options session_name: session_name,
632
658
  call_options: call_options,
633
659
  route_to_leader: route_to_leader
634
660
  request = {
635
661
  session: session_name,
636
662
  options: tx_opts,
637
- request_options: request_options
663
+ request_options: request_options,
664
+ mutation_key: mutation_key
638
665
  }
639
666
  service.begin_transaction request, opts
640
667
  end
@@ -657,6 +684,28 @@ module Google
657
684
  service.batch_write request, opts
658
685
  end
659
686
 
687
+ # Creates a specialized `V1::Transaction` object. Reads on that object will have
688
+ # at most one of following consistency properties:
689
+ # * reading all previously commited transactions
690
+ # * reading all data from a given timestamp
691
+ # * reading all data from a timestamp that is exactly a given value old
692
+ # (the last one sidesteps worries of client-server time skew).
693
+ #
694
+ # Having at _least_ one of those is not enforced so this can create normal transactions
695
+ # as well.
696
+ # Created transactions will include the the read timestamp chosen for the transaction.
697
+ # @param session_name [::String] Required.
698
+ # Required. The session in which the snapshot transaction is to be created..
699
+ # Values are of the form:
700
+ # `projects/<project_id>/instances/<instance_id>/databases/<database_id>/sessions/<session_id>`.
701
+ # @param strong [::Boolean, nil] Optional.
702
+ # Whether this transaction should have strong consistency.
703
+ # @param timestamp [::String, ::Date ::Time, nil] Optional.
704
+ # Timestamp that the reads should be executed at. Reads are repeatable with this option.
705
+ # @param staleness [::Numeric, nil] Optional.
706
+ # The offset of staleness that the reads should be executed at.
707
+ # @param call_options [::Hash, nil] Optional. A hash of values to specify the custom
708
+ # call options. Example option `:timeout`.
660
709
  def create_snapshot session_name, strong: nil, timestamp: nil,
661
710
  staleness: nil, call_options: nil
662
711
  tx_opts = V1::TransactionOptions.new(
@@ -671,7 +720,7 @@ module Google
671
720
  )
672
721
  opts = default_options session_name: session_name,
673
722
  call_options: call_options
674
- request = { session: session_name, options: tx_opts }
723
+ request = { session: session_name, options: tx_opts, mutation_key: nil }
675
724
  service.begin_transaction request, opts
676
725
  end
677
726
 
@@ -686,7 +735,7 @@ module Google
686
735
  opts = default_options session_name: session_name,
687
736
  call_options: call_options,
688
737
  route_to_leader: route_to_leader
689
- request = { session: session_name, options: tx_opts }
738
+ request = { session: session_name, options: tx_opts, mutation_key: nil }
690
739
  service.begin_transaction request, opts
691
740
  end
692
741
 
@@ -829,6 +878,21 @@ module Google
829
878
  value << " gccl"
830
879
  end
831
880
 
881
+ # Creates new `Gapic::CallOptions` from typical parameters for Spanner RPC calls.
882
+ #
883
+ # @param session_name [::String, nil] Optional.
884
+ # The session name. Used to extract the routing header. The value will be
885
+ # used to send the old `google-cloud-resource-prefix` routing header.
886
+ # Expected values are of the form:
887
+ # `projects/<project_id>/instances/<instance_id>/databases/<database_id>/sessions/<session_id>`.
888
+ # If nil is specified nothing will be sent.
889
+ # @param call_options [::Hash, nil] Optional. A hash of values to specify the custom
890
+ # call options. Example option `:timeout`.
891
+ # @param route_to_leader [::String, nil] Optional. The value to be sent
892
+ # as `x-goog-spanner-route-to-leader` header for leader aware routing.
893
+ # Expected values: `"true"` or `"false"`. If nil is specified nothing will be sent.
894
+ # @private
895
+ # @return [::Gapic::CallOptions]
832
896
  def default_options session_name: nil, call_options: nil, route_to_leader: nil
833
897
  opts = {}
834
898
  metadata = {}