google-cloud-spanner 1.2.0 → 1.3.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/google-cloud-spanner.rb +25 -0
  3. data/lib/google/cloud/spanner.rb +71 -11
  4. data/lib/google/cloud/spanner/batch_client.rb +355 -0
  5. data/lib/google/cloud/spanner/batch_snapshot.rb +588 -0
  6. data/lib/google/cloud/spanner/client.rb +27 -20
  7. data/lib/google/cloud/spanner/commit.rb +6 -5
  8. data/lib/google/cloud/spanner/convert.rb +39 -0
  9. data/lib/google/cloud/spanner/credentials.rb +7 -7
  10. data/lib/google/cloud/spanner/data.rb +5 -5
  11. data/lib/google/cloud/spanner/database.rb +13 -7
  12. data/lib/google/cloud/spanner/database/job.rb +7 -2
  13. data/lib/google/cloud/spanner/database/list.rb +1 -1
  14. data/lib/google/cloud/spanner/fields.rb +16 -12
  15. data/lib/google/cloud/spanner/instance.rb +25 -13
  16. data/lib/google/cloud/spanner/instance/config.rb +2 -2
  17. data/lib/google/cloud/spanner/instance/config/list.rb +1 -1
  18. data/lib/google/cloud/spanner/instance/job.rb +7 -2
  19. data/lib/google/cloud/spanner/instance/list.rb +1 -1
  20. data/lib/google/cloud/spanner/partition.rb +208 -0
  21. data/lib/google/cloud/spanner/pool.rb +6 -6
  22. data/lib/google/cloud/spanner/project.rb +59 -16
  23. data/lib/google/cloud/spanner/results.rb +12 -5
  24. data/lib/google/cloud/spanner/service.rb +85 -61
  25. data/lib/google/cloud/spanner/session.rb +45 -9
  26. data/lib/google/cloud/spanner/snapshot.rb +10 -2
  27. data/lib/google/cloud/spanner/status.rb +6 -5
  28. data/lib/google/cloud/spanner/transaction.rb +11 -3
  29. data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +1 -1
  30. data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +1 -1
  31. data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +1 -1
  32. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +1 -1
  33. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +1 -1
  34. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +1 -1
  35. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +1 -1
  36. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +138 -6
  37. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +1 -1
  38. data/lib/google/cloud/spanner/v1/doc/overview.rb +6 -5
  39. data/lib/google/cloud/spanner/v1/spanner_client.rb +257 -33
  40. data/lib/google/cloud/spanner/v1/spanner_client_config.json +10 -0
  41. data/lib/google/cloud/spanner/version.rb +1 -1
  42. data/lib/google/spanner/v1/spanner_pb.rb +35 -0
  43. data/lib/google/spanner/v1/spanner_services_pb.rb +20 -2
  44. metadata +12 -9
@@ -53,7 +53,11 @@ module Google
53
53
  # job.reload! # API call
54
54
  # job.done? #=> true
55
55
  #
56
- # instance = spanner.instance "my-new-instance"
56
+ # if job.error?
57
+ # status = job.error
58
+ # else
59
+ # instance = job.instance
60
+ # end
57
61
  #
58
62
  class Instance
59
63
  ##
@@ -93,7 +97,7 @@ module Google
93
97
  def name
94
98
  @grpc.display_name
95
99
  end
96
- alias_method :display_name, :name
100
+ alias display_name name
97
101
 
98
102
  ##
99
103
  # The instance configuration resource.
@@ -112,7 +116,7 @@ module Google
112
116
  def name= display_name
113
117
  @grpc.display_name = display_name
114
118
  end
115
- alias_method :display_name=, :name=
119
+ alias display_name= name=
116
120
 
117
121
  ##
118
122
  # The number of nodes allocated to this instance.
@@ -120,7 +124,7 @@ module Google
120
124
  def nodes
121
125
  @grpc.node_count
122
126
  end
123
- alias_method :node_count, :nodes
127
+ alias node_count nodes
124
128
 
125
129
  ##
126
130
  # Updates the number of nodes allocated to this instance.
@@ -128,7 +132,7 @@ module Google
128
132
  def nodes= nodes
129
133
  @grpc.node_count = nodes
130
134
  end
131
- alias_method :node_count=, :nodes=
135
+ alias node_count= nodes=
132
136
 
133
137
  ##
134
138
  # The current instance state. Possible values are `:CREATING` and
