google-cloud-spanner 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ecf7b383ed94a1522a5d90bd7574113f087c5ab
4
- data.tar.gz: c4698851d6e116762e2ea65a3e6e74f252af7060
3
+ metadata.gz: 0d52caffdd789f039f91609da3f8dba633459c82
4
+ data.tar.gz: ecc7cca28368c261123342f43e9b055f13c22d70
5
5
  SHA512:
6
- metadata.gz: 6014ed1d974e1524681604fc4da27ff8404308e7e5998ff6b2aa7c69cfc9d599d41a4312755177c33c4c499cc18c8623ce59fe433cebe30ce0dff96c63c73439
7
- data.tar.gz: a38b63f7ebada70240457881d43d12df69238ad0562f2a9384647c74cf8ddcec34e286e7946e657c55cfe5d09e467e0ae2655fbe32ac89c4ab2cef26e6e2e7c1
6
+ metadata.gz: cfd38cc3172d32788a6891d78e44e349b8410c23285bcaa2bd79b582a58a48ce167f45251fec7ceff4df44b8f9fc6484f83d4feb7d744958ca6d8c99b1f2d457
7
+ data.tar.gz: 41f84f6f1432833bc79ca731f8b99d29ea6edd70bc71ebdc6b34afe7a7b16f0b1709968d2d358aee5ba6df91f052cd6324a53590a6d7c5d8d73e544b42f5b9df
@@ -132,7 +132,7 @@ module Google
132
132
  #
133
133
  # db = spanner.client "my-instance", "my-database"
134
134
  #
135
- # results = client.execute "SELECT 1"
135
+ # results = db.execute "SELECT 1"
136
136
  #
137
137
  # results.rows.each do |row|
138
138
  # puts row
@@ -160,8 +160,8 @@ module Google
160
160
  # db = spanner.client "my-instance", "my-database"
161
161
  #
162
162
  # db.commit do |c|
163
- # c.update "users", [{ id: 1, name: "Charlie", active: false }]
164
- # c.insert "users", [{ id: 2, name: "Harvey", active: true }]
163
+ # c.update "users", [{ id: 1, username: "charlie94", name: "Charlie" }]
164
+ # c.insert "users", [{ id: 2, username: "harvey00", name: "Harvey" }]
165
165
  # end
166
166
  # ```
167
167
  #
@@ -232,7 +232,7 @@ module Google
232
232
  # # Read the second album budget.
233
233
  # second_album_result = tx.read "Albums", ["marketing_budget"],
234
234
  # keys: [[2, 2]], limit: 1
235
- # second_album_row = second_album_result.first
235
+ # second_album_row = second_album_result.rows.first
236
236
  # second_album_budget = second_album_row.values.first
237
237
  #
238
238
  # transfer_amount = 200000
@@ -245,7 +245,7 @@ module Google
245
245
  # # Read the first album's budget.
246
246
  # first_album_result = tx.read "Albums", ["marketing_budget"],
247
247
  # keys: [[1, 1]], limit: 1
248
- # first_album_row = first_album_result.first
248
+ # first_album_row = first_album_result.rows.first
249
249
  # first_album_budget = first_album_row.values.first
250
250
  #
251
251
  # # Update the budgets.
@@ -366,12 +366,16 @@ module Google
366
366
  def self.new project: nil, keyfile: nil, scope: nil, timeout: nil,
367
367
  client_config: nil
368
368
  project ||= Google::Cloud::Spanner::Project.default_project
369
+ project = project.to_s # Always cast to a string
370
+ fail ArgumentError, "project is missing" if project.empty?
371
+
369
372
  if keyfile.nil?
370
373
  credentials = Google::Cloud::Spanner::Credentials.default scope: scope
371
374
  else
372
375
  credentials = Google::Cloud::Spanner::Credentials.new(
373
376
  keyfile, scope: scope)
374
377
  end
378
+
375
379
  Google::Cloud::Spanner::Project.new(
376
380
  Google::Cloud::Spanner::Service.new(
377
381
  project, credentials, timeout: timeout,
@@ -6,9 +6,7 @@
6
6
  "DEADLINE_EXCEEDED",
7
7
  "UNAVAILABLE"
8
8
  ],
9
- "non_idempotent": [
10
- "UNAVAILABLE"
11
- ]
9
+ "non_idempotent": []
12
10
  },
