gapic-common 1.2.0 → 1.4.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 +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +30 -1
- data/lib/gapic/call_options/retry_policy.rb +2 -1
- data/lib/gapic/common/retry_policy.rb +64 -12
- data/lib/gapic/common/version.rb +1 -1
- data/lib/gapic/operation/retry_policy.rb +4 -2
- data/lib/gapic/rest/grpc_transcoder.rb +10 -1
- metadata +12 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6dbc3fc8f2b53d01d228f377851d21d941cedd307a9528e7ba732c5e8d2565e2
|
|
4
|
+
data.tar.gz: d6ded505cf91783149b49373f2efb2ef105860705a22f2940c27f6b27d83be39
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e7533c44cae4643cec30b87e96fc61750ca2ba2a3458c711788d51987436b25ca3248f6c8d011cf06a00992eec4458e3403ac0b64850a43d153368cd3a6c0203
|
|
7
|
+
data.tar.gz: cd51bd91e418f8d5c71c3f6e484b29f4c420121b14baca686309f074928162f97c0259555bd32d28cb6e747a397c99c0e3d5aeb6cd99085d309ed61482af7cfa
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### 1.4.0 (2026-09-23)
|
|
4
|
+
|
|
5
|
+
#### Features
|
|
6
|
+
|
|
7
|
+
* add custom predicate matching to RetryPolicy ([#59](https://github.com/googleapis/ruby-core-libraries/issues/59))
|
|
8
|
+
#### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* require grpc >= 1.83 for post-quantum key exchange ([#73](https://github.com/googleapis/ruby-core-libraries/issues/73))
|
|
11
|
+
* only use emit_default for proto messages ([#75](https://github.com/googleapis/ruby-core-libraries/issues/75))
|
|
12
|
+
|
|
13
|
+
### 1.3.0 (2026-03-05)
|
|
14
|
+
|
|
15
|
+
#### Features
|
|
16
|
+
|
|
17
|
+
* add jitter to retries ([#54](https://github.com/googleapis/ruby-core-libraries/issues/54))
|
|
18
|
+
|
|
3
19
|
### 1.2.0 (2025-09-04)
|
|
4
20
|
|
|
5
21
|
#### Features
|
data/README.md
CHANGED
|
@@ -15,7 +15,7 @@ convenient and idiomatic API surface to callers.
|
|
|
15
15
|
|
|
16
16
|
## Supported Ruby Versions
|
|
17
17
|
|
|
18
|
-
This library is supported on Ruby 3.
|
|
18
|
+
This library is supported on Ruby 3.2+.
|
|
19
19
|
|
|
20
20
|
Google provides official support for Ruby versions that are actively supported
|
|
21
21
|
by Ruby Core—that is, Ruby versions that are either in normal maintenance or in
|
|
@@ -24,6 +24,35 @@ work, but are unsupported and not recommended. See
|
|
|
24
24
|
https://www.ruby-lang.org/en/downloads/branches/ for details about the Ruby
|
|
25
25
|
support schedule.
|
|
26
26
|
|
|
27
|
+
## Post-Quantum Key Exchange
|
|
28
|
+
|
|
29
|
+
Clients built on this library negotiate the hybrid post-quantum key exchange
|
|
30
|
+
group `X25519MLKEM768` — classical X25519 paired with ML-KEM-768 ([NIST FIPS
|
|
31
|
+
203][]) — over TLS 1.3. No application code changes are required, because the
|
|
32
|
+
handshake belongs to the transport layer. The two transports source that
|
|
33
|
+
support differently:
|
|
34
|
+
|
|
35
|
+
| Transport | Cryptographic provider | Requirement | Enforced by this gem? |
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| gRPC | BoringSSL, vendored inside the `grpc` gem | `grpc >= 1.83` | Yes |
|
|
38
|
+
| REST | Host system OpenSSL, via `Net::HTTP` and Faraday | OpenSSL `>= 3.5` | No |
|
|
39
|
+
|
|
40
|
+
gRPC clients get post-quantum key exchange automatically: `gapic-common`
|
|
41
|
+
depends on `grpc >= 1.83`, the first release to offer `X25519MLKEM768` in the
|
|
42
|
+
TLS ClientHello by default.
|
|
43
|
+
|
|
44
|
+
The REST requirement cannot be expressed as a gem dependency. Ruby's `openssl`
|
|
45
|
+
is a default gem bound to whatever `libssl` the host provides, and ML-KEM first
|
|
46
|
+
ships in OpenSSL 3.5. On an older host, REST connections negotiate classical
|
|
47
|
+
X25519 instead — a safe fallback rather than an error, but not post-quantum. To
|
|
48
|
+
check the OpenSSL your Ruby is linked against:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_LIBRARY_VERSION'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
[NIST FIPS 203]: https://csrc.nist.gov/pubs/fips/203/final
|
|
55
|
+
|
|
27
56
|
## Contributing
|
|
28
57
|
|
|
29
58
|
Contributions to this library are always welcome and highly encouraged.
|
|
@@ -30,8 +30,9 @@ module Gapic
|
|
|
30
30
|
# @param initial_delay [Numeric] Initial delay in seconds.
|
|
31
31
|
# @param multiplier [Numeric] The delay scaling factor for each subsequent retry attempt.
|
|
32
32
|
# @param max_delay [Numeric] Maximum delay in seconds.
|
|
33
|
+
# @param jitter [Numeric] Random jitter added to the delay in seconds.
|
|
33
34
|
#
|
|
34
|
-
def initialize retry_codes: nil, initial_delay: nil, multiplier: nil, max_delay: nil
|
|
35
|
+
def initialize retry_codes: nil, initial_delay: nil, multiplier: nil, max_delay: nil, jitter: nil
|
|
35
36
|
super
|
|
36
37
|
end
|
|
37
38
|
end
|
|
@@ -31,6 +31,11 @@ module Gapic
|
|
|
31
31
|
# @return [Numeric] Default timeout threshold value in seconds.
|
|
32
32
|
DEFAULT_TIMEOUT = 3600 # One hour
|
|
33
33
|
|
|
34
|
+
# @private
|
|
35
|
+
# @return [Numeric] Default random jitter added to delay in seconds.
|
|
36
|
+
DEFAULT_JITTER = 1.0
|
|
37
|
+
private_constant :DEFAULT_JITTER
|
|
38
|
+
|
|
34
39
|
##
|
|
35
40
|
# Create new Gapic::Common::RetryPolicy instance.
|
|
36
41
|
#
|
|
@@ -39,14 +44,27 @@ module Gapic
|
|
|
39
44
|
# @param multiplier [Numeric] The delay scaling factor for each subsequent retry attempt.
|
|
40
45
|
# @param retry_codes [Array<String|Integer>] List of retry codes.
|
|
41
46
|
# @param timeout [Numeric] Timeout threshold value in seconds.
|
|
47
|
+
# @param jitter [Numeric] Random jitter added to the delay in seconds.
|
|
48
|
+
# @param retry_predicate [Proc, nil] The predicate to evaluate whether to retry on a given error. Optional.
|
|
49
|
+
# If the predicate is specified, it is run first. If it returns nil, the decision on
|
|
50
|
+
# whether to retry is made on the basis of retry_codes. Otherwise, the truthiness of
|
|
51
|
+
# the return value determines whether to retry.
|
|
42
52
|
#
|
|
43
|
-
def initialize initial_delay: nil, max_delay: nil, multiplier: nil, retry_codes: nil, timeout: nil
|
|
53
|
+
def initialize initial_delay: nil, max_delay: nil, multiplier: nil, retry_codes: nil, timeout: nil,
|
|
54
|
+
jitter: nil, retry_predicate: nil
|
|
55
|
+
raise ArgumentError, "jitter cannot be negative" if jitter&.negative?
|
|
56
|
+
if retry_predicate && !retry_predicate.respond_to?(:call)
|
|
57
|
+
raise ArgumentError, "retry_predicate must respond to :call"
|
|
58
|
+
end
|
|
59
|
+
|
|
44
60
|
# Instance values are set as `nil` to determine whether values are overriden from default.
|
|
45
61
|
@initial_delay = initial_delay
|
|
46
62
|
@max_delay = max_delay
|
|
47
63
|
@multiplier = multiplier
|
|
48
64
|
@retry_codes = convert_codes retry_codes
|
|
49
65
|
@timeout = timeout
|
|
66
|
+
@jitter = jitter
|
|
67
|
+
@retry_predicate = retry_predicate
|
|
50
68
|
start!
|
|
51
69
|
end
|
|
52
70
|
|
|
@@ -75,6 +93,24 @@ module Gapic
|
|
|
75
93
|
@timeout || DEFAULT_TIMEOUT
|
|
76
94
|
end
|
|
77
95
|
|
|
96
|
+
# @return [Numeric] Random jitter added to the delay in seconds.
|
|
97
|
+
def jitter
|
|
98
|
+
@jitter || DEFAULT_JITTER
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
# The predicate to evaluate whether to retry on a given error. Optional.
|
|
103
|
+
#
|
|
104
|
+
# When a predicate is specified:
|
|
105
|
+
# 1. The predicate is executed first on the error.
|
|
106
|
+
# 2. If it returns nil, the decision on whether to retry is made on the basis of retry_codes.
|
|
107
|
+
# 3. Otherwise, the truthiness of the return value determines whether to retry.
|
|
108
|
+
#
|
|
109
|
+
# @return [Proc, nil]
|
|
110
|
+
def retry_predicate
|
|
111
|
+
@retry_predicate
|
|
112
|
+
end
|
|
113
|
+
|
|
78
114
|
##
|
|
79
115
|
# Returns a duplicate in a non-executing state, i.e. with the deadline
|
|
80
116
|
# and current delay reset.
|
|
@@ -86,7 +122,9 @@ module Gapic
|
|
|
86
122
|
max_delay: @max_delay,
|
|
87
123
|
multiplier: @multiplier,
|
|
88
124
|
retry_codes: @retry_codes,
|
|
89
|
-
timeout: @timeout
|
|
125
|
+
timeout: @timeout,
|
|
126
|
+
jitter: @jitter,
|
|
127
|
+
retry_predicate: @retry_predicate
|
|
90
128
|
end
|
|
91
129
|
|
|
92
130
|
##
|
|
@@ -161,6 +199,13 @@ module Gapic
|
|
|
161
199
|
# @return [Boolean] Whether this error should be retried.
|
|
162
200
|
#
|
|
163
201
|
def retry_error? error
|
|
202
|
+
# Run predicate first. If it returns nil, decision is made on the basis of retry_codes.
|
|
203
|
+
# Otherwise return truthiness.
|
|
204
|
+
if @retry_predicate.respond_to? :call
|
|
205
|
+
result = @retry_predicate.call error
|
|
206
|
+
return !!result unless result.nil?
|
|
207
|
+
end
|
|
208
|
+
|
|
164
209
|
(defined?(::GRPC) && error.is_a?(::GRPC::BadStatus) && retry_codes.include?(error.code)) ||
|
|
165
210
|
(error.respond_to?(:response_status) &&
|
|
166
211
|
retry_codes.include?(ErrorCodes.grpc_error_for(error.response_status)))
|
|
@@ -182,10 +227,12 @@ module Gapic
|
|
|
182
227
|
#
|
|
183
228
|
def apply_defaults retry_policy
|
|
184
229
|
return unless retry_policy.is_a? Hash
|
|
185
|
-
@retry_codes
|
|
186
|
-
@initial_delay
|
|
187
|
-
@multiplier
|
|
188
|
-
@max_delay
|
|
230
|
+
@retry_codes ||= convert_codes retry_policy[:retry_codes]
|
|
231
|
+
@initial_delay ||= retry_policy[:initial_delay]
|
|
232
|
+
@multiplier ||= retry_policy[:multiplier]
|
|
233
|
+
@max_delay ||= retry_policy[:max_delay]
|
|
234
|
+
@jitter ||= retry_policy[:jitter]
|
|
235
|
+
@retry_predicate ||= retry_policy[:retry_predicate]
|
|
189
236
|
|
|
190
237
|
self
|
|
191
238
|
end
|
|
@@ -197,30 +244,35 @@ module Gapic
|
|
|
197
244
|
other.max_delay == max_delay &&
|
|
198
245
|
other.multiplier == multiplier &&
|
|
199
246
|
other.retry_codes == retry_codes &&
|
|
200
|
-
other.timeout == timeout
|
|
247
|
+
other.timeout == timeout &&
|
|
248
|
+
other.jitter == jitter &&
|
|
249
|
+
other.retry_predicate == retry_predicate
|
|
201
250
|
end
|
|
202
251
|
alias == eql?
|
|
203
252
|
|
|
204
253
|
# @private Hash code
|
|
205
254
|
def hash
|
|
206
|
-
[initial_delay, max_delay, multiplier, retry_codes, timeout].hash
|
|
255
|
+
[initial_delay, max_delay, multiplier, retry_codes, timeout, jitter, retry_predicate].hash
|
|
207
256
|
end
|
|
208
257
|
|
|
209
258
|
private
|
|
210
259
|
|
|
211
260
|
# @private
|
|
261
|
+
# Perform the currently calculated delay, adding a jitter.
|
|
262
|
+
#
|
|
212
263
|
# @return [Numeric] The performed delay.
|
|
213
264
|
def delay!
|
|
265
|
+
delay_to_perform = [delay + Kernel.rand(0.0..jitter), max_delay].min
|
|
214
266
|
if @mock_time
|
|
215
|
-
@mock_time +=
|
|
216
|
-
@mock_delay_callback&.call
|
|
267
|
+
@mock_time += delay_to_perform
|
|
268
|
+
@mock_delay_callback&.call delay_to_perform
|
|
217
269
|
else
|
|
218
|
-
Kernel.sleep
|
|
270
|
+
Kernel.sleep delay_to_perform
|
|
219
271
|
end
|
|
220
272
|
end
|
|
221
273
|
|
|
222
274
|
# @private
|
|
223
|
-
# @return [Numeric] The new delay in seconds.
|
|
275
|
+
# @return [Numeric] The new delay (sans jitter) in seconds.
|
|
224
276
|
def increment_delay!
|
|
225
277
|
@delay = [delay * multiplier, max_delay].min
|
|
226
278
|
end
|
data/lib/gapic/common/version.rb
CHANGED
|
@@ -38,13 +38,15 @@ module Gapic
|
|
|
38
38
|
# @param multiplier [Numeric] The delay scaling factor for each subsequent retry attempt.
|
|
39
39
|
# @param max_delay [Numeric] Maximum delay in seconds.
|
|
40
40
|
# @param timeout [Numeric] Timeout threshold value in seconds.
|
|
41
|
+
# @param jitter [Numeric] Random jitter added to the delay in seconds.
|
|
41
42
|
#
|
|
42
|
-
def initialize initial_delay: nil, multiplier: nil, max_delay: nil, timeout: nil
|
|
43
|
+
def initialize initial_delay: nil, multiplier: nil, max_delay: nil, timeout: nil, jitter: nil
|
|
43
44
|
super(
|
|
44
45
|
initial_delay: initial_delay || DEFAULT_INITIAL_DELAY,
|
|
45
46
|
max_delay: max_delay || DEFAULT_MAX_DELAY,
|
|
46
47
|
multiplier: multiplier || DEFAULT_MULTIPLIER,
|
|
47
|
-
timeout: timeout || DEFAULT_TIMEOUT
|
|
48
|
+
timeout: timeout || DEFAULT_TIMEOUT,
|
|
49
|
+
jitter: jitter
|
|
48
50
|
)
|
|
49
51
|
end
|
|
50
52
|
end
|
|
@@ -160,7 +160,16 @@ module Gapic
|
|
|
160
160
|
request_body_field = request.send body_template.to_sym if request.respond_to? body_template.to_sym
|
|
161
161
|
if request_body_field
|
|
162
162
|
request_hash_without_uri.delete camel_name_for body_template
|
|
163
|
-
|
|
163
|
+
# `emit_defaults` is only meaningful for, and only accepted by,
|
|
164
|
+
# `Google::Protobuf::MessageExts#to_json`. When the body template points at a
|
|
165
|
+
# scalar, enum or repeated field, `to_json` resolves to the json gem's
|
|
166
|
+
# `Object#to_json`, which has never accepted the keyword: json 2.x silently
|
|
167
|
+
# discarded unknown keywords, json 3.x raises ArgumentError.
|
|
168
|
+
body = if request_body_field.is_a? ::Google::Protobuf::MessageExts
|
|
169
|
+
request_body_field.to_json emit_defaults: true
|
|
170
|
+
else
|
|
171
|
+
request_body_field.to_json
|
|
172
|
+
end
|
|
164
173
|
end
|
|
165
174
|
|
|
166
175
|
query_params = build_query_params request_hash_without_uri
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gapic-common
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Google API Authors
|
|
@@ -137,16 +137,22 @@ dependencies:
|
|
|
137
137
|
name: grpc
|
|
138
138
|
requirement: !ruby/object:Gem::Requirement
|
|
139
139
|
requirements:
|
|
140
|
-
- - "
|
|
140
|
+
- - ">="
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '1.83'
|
|
143
|
+
- - "<"
|
|
141
144
|
- !ruby/object:Gem::Version
|
|
142
|
-
version:
|
|
145
|
+
version: 2.a
|
|
143
146
|
type: :runtime
|
|
144
147
|
prerelease: false
|
|
145
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
146
149
|
requirements:
|
|
147
|
-
- - "
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '1.83'
|
|
153
|
+
- - "<"
|
|
148
154
|
- !ruby/object:Gem::Version
|
|
149
|
-
version:
|
|
155
|
+
version: 2.a
|
|
150
156
|
email:
|
|
151
157
|
- googleapis-packages@google.com
|
|
152
158
|
executables: []
|
|
@@ -213,7 +219,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
213
219
|
requirements:
|
|
214
220
|
- - ">="
|
|
215
221
|
- !ruby/object:Gem::Version
|
|
216
|
-
version: '3.
|
|
222
|
+
version: '3.2'
|
|
217
223
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
224
|
requirements:
|
|
219
225
|
- - ">="
|