@@ -180,14 +184,15 @@ module Google
180
184
  def labels= labels
181
185
  @grpc.labels = Google::Protobuf::Map.new(
182
186
  :string, :string,
183
- Hash[labels.map { |k, v| [String(k), String(v)] }])
187
+ Hash[labels.map { |k, v| [String(k), String(v)] }]
188
+ )
184
189
  end
185
190
 
186
191
  def save
187
192
  job_grpc = service.update_instance @grpc
188
193
  Instance::Job.from_grpc job_grpc, service
189
194
  end
190
- alias_method :update, :save
195
+ alias update save
191
196
 
192
197
  ##
193
198
  # Permanently deletes the instance.
@@ -316,7 +321,12 @@ module Google
316
321
  # job.done? #=> false
317
322
  # job.reload! # API call
318
323
  # job.done? #=> true
319
- # database = job.database
324
+ #
325
+ # if job.error?
326
+ # status = job.error
327
+ # else
328
+ # database = job.database
329
+ # end
320
330
  #
321
331
  def create_database database_id, statements: []
322
332
  grpc = service.create_database instance_id, database_id,
@@ -364,7 +374,7 @@ module Google
364
374
  policy = Policy.from_grpc grpc
365
375
  return policy unless block_given?
366
376
  yield policy
367
- self.policy = policy
377
+ update_policy policy
368
378
  end
369
379
 
370
380
  ##
@@ -394,13 +404,14 @@ module Google
394
404
  #
395
405
  # policy.add "roles/owner", "user:owner@example.com"
396
406
  #
397
- # instance.policy = policy # API call
407
+ # instance.update_policy policy # API call
398
408
  #
399
- def policy= new_policy
409
+ def update_policy new_policy
400
410
  ensure_service!
401
411
  grpc = service.set_instance_policy path, new_policy.to_grpc
402
412
  Policy.from_grpc grpc
403
413
  end
414
+ alias policy= update_policy
404
415
 
405
416
  ##
406
417
  # Tests the specified permissions against the [Cloud
@@ -456,13 +467,14 @@ module Google
456
467
  # @private Raise an error unless an active connection to the service is
457
468
  # available.
458
469
  def ensure_service!
459
- fail "Must have active connection to service" unless service
470
+ raise "Must have active connection to service" unless service
460
471
  end
461
472
 
462
473
  def instance_config_path name
463
474
  return name if name.to_s.include? "/"
464
475
  Admin::Instance::V1::InstanceAdminClient.instance_config_path(
465
- project, name.to_s)
476
+ project, name.to_s
477
+ )
466
478
  end
467
479
  end
468
480
  end
@@ -73,7 +73,7 @@ module Google
73
73
  def name
74
74
  @grpc.display_name
75
75
  end
76
- alias_method :display_name, :name
76
+ alias display_name name
77
77
 
78
78
  ##
79
79
  # @private Creates a new Instance::Config instance from a
@@ -88,7 +88,7 @@ module Google
88
88
  # @private Raise an error unless an active connection to the service
89
89
  # is available.
90
90
  def ensure_service!
91
- fail "Must have active connection to service" unless service
91
+ raise "Must have active connection to service" unless service
92
92
  end
93
93
  end
94
94
  end
@@ -161,7 +161,7 @@ module Google
161
161
  ##
162
162
  # Raise an error unless an active service is available.
163
163
  def ensure_service!
164
- fail "Must have active connection" unless @service
164
+ raise "Must have active connection" unless @service
165
165
  end
166
166
  end
167
167
  end
@@ -44,7 +44,12 @@ module Google
44
44
  # job.done? #=> false
45
45
  # job.reload! # API call
46
46
  # job.done? #=> true
47
- # instance = job.instance
47
+ #
48
+ # if job.error?
49
+ # status = job.error
50
+ # else
51
+ # instance = job.instance
52
+ # end
48
53
  #
49
54
  class Job
50
55
  ##
@@ -187,7 +192,7 @@ module Google
187
192
  @grpc.reload!
188
193
  self
189
194
  end
190
- alias_method :refresh!, :reload!
195
+ alias refresh! reload!
191
196
 
192
197
  ##
193
198
  # Reloads the job until the operation is complete. The delay between
@@ -158,7 +158,7 @@ module Google
158
158
  ##
159
159
  # Raise an error unless an active service is available.
160
160
  def ensure_service!
161
- fail "Must have active connection" unless @service
161
+ raise "Must have active connection" unless @service
162
162
  end
163
163
  end
164
164
  end
@@ -0,0 +1,208 @@
1
+ # Copyright 2018 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "json"
17
+ require "base64"
18
+
19
+ module Google
20
+ module Cloud
21
+ module Spanner
22
+ ##
23
+ # # Partition
24
+ #
25
+ # Defines the segments of data to be read in a batch read/query context. A
26
+ # Partition instance can be serialized and used across several different
27
+ # machines or processes.
28
+ #
29
+ # See {BatchSnapshot#partition_read}, {BatchSnapshot#partition_query}, and
30
+ # {BatchSnapshot#execute_partition}.
31
+ #
32
+ # @example
33
+ # require "google/cloud/spanner"
34
+ #
35
+ # spanner = Google::Cloud::Spanner.new
36
+ #
37
+ # batch_client = spanner.batch_client "my-instance", "my-database"
38
+ #
39
+ # batch_snapshot = batch_client.batch_snapshot
40
+ # partitions = batch_snapshot.partition_read "users", [:id, :name]
41
+ #
42
+ # partition = partitions.first
43
+ #
44
+ # results = batch_snapshot.execute_partition partition
45
+ #
46
+ class Partition
47
+ # @ private
48
+ attr_reader :execute, :read
49
+
50
+ ##
51
+ # @private Creates a Partition object.
52
+ def initialize; end
53
+
54
+ ##
55
+ # Whether the partition was created for an execute/query operation.
56
+ # @return [Boolean]
57
+ def execute?
58
+ !@execute.nil?
59
+ end
60
+
61
+ ##
62
+ # Whether the partition was created for a read operation.
63
+ # @return [Boolean]
64
+ def read?
65
+ !@read.nil?
66
+ end
67
+
68
+ ##
69
+ # @private
70
+ # Whether the partition does not have an execute or read operation.
71
+ # @return [Boolean]
72
+ def empty?
73
+ @execute.nil? && @read.nil?
74
+ end
75
+
76
+ ##
77
+ # @private
78
+ # Converts the the batch partition object to a Hash ready for
79
+ # serialization.
80
+ #
81
+ # @return [Hash] A hash containing a representation of the batch
82
+ # partition object.
83
+ #
84
+ def to_h
85
+ {}.tap do |h|
86
+ h[:execute] = Base64.strict_encode64(@execute.to_proto) if @execute
87
+ h[:read] = Base64.strict_encode64(@read.to_proto) if @read
88
+ end
89
+ end
90
+
91
+ ##
92
+ # Serializes the batch partition object so it can be recreated on
93
+ # another process. See {Partition.load} and
94
+ # {BatchClient#load_partition}.
95
+ #
96
+ # @return [String] The serialized representation of the batch partition.
97
+ #
98
+ # @example
99
+ # require "google/cloud/spanner"
100
+ #
101
+ # spanner = Google::Cloud::Spanner.new
102
+ #
103
+ # batch_client = spanner.batch_client "my-instance", "my-database"
104
+ #
105
+ # batch_snapshot = batch_client.batch_snapshot
106
+ #
107
+ # partitions = batch_snapshot.partition_read "users", [:id, :name]
108
+ #
109
+ # partition = partitions.first
110
+ #
111
+ # serialized_snapshot = batch_snapshot.dump
112
+ # serialized_partition = partition.dump
113
+ #
114
+ # # In a separate process
115
+ # new_batch_snapshot = batch_client.load_batch_snapshot \
116
+ # serialized_snapshot
117
+ #
118
+ # new_partition = batch_client.load_partition \
119
+ # serialized_partition
120
+ #
121
+ # results = new_batch_snapshot.execute_partition \
122
+ # new_partition
123
+ #
124
+ def dump
125
+ JSON.dump to_h
126
+ end
127
+ alias serialize dump
128
+
129
+ ##
130
+ # Returns a {Partition} from a serialized representation.
131
+ #
132
+ # @param [String] data The serialized representation of an existing
133
+ # batch partition. See {Partition#dump}.
134
+ #
135
+ # @return [Google::Cloud::Spanner::Partition]
136
+ #
137
+ # @example
138
+ # require "google/cloud/spanner"
139
+ #
140
+ # spanner = Google::Cloud::Spanner.new
141
+ #
142
+ # batch_client = spanner.batch_client "my-instance", "my-database"
143
+ #
144
+ # batch_snapshot = batch_client.batch_snapshot
145
+ #
146
+ # partitions = batch_snapshot.partition_read "users", [:id, :name]
147
+ #
148
+ # partition = partitions.first
149
+ #
150
+ # serialized_snapshot = batch_snapshot.dump
151
+ # serialized_partition = partition.dump
152
+ #
153
+ # # In a separate process
154
+ # new_batch_snapshot = batch_client.load_batch_snapshot \
155
+ # serialized_snapshot
156
+ #
157
+ # new_partition = Google::Cloud::Spanner::Partition.load \
158
+ # serialized_partition
159
+ #
160
+ # results = new_batch_snapshot.execute_partition \
161
+ # new_partition
162
+ #
163
+ def self.load data
164
+ data = JSON.parse data, symbolize_names: true unless data.is_a? Hash
165
+
166
+ # TODO: raise if hash[:execute].nil? && hash[:read].nil?
167
+ new.tap do |p|
168
+ if data[:execute]
169
+ execute_grpc = Google::Spanner::V1::ExecuteSqlRequest.decode \
170
+ Base64.decode64(data[:execute])
171
+ p.instance_variable_set :@execute, execute_grpc
172
+ end
173
+ if data[:read]
174
+ read_grpc = Google::Spanner::V1::ReadRequest.decode \
175
+ Base64.decode64(data[:read])
176
+ p.instance_variable_set :@read, read_grpc
177
+ end
178
+ end
179
+ end
180
+
181
+ # @private
182
+ def inspect
183
+ status = "empty"
184
+ status = "execute" if execute?
185
+ status = "read" if read?
186
+ "#<#{self.class.name} #{status}>"
187
+ end
188
+
189
+ ##
190
+ # @private New Partition from a Google::Spanner::V1::ExecuteSqlRequest
191
+ # object.
192
+ def self.from_execute_grpc grpc
193
+ new.tap do |p|
194
+ p.instance_variable_set :@execute, grpc
195
+ end
196
+ end
197
+
198
+ ##
199
+ # @private New Partition from a Google::Spanner::V1::ReadRequest object.
200
+ def self.from_read_grpc grpc
201
+ new.tap do |p|
202
+ p.instance_variable_set :@read, grpc
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -64,7 +64,7 @@ module Google
64
64
  action = nil