13
11
  "retry_params": {
14
12
  "default": {
@@ -6,9 +6,7 @@
6
6
  "DEADLINE_EXCEEDED",
7
7
  "UNAVAILABLE"
8
8
  ],
9
- "non_idempotent": [
10
- "UNAVAILABLE"
11
- ]
9
+ "non_idempotent": []
12
10
  },
13
11
  "retry_params": {
14
12
  "default": {
@@ -51,13 +51,11 @@ module Google
51
51
  class Client
52
52
  ##
53
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
54
+ def initialize project, instance_id, database_id, opts = {}
56
55
  @project = project
57
56
  @instance_id = instance_id
58
57
  @database_id = database_id
59
- @pool = Pool.new self, min: min, max: max, keepalive: keepalive,
60
- write_ratio: write_ratio, fail: fail
58
+ @pool = Pool.new self, opts
61
59
  end
62
60
 
63
61
  # The unique identifier for the project.
@@ -663,6 +661,9 @@ module Google
663
661
  end
664
662
  end
665
663
 
664
+ # rubocop:disable Metrics/AbcSize
665
+ # rubocop:disable Metrics/MethodLength
666
+
666
667
  ##
667
668
  # Creates a transaction for reads and writes that execute atomically at
668
669
  # a single logical point in time across columns, rows, and tables in a
@@ -699,8 +700,8 @@ module Google
699
700
  # puts "User #{row[:id]} is #{row[:name]}"
700
701
  # end
701
702
  #
702
- # c.update "users", [{ id: 1, name: "Charlie", active: false }]
703
- # c.insert "users", [{ id: 2, name: "Harvey", active: true }]
703
+ # tx.update "users", [{ id: 1, name: "Charlie", active: false }]
704
+ # tx.insert "users", [{ id: 2, name: "Harvey", active: true }]
704
705
  # end
705
706
  #
706
707
  # @example Manually rollback the transaction using {Rollback}:
@@ -710,8 +711,8 @@ module Google
710
711
  # db = spanner.client "my-instance", "my-database"
711
712
  #
712
713
  # db.transaction do |tx|
713
- # c.update "users", [{ id: 1, name: "Charlie", active: false }]
714
- # c.insert "users", [{ id: 2, name: "Harvey", active: true }]
714
+ # tx.update "users", [{ id: 1, name: "Charlie", active: false }]
715
+ # tx.insert "users", [{ id: 2, name: "Harvey", active: true }]
715
716
  #
716
717
  # if something_wrong?
717
718
  # # Rollback the transaction without passing on the error
@@ -722,6 +723,9 @@ module Google
722
723
  #
723
724
  def transaction deadline: 120, &block
724
725
  ensure_service!
726
+ unless Thread.current[:transaction_id].nil?
727
+ fail "Nested transactions are not allowed"
728
+ end
725
729
 
726
730
  deadline = validate_deadline deadline
727
731
  backoff = 1.0
@@ -729,13 +733,19 @@ module Google
729
733
 
730
734
  @pool.with_transaction do |tx|
731
735
  begin
736
+ Thread.current[:transaction_id] = tx.transaction_id
732
737
  block.call tx
733
738
  commit_resp = @project.service.commit \
734
739
  tx.session.path, tx.mutations, transaction_id: tx.transaction_id
735
740
  return Convert.timestamp_to_time commit_resp.commit_timestamp
736
- rescue Google::Cloud::AbortedError => err
741
+ rescue GRPC::Aborted, Google::Cloud::AbortedError => err
737
742
  # Re-raise if deadline has passed
738
- raise err if current_time - start_time > deadline
743
+ if current_time - start_time > deadline
744
+ if err.is_a? GRPC::BadStatus
745
+ err = Google::Cloud::Error.from_error err
746
+ end
747
+ raise err
748
+ end
739
749
  # Sleep the amount from RetryDelay, or incremental backoff
740
750
  sleep(delay_from_aborted(err) || backoff *= 1.3)
741
751
  # Create new transaction on the session and retry the block
@@ -748,10 +758,15 @@ module Google
748
758
  return nil if err.is_a? Rollback
749
759
  # Re-raise error.
750
760
  raise err
761
+ ensure
762
+ Thread.current[:transaction_id] = nil
751
763
  end
752
764
  end
753
765
  end
754
766
 
767
+ # rubocop:enable Metrics/AbcSize
768
+ # rubocop:enable Metrics/MethodLength
769
+
755
770
  ##
756
771
  # Creates a snapshot read-only transaction for reads that execute
757
772
  # atomically at a single logical point in time across columns, rows, and
@@ -811,14 +826,24 @@ module Google
811
826
  read_timestamp: read_timestamp,
812
827
  staleness: staleness,
813
828
  exact_staleness: exact_staleness
829
+
814
830
  ensure_service!
831
+ unless Thread.current[:transaction_id].nil?
832
+ fail "Nested snapshots are not allowed"
833
+ end
834
+
815
835
  @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?
836
+ begin
837
+ snp_grpc = @project.service.create_snapshot \
838
+ session.path, strong: strong,
839
+ timestamp: (timestamp || read_timestamp),
840
+ staleness: (staleness || exact_staleness)
841
+ Thread.current[:transaction_id] = snp_grpc.id
842
+ snp = Snapshot.from_grpc(snp_grpc, session)
843
+ yield snp if block_given?
844
+ ensure
845
+ Thread.current[:transaction_id] = nil
846
+ end
822
847
  end
823
848
  nil
824
849
  end
@@ -1011,7 +1036,8 @@ module Google
1011
1036
  end
1012
1037
 
1013
1038
  ##
1014
- # Retrieves the delay value from Google::Cloud::AbortedError
1039
+ # Retrieves the delay value from Google::Cloud::AbortedError or
1040
+ # GRPC::Aborted
1015
1041
  def delay_from_aborted err
1016
1042
  return nil if err.nil?
1017
1043
  if err.respond_to?(:metadata) && err.metadata["retryDelay"]
@@ -209,48 +209,6 @@ module Google
209
209
  end
210
210
  end
211
211
 
212
- ##
213
- # @private Convert an Object to a Google::Protobuf::Value.
214
- def object_to_value obj
215
- case obj
216
- when NilClass then Google::Protobuf::Value.new null_value:
217
- :NULL_VALUE
218
- when Numeric then Google::Protobuf::Value.new number_value: obj
219
- when String then Google::Protobuf::Value.new string_value: obj
220
- when TrueClass then Google::Protobuf::Value.new bool_value: true
221
- when FalseClass then Google::Protobuf::Value.new bool_value: false
222
- when Hash then Google::Protobuf::Value.new struct_value:
223
- hash_to_struct(obj)
224
- when Array then Google::Protobuf::Value.new list_value:
225
- Google::Protobuf::ListValue.new(values:
226
- obj.map { |o| object_to_value(o) })
227
- else
228
- # TODO: Could raise ArgumentError here, or convert to a string
229
- Google::Protobuf::Value.new string_value: obj.to_s
230
- end
231
- end
232
-
233
- ##
234
- # @private Convert a Google::Protobuf::Value to an Object.
235
- def value_to_object value
236
- # TODO: ArgumentError if struct is not a Google::Protobuf::Value
237
- if value.kind == :null_value
238
- nil
239
- elsif value.kind == :number_value
240
- value.number_value
241
- elsif value.kind == :string_value
242
- value.string_value
243
- elsif value.kind == :bool_value
244
- value.bool_value
245
- elsif value.kind == :struct_value
246
- struct_to_hash value.struct_value
247
- elsif value.kind == :list_value
248
- value.list_value.values.map { |v| value_to_object(v) }
249
- else
250
- nil # just in case
251
- end
252
- end
253
-
254
212
  def number_to_duration number
255
213
  return nil if number.nil?
256
214
 
@@ -72,6 +72,8 @@ module Google
72
72
  # job = spanner.create_database "my-instance",
73
73
  # "my-new-database"
74
74
  #
75
+ # job.done? #=> false
76
+ # job.reload!
75
77
  # job.done? #=> true
76
78
  # database = job.database
77
79
  #
@@ -31,8 +31,8 @@ module Google
31
31
  # db = spanner.client "my-instance", "my-database"
32
32
  #
33
33
  # db.transaction do |tx|
34
- # c.update "users", [{ id: 1, name: "Charlie", active: false }]
35
- # c.insert "users", [{ id: 2, name: "Harvey", active: true }]
34
+ # tx.update "users", [{ id: 1, name: "Charlie", active: false }]
35
+ # tx.insert "users", [{ id: 2, name: "Harvey", active: true }]
36
36
  #
37
37
  # if something_wrong?
38
38
  # # Rollback the transaction without passing on the error
@@ -279,7 +279,7 @@ module Google
279
279
  #
280
280
  # spanner = Google::Cloud::Spanner.new
281
281
  # instance = spanner.instance "my-instance"
282
- # database = instance.database "my-database" #=> nil
282
+ # database = instance.database "my-database" # nil
283
283
  #
284
284
  def database database_id
285
285
  ensure_service!
@@ -78,6 +78,8 @@ module Google
78
78
  # nodes: 5,
79
79
  # labels: { production: :env }
80
80
  #
81
+ # job.done? #=> false
82
+ # job.reload!
81
83
  # job.done? #=> true
82
84
  # instance = job.instance
83
85
  #
@@ -158,20 +158,6 @@ module Google
158
158
  roles[role_name] ||= []
159
159
  end
160
160
 
161
- ##
162
- # Returns a deep copy of the policy.
163
- #
164
- # @return [Policy]
165
- #
166
- def deep_dup
167
- dup.tap do |p|
168
- roles_dup = p.roles.each_with_object({}) do |(k, v), memo|
169
- memo[k] = v.dup rescue value
170
- end
171
- p.instance_variable_set "@roles", roles_dup
172
- end
173
- end
174
-
175
161
  ##
176
162
  # @private Convert the Policy to a Google::Iam::V1::Policy object.
177
163
  def to_grpc
@@ -33,7 +33,7 @@ module Google
33
33
  attr_accessor :all_sessions, :session_queue, :transaction_queue
34
34
 
35
35
  def initialize client, min: 10, max: 100, keepalive: 1800,
36
- write_ratio: 0.3, fail: true
36
+ write_ratio: 0.3, fail: true, threads: nil
37
37
  @client = client
38
38
  @min = min
39
39
  @max = max
@@ -42,6 +42,7 @@ module Google
42
42
  @write_ratio = 0 if write_ratio < 0
43
43
  @write_ratio = 1 if write_ratio > 1
44
44
  @fail = fail
45
+ @threads = threads || [2, Concurrent.processor_count * 2].max
45
46
 
46
47
  @mutex = Mutex.new
47
48
  @resource = ConditionVariable.new
@@ -86,11 +87,11 @@ module Google
86
87
  end
87
88
 
88
89
  def checkin_session session
89
- unless all_sessions.include? session
90
- fail ArgumentError, "Cannot checkin session"
91
- end
92
-
93
90
  @mutex.synchronize do
91
+ unless all_sessions.include? session
92
+ fail ArgumentError, "Cannot checkin session"
93
+ end
94
+
94
95
  session_queue.push session
95
96
 
96
97
  @resource.signal
@@ -144,11 +145,11 @@ module Google
144
145
  end
145
146
 
146
147
  def checkin_transaction tx
147
- unless all_sessions.include? tx.session
148
- fail ArgumentError, "Cannot checkin session"
149
- end
150
-
151
148
  @mutex.synchronize do
149
+ unless all_sessions.include? tx.session
150
+ fail ArgumentError, "Cannot checkin session"
151
+ end
152
+
152
153
  transaction_queue.push tx
153
154
 
154
155
  @resource.signal
@@ -215,10 +216,7 @@ module Google
215
216
 
216
217
  def init
217
218
  # init the thread pool
218
- @thread_pool = Concurrent::FixedThreadPool.new(
219
- [2, Concurrent.processor_count].max * 2,
220
- fallback_policy: :caller_runs
221
- )
219
+ @thread_pool = Concurrent::FixedThreadPool.new @threads
222
220
  # init the queues
223
221
  @new_sessions_in_process = @min.to_i
224
222
  @all_sessions = []
@@ -158,7 +158,7 @@ module Google
158
158
  # require "google/cloud/spanner"
159
159
  #
160
160
  # spanner = Google::Cloud::Spanner.new
161
- # instance = spanner.instance "non-existing" #=> nil
161
+ # instance = spanner.instance "non-existing" # nil
162
162
  #
163
163
  def instance instance_id
164
164
  ensure_service!
@@ -288,7 +288,7 @@ module Google
288
288
  # require "google/cloud/spanner"
289
289
  #
290
290
  # spanner = Google::Cloud::Spanner.new
291
- # config = spanner.instance_config "non-existing" #=> nil
291
+ # config = spanner.instance_config "non-existing" # nil
292
292
  #
293
293
  def instance_config instance_config_id
294
294
  ensure_service!
@@ -355,7 +355,7 @@ module Google
355
355
  # require "google/cloud/spanner"
356
356
  #
357
357
  # spanner = Google::Cloud::Spanner.new
358
- # database = spanner.database "my-instance", "my-database" #=> nil
358
+ # database = spanner.database "my-instance", "my-database" # nil
359
359
  #
360
360
  def database instance_id, database_id
361
361
  ensure_service!
@@ -432,6 +432,8 @@ module Google
432
432
  # {SessionLimitError} when the client has allocated the `max` number
433
433
  # of sessions. When `false` the client blocks until a session
434
434
  # becomes available. The default is `true`.
435
+ # * `:threads` (Integer) The number of threads in the thread pool. The
436
+ # default is twice the number of available CPUs.
435
437
  #
436
438
  # @return [Client] The newly created client.
437
439
  #
@@ -471,7 +473,8 @@ module Google
471
473
 
472
474
  def valid_session_pool_options opts = {}
473
475
  { min: opts[:min], max: opts[:max], keepalive: opts[:keepalive],
474
- write_ratio: opts[:write_ratio], fail: opts[:fail]
476
+ write_ratio: opts[:write_ratio], fail: opts[:fail],
477
+ threads: opts[:threads]
475
478
  }.delete_if { |_k, v| v.nil? }
476
479
  end
477
480
  end
@@ -36,7 +36,7 @@ module Google
36
36
  #
37
37
  # results = db.execute "SELECT * FROM users"
38
38
  #
39
- # results.types.each do |name, type|
39
+ # results.fields.pairs.each do |name, type|
40
40
  # puts "Column #{name} is type {type}"
41
41
  # end
42
42
  #
@@ -272,24 +272,6 @@ module Google
272
272
  end
273
273
  end
274
274
 
275
- def execute_sql session_name, sql, transaction: nil, params: nil
276
- input_params = nil
277
- input_param_types = nil
278
- unless params.nil?
279
- input_param_pairs = Convert.to_query_params params
280
- input_params = Google::Protobuf::Struct.new(
281
- fields: Hash[input_param_pairs.map { |k, v| [k, v.first] }])
282
- input_param_types = Hash[
283
- input_param_pairs.map { |k, v| [k, v.last] }]
284
- end
285
- opts = default_options_from_session session_name
286
- execute do
287
- service.execute_sql \
288
- session_name, sql, transaction: transaction, params: input_params,
289
- param_types: input_param_types, options: opts
290
- end
291
- end
292
-
293
275
  def streaming_execute_sql session_name, sql, transaction: nil,
294
276
  params: nil, types: nil, resume_token: nil
295
277
  input_params = nil
@@ -310,18 +292,6 @@ module Google
310
292
  end
311
293
  end
312
294
 
313
- def read_table session_name, table_name, columns, keys: nil, index: nil,
314
- transaction: nil, limit: nil
315
- columns.map!(&:to_s)
316
- opts = default_options_from_session session_name
317
- execute do
318
- service.read \
319
- session_name, table_name, columns, key_set(keys),
320
- transaction: transaction, index: index, limit: limit,
321
- options: opts
322
- end
323
- end
324
-
325
295
  def streaming_read_table session_name, table_name, columns, keys: nil,
326
296
  index: nil, transaction: nil, limit: nil,
327
297
  resume_token: nil
@@ -38,11 +38,38 @@ module Google
38
38
  # db = spanner.client "my-instance", "my-database"
39
39
  #
40
40
  # db.transaction do |tx|
41
- # results = tx.execute "SELECT * FROM users"
41
+ # # Read the second album budget.
42
+ # second_album_result = tx.read "Albums", ["marketing_budget"],
43
+ # keys: [[2, 2]], limit: 1
44
+ # second_album_row = second_album_result.rows.first
45
+ # second_album_budget = second_album_row.values.first
42
46
  #
43
- # results.rows.each do |row|
44
- # puts "User #{row[:id]} is #{row[:name]}"
47
+ # transfer_amount = 200000
48
+ #
49
+ # if second_album_budget < 300000
50
+ # # Raising an exception will automatically roll back the
51
+ # # transaction.
52
+ # raise "The second album doesn't have enough funds to transfer"
45
53
  # end
54
+ #
55
+ # # Read the first album's budget.
56
+ # first_album_result = tx.read "Albums", ["marketing_budget"],
57
+ # keys: [[1, 1]], limit: 1
58
+ # first_album_row = first_album_result.rows.first
59
+ # first_album_budget = first_album_row.values.first
60
+ #
61
+ # # Update the budgets.
62
+ # second_album_budget -= transfer_amount
63
+ # first_album_budget += transfer_amount
64
+ # puts "Setting first album's budget to #{first_album_budget} and " \
65
+ # "the second album's budget to #{second_album_budget}."
66
+ #
67
+ # # Update the rows.
68
+ # rows = [
69
+ # {singer_id: 1, album_id: 1, marketing_budget: first_album_budget},
70
+ # {singer_id: 2, album_id: 2, marketing_budget: second_album_budget}
71
+ # ]
72
+ # tx.update "Albums", rows
46
73
  # end
47
74
  #
48
75
  class Transaction
@@ -423,7 +450,7 @@ module Google
423
450
  # db = spanner.client "my-instance", "my-database"
424
451
  #
425
452
  # db.transaction do |tx|
426
- # users_types = rx.fields_for "users"
453
+ # users_types = tx.fields_for "users"
427
454
  # tx.insert "users", [{ id: 1, name: "Charlie", active: false },
428
455
  # { id: 2, name: "Harvey", active: true }],
429
456
  # types: users_types
@@ -6,9 +6,7 @@
6
6
  "DEADLINE_EXCEEDED",
7
7
  "UNAVAILABLE"
8
8
  ],
9
- "non_idempotent": [
10
- "UNAVAILABLE"
11
- ]
9
+ "non_idempotent": []
12
10
  },
13
11
  "retry_params": {
14
12
  "default": {
@@ -16,7 +16,7 @@
16
16
  module Google
17
17
  module Cloud
18
18
  module Spanner
19
- VERSION = "0.21.0"
19
+ VERSION = "0.22.0"
20
20
  end
21
21
  end
22
22
  end
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: 0.21.0
4
+ version: 0.22.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: 2017-06-08 00:00:00.000000000 Z
12
+ date: 2017-07-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: google-cloud-core
@@ -197,16 +197,16 @@ dependencies:
197
197
  name: yard-doctest
198
198
  requirement: !ruby/object:Gem::Requirement
199
199
  requirements:
200
- - - "~>"
200
+ - - "<="
201
201
  - !ruby/object:Gem::Version
202
- version: 0.1.6
202
+ version: 0.1.8
203
203
  type: :development
204
204
  prerelease: false
205
205
  version_requirements: !ruby/object:Gem::Requirement
206
206
  requirements:
207
- - - "~>"
207
+ - - "<="
208
208
  - !ruby/object:Gem::Version
209
- version: 0.1.6
209
+ version: 0.1.8
210
210
  description: google-cloud-spanner is the official library for Google Cloud Spanner
211
211
  API.
212
212
  email: