google-cloud-spanner 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7eb1575c3244eb459d8379345e2dea6f19c925bbeb526ff969aa07a34428c94
4
- data.tar.gz: 9c8c0b2d99074ec99ff38125ccbf89713f0d7c5441445650b96b5a969b09629a
3
+ metadata.gz: 01dd371ff37296529b691fd13961d7e7681e63480a88202b9e958a8c28bc480b
4
+ data.tar.gz: 9915fdf52be7508c55380b193a9586a35c702617080d3bf93a9f53b37b62be84
5
5
  SHA512:
6
- metadata.gz: ba900cc5a539d55baead3d8825472b05b40ab19adc3062dfb93c7cf1713805b9619c2cbd7b115f3e2e61f79371e4c53eb8a225f8cd1f7a3dc1c30f72e1590cc7
7
- data.tar.gz: 1639fd79b6bd30af238b2d002852bec94e4f29aa581594758169180e15e6b335f86c5a6cac1fa9a13a7fef02edc105ea46eb4867a02c59bf0a069c142d8799e2
6
+ metadata.gz: 0a34c4eeaaf84abe4d311350a155b22190c7dbc426a48719df4ad553692d841aa1a2d3a72d7f043b2fef7b3fa87a81cda69f965f5eef58a8ce17a77bf186a48e
7
+ data.tar.gz: 36062397079b8960916575b9188c0ad10f376d27b7d194a20e7f4c6e69f29eeb018b499576209bf1b2d2985725233db5de3e233ff840b5c93790d15b9b4204ab
@@ -1,5 +1,16 @@
1
1
  # Release History
2
2
 
3
+ ### 2.2.0 / 2020-09-15
4
+
5
+ #### Features
6
+
7
+ * quota_project can be set via library configuration
8
+ * Support numeric type.
9
+
10
+ #### Bug Fixes
11
+
12
+ * retry or resume eos and rst_stream errors
13
+
3
14
  ### 2.1.0 / 2020-08-05
4
15
 
5
16
  #### Features
@@ -174,6 +174,7 @@ Google::Cloud.configure.add_config! :spanner do |config|
174
174
  allow_nil: true
175
175
  config.add_alias! :keyfile, :credentials
176
176
  config.add_field! :scope, default_scopes, match: [String, Array]
177
+ config.add_field! :quota_project, nil, match: String
177
178
  config.add_field! :timeout, nil, match: Integer
178
179
  config.add_field! :endpoint, "spanner.googleapis.com", match: String
179
180
  config.add_field! :emulator_host, default_emulator, match: String, allow_nil: true
@@ -17,6 +17,7 @@ require "time"
17
17
  require "date"
18
18
  require "stringio"
19
19
  require "base64"
20
+ require "bigdecimal"
20
21
  require "google/cloud/spanner/data"
21
22
 
22
23
  module Google
@@ -67,6 +68,9 @@ module Google
67
68
  Google::Protobuf::Value.new bool_value: false
68
69
  when Integer
69
70
  Google::Protobuf::Value.new string_value: obj.to_s
71
+ # BigDecimal must be put before Numeric.
72
+ when BigDecimal
73
+ Google::Protobuf::Value.new string_value: obj.to_s("F")
70
74
  when Numeric
71
75
  if obj == Float::INFINITY
72
76
  Google::Protobuf::Value.new string_value: "Infinity"
@@ -125,6 +129,9 @@ module Google
125
129
  :BOOL
126
130
  when Integer
127
131
  :INT64
132
+ # BigDecimal must be put before Numeric.
133
+ when BigDecimal
134
+ :NUMERIC
128
135
  when Numeric
129
136
  :FLOAT64
130
137
  when Time
@@ -214,6 +221,8 @@ module Google
214
221
  end
215
222
  when :STRUCT
216
223
  Data.from_grpc value.list_value.values, type.struct_type.fields
224
+ when :NUMERIC
225
+ BigDecimal value.string_value
217
226
  end
218
227
  end
219
228
 
@@ -41,6 +41,8 @@ module Google
41
41
  # end
42
42
  #
43
43
  class Results
44
+ RST_STREAM_INTERNAL_ERROR = "Received RST_STREAM".freeze
45
+ EOS_INTERNAL_ERROR = "Received unexpected EOS on DATA frame from server".freeze
44
46
  ##
45
47
  # The read timestamp chosen for single-use snapshots (read-only
46
48
  # transactions).
@@ -107,12 +109,24 @@ module Google
107
109
  buffer_upper_bound = 10
108
110
  chunked_value = nil
109
111
  resume_token = nil
112
+ should_resume_request = false
113
+ should_retry_request = false
110
114
 
111
115
  # Cannot call Enumerator#each because it won't return the first
112
116
  # value that was already identified when calling Enumerator#peek.
113
117
  # Iterate only using Enumerator#next and break on StopIteration.
114
118
  loop do
115
119
  begin
120
+ if should_resume_request
121
+ @enum = resume_request(resume_token)
122
+ buffered_responses = []
123
+ should_resume_request = false
124
+ elsif should_retry_request
125
+ @enum = retry_request()
126
+ buffered_responses = []
127
+ should_retry_request = false
128
+ end
129
+
116
130
  grpc = @enum.next
117
131
  # metadata should be set before the first iteration...
118
132
  @metadata ||= grpc.metadata
@@ -143,28 +157,36 @@ module Google
143
157
  # Flush the buffered responses now that they are all handled
144
158
  buffered_responses = []
145
159
  end