65
65
  @mutex.synchronize do
66
66
  loop do
67
- fail ClientClosedError if @closed
67
+ raise ClientClosedError if @closed
68
68
 
69
69
  read_session = session_queue.shift
70
70
  return read_session if read_session
@@ -77,7 +77,7 @@ module Google
77
77
  break
78
78
  end
79
79
 
80
- fail SessionLimitError if @fail
80
+ raise SessionLimitError if @fail
81
81
 
82
82
  @resource.wait @mutex
83
83
  end
@@ -89,7 +89,7 @@ module Google
89
89
  def checkin_session session
90
90
  @mutex.synchronize do
91
91
  unless all_sessions.include? session
92
- fail ArgumentError, "Cannot checkin session"
92
+ raise ArgumentError, "Cannot checkin session"
93
93
  end
94
94
 
95
95
  session_queue.push session
@@ -117,7 +117,7 @@ module Google
117
117
  action = nil
118
118
  @mutex.synchronize do
119
119
  loop do
120
- fail ClientClosedError if @closed
120
+ raise ClientClosedError if @closed
121
121
 
122
122
  write_transaction = transaction_queue.shift
123
123
  return write_transaction if write_transaction
@@ -133,7 +133,7 @@ module Google
133
133
  break
134
134
  end
135
135
 
136
- fail SessionLimitError if @fail
136
+ raise SessionLimitError if @fail
137
137
 
138
138
  @resource.wait @mutex
139
139
  end
@@ -147,7 +147,7 @@ module Google
147
147
  def checkin_transaction tx
148
148
  @mutex.synchronize do
149
149
  unless all_sessions.include? tx.session
150
- fail ArgumentError, "Cannot checkin session"
150
+ raise ArgumentError, "Cannot checkin session"
151
151
  end
152
152
 
153
153
  transaction_queue.push tx
@@ -14,9 +14,9 @@
14
14
 
15
15
 
16
16
  require "google/cloud/spanner/errors"
17
- require "google/cloud/env"
18
17
  require "google/cloud/spanner/service"
19
18
  require "google/cloud/spanner/client"
19
+ require "google/cloud/spanner/batch_client"
20
20
  require "google/cloud/spanner/instance"
21
21
  require "google/cloud/spanner/database"
22
22
  require "google/cloud/spanner/range"
@@ -92,16 +92,7 @@ module Google
92
92
  def project_id
93
93
  service.project
94
94
  end
95
- alias_method :project, :project_id
96
-
97
- ##
98
- # @private Default project.
99
- def self.default_project_id
100
- ENV["SPANNER_PROJECT"] ||
101
- ENV["GOOGLE_CLOUD_PROJECT"] ||
102
- ENV["GCLOUD_PROJECT"] ||
103
- Google::Cloud.env.project_id
104
- end
95
+ alias project project_id
105
96
 
106
97
  ##
107
98
  # Retrieves the list of Cloud Spanner instances for the project.
@@ -218,7 +209,12 @@ module Google
218
209
  # job.done? #=> false
219
210
  # job.reload! # API call
220
211
  # job.done? #=> true
221
- # instance = job.instance
212
+ #
213
+ # if job.error?
214
+ # status = job.error
215
+ # else
216
+ # instance = job.instance
217
+ # end
222
218
  #
223
219
  def create_instance instance_id, name: nil, config: nil, nodes: nil,
224
220
  labels: nil
@@ -396,7 +392,12 @@ module Google
396
392
  # job.done? #=> false
397
393
  # job.reload! # API call
398
394
  # job.done? #=> true
399
- # database = job.database
395
+ #
396
+ # if job.error?
397
+ # status = job.error
398
+ # else
399
+ # database = job.database
400
+ # end
400
401
  #
401
402
  def create_database instance_id, database_id, statements: []
402
403
  grpc = service.create_database instance_id, database_id,
@@ -457,22 +458,64 @@ module Google
457
458
  valid_session_pool_options(pool)
458
459
  end
459
460
 
461
+ ##
462
+ # Creates a Cloud Spanner batch client. A batch client is used to read
463
+ # data across multiple machines or processes.
464
+ #
465
+ # @param [String] instance_id The unique identifier for the instance.
466
+ # Required.
467
+ # @param [String] database_id The unique identifier for the database.
468
+ # Required.
469
+ #
470
+ # @return [Client] The newly created client.
471
+ #
472
+ # @example
473
+ # require "google/cloud/spanner"
474
+ #
475
+ # spanner = Google::Cloud::Spanner.new
476
+ #
477
+ # batch_client = spanner.batch_client "my-instance", "my-database"
478
+ #
479
+ # batch_snapshot = batch_client.batch_snapshot
480
+ # serialized_snapshot = batch_snapshot.dump
481
+ #
482
+ # partitions = batch_snapshot.partition_read "users", [:id, :name]
483
+ #
484
+ # partition = partitions.first
485
+ # serialized_partition = partition.dump
486
+ #
487
+ # # In a separate process
488
+ # new_batch_snapshot = batch_client.load_batch_snapshot \
489
+ # serialized_snapshot
490
+ #
491
+ # new_partition = batch_client.load_partition \
492
+ # serialized_partition
493
+ #
494
+ # results = new_batch_snapshot.execute_partition \
495
+ # new_partition
496
+ #
497
+ def batch_client instance_id, database_id
498
+ BatchClient.new self, instance_id, database_id
499
+ end
500
+
460
501
  protected
461
502
 
462
503
  ##
463
504
  # @private Raise an error unless an active connection to the service is
464
505
  # available.
465
506
  def ensure_service!
466
- fail "Must have active connection to service" unless service
507
+ raise "Must have active connection to service" unless service
467
508
  end
468
509
 
469
510
  def database_path instance_id, database_id
470
511
  Admin::Database::V1::DatabaseAdminClient.database_path(
471
- project, instance_id, database_id)
512
+ project, instance_id, database_id
513
+ )
472
514
  end
473
515
 
474
516
  def valid_session_pool_options opts = {}
475
- { min: opts[:min], max: opts[:max], keepalive: opts[:keepalive],
517
+ {
518
+ min: opts[:min], max: opts[:max], keepalive: opts[:keepalive],
476
519
  write_ratio: opts[:write_ratio], fail: opts[:fail],
477
520
  threads: opts[:threads]
478
521
  }.delete_if { |_k, v| v.nil? }