146
- rescue GRPC::Cancelled, GRPC::DeadlineExceeded, GRPC::Internal,
147
- GRPC::ResourceExhausted, GRPC::Unauthenticated,
148
- GRPC::Unavailable, GRPC::Core::CallError => err
149
- if resume_token.nil? || resume_token.empty?
150
- # Re-raise if the resume_token is not a valid value.
151
- # This can happen if the buffer was flushed.
152
- raise Google::Cloud::Error.from_error(err)
153
- end
160
+ # TODO: once the generated client throws only Google Cloud errors, remove
161
+ # the GRPC errors from the rescue block
162
+ rescue GRPC::Aborted,
163
+ GRPC::Cancelled,
164
+ GRPC::DeadlineExceeded,
165
+ GRPC::Internal,
166
+ GRPC::ResourceExhausted,
167
+ GRPC::Unauthenticated,
168
+ GRPC::Unavailable,
169
+ GRPC::Core::CallError,
170
+ Google::Cloud::AbortedError,
171
+ Google::Cloud::CanceledError,
172
+ Google::Cloud::DeadlineExceededError,
173
+ Google::Cloud::InternalError,
174
+ Google::Cloud::ResourceExhaustedError,
175
+ Google::Cloud::UnauthenticatedError,
176
+ Google::Cloud::UnavailableError => err
154
177
 
155
- # Resume the stream from the last known resume_token
156
- if @execute_query_options
157
- @enum = @service.execute_streaming_sql \
158
- @session_path, @sql,
159
- @execute_query_options.merge(resume_token: resume_token)
178
+ if resumable?(resume_token)
179
+ should_resume_request = true
180
+ elsif retryable?(err)
181
+ should_retry_request = true
182
+ elsif err.is_a?(Google::Cloud::Error)
183
+ raise err
160
184
  else
161
- @enum = @service.streaming_read_table \
162
- @session_path, @table, @columns,
163
- @read_options.merge(resume_token: resume_token)
185
+ raise Google::Cloud::Error.from_error(err)
164
186
  end
165
187
 
166
- # Flush the buffered responses to reset to the resume_token
167
- buffered_responses = []
188
+ # TODO: once the generated client throws only Google Cloud errors, remove
189
+ # this rescue block (for GRPC::BadStatus)
168
190
  rescue GRPC::BadStatus => err
169
191
  raise Google::Cloud::Error.from_error(err)
170
192
  rescue StopIteration
@@ -198,6 +220,60 @@ module Google
198
220
 
199
221
  # rubocop:enable all
200
222
 
223
+ ##
224
+ # @private
225
+ # Checks if a request can be resumed by inspecting the resume token
226
+ def resumable? resume_token
227
+ resume_token && !resume_token.empty?
228
+ end
229
+
230
+ ##
231
+ # @private
232
+ # Checks if a request can be retried. This is based on the error returned.
233
+ # Retryable errors are:
234
+ # - Unavailable error
235
+ # - Internal EOS error
236
+ # - Internal RST_STREAM error
237
+ def retryable? err
238
+ err.instance_of?(Google::Cloud::UnavailableError) ||
239
+ err.instance_of?(GRPC::Unavailable) ||
240
+ (err.instance_of?(Google::Cloud::InternalError) && err.message.include?(EOS_INTERNAL_ERROR)) ||
241
+ (err.instance_of?(GRPC::Internal) && err.details.include?(EOS_INTERNAL_ERROR)) ||
242
+ (err.instance_of?(Google::Cloud::InternalError) && err.message.include?(RST_STREAM_INTERNAL_ERROR)) ||
243
+ (err.instance_of?(GRPC::Internal) && err.details.include?(RST_STREAM_INTERNAL_ERROR))
244
+ end
245
+
246
+ ##
247
+ # @private
248
+ # Resumes a request, by re-executing it with a resume token.
249
+ def resume_request resume_token
250
+ if @execute_query_options
251
+ @service.execute_streaming_sql(
252
+ @session_path,
253
+ @sql,
254
+ @execute_query_options.merge(resume_token: resume_token)
255
+ )
256
+ else
257
+ @service.streaming_read_table(
258
+ @session_path,
259
+ @table,
260
+ @columns,
261
+ @read_options.merge(resume_token: resume_token)
262
+ )
263
+ end
264
+ end
265
+
266
+ ##
267
+ # @private
268
+ # Retries a request, by re-executing it from scratch.
269
+ def retry_request
270
+ if @execute_query_options
271
+ @service.execute_streaming_sql @session_path, @sql, @execute_query_options
272
+ else
273
+ @service.streaming_read_table @session_path, @table, @columns, @read_options
274
+ end
275
+ end
276
+
201
277
  ##
202
278
  # @private
203
279
  # Get row count from stats. This will be the exact row count for DML
@@ -16,7 +16,7 @@
16
16
  module Google
17
17
  module Cloud
18
18
  module Spanner
19
- VERSION = "2.1.0".freeze
19
+ VERSION = "2.2.0".freeze
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: 2.1.0
4
+ version: 2.2.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: 2020-08-06 00:00:00.000000000 Z
12
+ date: 2020-09-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: google-cloud-core
@@ -329,7 +329,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
329
329
  - !ruby/object:Gem::Version
330
330
  version: '0'
331
331
  requirements: []
332
- rubygems_version: 3.1.3
332
+ rubygems_version: 3.1.4
333
333
  signing_key:
334
334
  specification_version: 4
335
335
  summary: API Client library for Google Cloud Spanner API