gapic-common 0.22.0 → 0.25.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 +21 -0
- data/README.md +5 -5
- data/lib/gapic/call_options/error_codes.rb +2 -60
- data/lib/gapic/call_options/retry_policy.rb +10 -109
- data/lib/gapic/call_options.rb +1 -1
- data/lib/gapic/common/error_codes.rb +74 -0
- data/lib/gapic/common/polling_harness.rb +82 -0
- data/lib/gapic/common/retry_policy.rb +255 -0
- data/lib/gapic/common/version.rb +1 -1
- data/lib/gapic/common.rb +1 -0
- data/lib/gapic/grpc/service_stub/channel.rb +4 -2
- data/lib/gapic/grpc/service_stub/channel_pool.rb +9 -2
- data/lib/gapic/grpc/service_stub/rpc_call.rb +120 -12
- data/lib/gapic/grpc/service_stub.rb +15 -3
- data/lib/gapic/headers.rb +5 -1
- data/lib/gapic/logging_concerns.rb +210 -0
- data/lib/gapic/operation/retry_policy.rb +25 -65
- data/lib/gapic/rest/client_stub.rb +91 -18
- data/lib/gapic/universe_domain_concerns.rb +2 -2
- metadata +40 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28ba6b92736665b67d4b1618bf09b98a4951c9977cd85ed25b190e03216b1ff2
|
4
|
+
data.tar.gz: ece960000b24d5c1f36c508b23e70d5f7edcdf94fb257fd1182f0e9892047108
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c41746a88f4395985296dd5ec97259ff861401186af7648b263199ee1817f686453525d64fb2f733d6479a160518e88103b5a7f7397cd9cfead7134aec5782a7
|
7
|
+
data.tar.gz: 8fa6f167f8b4a48186d97a8f2907ee082c3c2d8c3b8f3437d0d6a7e33c8d80c3acb17acc0a496fafbf6e918e032b7476ff7280602c5bbe4a0914715359a7c49d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 0.25.0 (2025-01-22)
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* Implement a polling harness ([#20](https://github.com/googleapis/ruby-core-libraries/issues/20))
|
8
|
+
|
9
|
+
### 0.24.0 (2024-12-05)
|
10
|
+
|
11
|
+
#### Features
|
12
|
+
|
13
|
+
* Log requests and responses ([#1112](https://github.com/googleapis/gapic-generator-ruby/issues/1112))
|
14
|
+
#### Bug Fixes
|
15
|
+
|
16
|
+
* Hardened deadline determination against Time.now hacking ([#942](https://github.com/googleapis/gapic-generator-ruby/issues/942))
|
17
|
+
|
18
|
+
### 0.23.0 (2024-10-15)
|
19
|
+
|
20
|
+
#### Features
|
21
|
+
|
22
|
+
* Disable universe domain check if credentials include an explicit request to do so ([#1119](https://github.com/googleapis/gapic-generator-ruby/issues/1119))
|
23
|
+
|
3
24
|
### 0.22.0 (2024-07-17)
|
4
25
|
|
5
26
|
#### Features
|
data/README.md
CHANGED
@@ -15,14 +15,14 @@ convenient and idiomatic API surface to callers.
|
|
15
15
|
|
16
16
|
## Supported Ruby Versions
|
17
17
|
|
18
|
-
This library is supported on Ruby
|
18
|
+
This library is supported on Ruby 3.0+.
|
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
|
22
|
-
security maintenance, and not end of life.
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
security maintenance, and not end of life. Older versions of Ruby _may_ still
|
23
|
+
work, but are unsupported and not recommended. See
|
24
|
+
https://www.ruby-lang.org/en/downloads/branches/ for details about the Ruby
|
25
|
+
support schedule.
|
26
26
|
|
27
27
|
## Contributing
|
28
28
|
|
@@ -12,63 +12,5 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
##
|
18
|
-
# @private
|
19
|
-
# The gRPC error codes and their HTTP mapping
|
20
|
-
#
|
21
|
-
module ErrorCodes
|
22
|
-
# @private
|
23
|
-
# See https://grpc.github.io/grpc/core/md_doc_statuscodes.html for a
|
24
|
-
# list of error codes.
|
25
|
-
error_code_mapping = [
|
26
|
-
"OK",
|
27
|
-
"CANCELLED",
|
28
|
-
"UNKNOWN",
|
29
|
-
"INVALID_ARGUMENT",
|
30
|
-
"DEADLINE_EXCEEDED",
|
31
|
-
"NOT_FOUND",
|
32
|
-
"ALREADY_EXISTS",
|
33
|
-
"PERMISSION_DENIED",
|
34
|
-
"RESOURCE_EXHAUSTED",
|
35
|
-
"FAILED_PRECONDITION",
|
36
|
-
"ABORTED",
|
37
|
-
"OUT_OF_RANGE",
|
38
|
-
"UNIMPLEMENTED",
|
39
|
-
"INTERNAL",
|
40
|
-
"UNAVAILABLE",
|
41
|
-
"DATA_LOSS",
|
42
|
-
"UNAUTHENTICATED"
|
43
|
-
].freeze
|
44
|
-
|
45
|
-
# @private
|
46
|
-
ERROR_STRING_MAPPING = error_code_mapping.each_with_index.to_h.freeze
|
47
|
-
|
48
|
-
# @private
|
49
|
-
HTTP_GRPC_CODE_MAP = {
|
50
|
-
400 => 3, # InvalidArgumentError
|
51
|
-
401 => 16, # UnauthenticatedError
|
52
|
-
403 => 7, # PermissionDeniedError
|
53
|
-
404 => 5, # NotFoundError
|
54
|
-
409 => 6, # AlreadyExistsError
|
55
|
-
412 => 9, # FailedPreconditionError
|
56
|
-
429 => 8, # ResourceExhaustedError
|
57
|
-
499 => 1, # CanceledError
|
58
|
-
500 => 13, # InternalError
|
59
|
-
501 => 12, # UnimplementedError
|
60
|
-
503 => 14, # UnavailableError
|
61
|
-
504 => 4 # DeadlineExceededError
|
62
|
-
}.freeze
|
63
|
-
|
64
|
-
# @private
|
65
|
-
# Converts http error codes into corresponding gRPC ones
|
66
|
-
def self.grpc_error_for http_error_code
|
67
|
-
return 2 unless http_error_code
|
68
|
-
|
69
|
-
# The http status codes mapped to their error classes.
|
70
|
-
HTTP_GRPC_CODE_MAP[http_error_code] || 2 # UnknownError
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
15
|
+
# File moved.
|
16
|
+
require "gapic/common/error_codes"
|
@@ -12,126 +12,27 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
require "gapic/
|
15
|
+
require "gapic/common/retry_policy"
|
16
16
|
|
17
17
|
module Gapic
|
18
18
|
class CallOptions
|
19
19
|
##
|
20
|
-
# The policy for retrying failed RPC calls using an incremental backoff. A new object instance
|
21
|
-
# every RpcCall invocation.
|
20
|
+
# The policy for retrying failed RPC calls using an incremental backoff. A new object instance
|
21
|
+
# should be used for every RpcCall invocation.
|
22
22
|
#
|
23
|
-
# Only errors
|
23
|
+
# Only errors originating from GRPC will be retried.
|
24
24
|
#
|
25
|
-
class RetryPolicy
|
25
|
+
class RetryPolicy < Gapic::Common::RetryPolicy
|
26
26
|
##
|
27
27
|
# Create new API Call RetryPolicy.
|
28
28
|
#
|
29
|
-
# @param
|
30
|
-
# @param
|
31
|
-
# @param
|
29
|
+
# @param retry_codes [Array<String|Numeric>] List of retry codes.
|
30
|
+
# @param initial_delay [Numeric] Initial delay in seconds.
|
31
|
+
# @param multiplier [Numeric] The delay scaling factor for each subsequent retry attempt.
|
32
|
+
# @param max_delay [Numeric] Maximum delay in seconds.
|
32
33
|
#
|
33
34
|
def initialize retry_codes: nil, initial_delay: nil, multiplier: nil, max_delay: nil
|
34
|
-
|
35
|
-
@initial_delay = initial_delay
|
36
|
-
@multiplier = multiplier
|
37
|
-
@max_delay = max_delay
|
38
|
-
@delay = nil
|
39
|
-
end
|
40
|
-
|
41
|
-
def retry_codes
|
42
|
-
@retry_codes || []
|
43
|
-
end
|
44
|
-
|
45
|
-
def initial_delay
|
46
|
-
@initial_delay || 1
|
47
|
-
end
|
48
|
-
|
49
|
-
def multiplier
|
50
|
-
@multiplier || 1.3
|
51
|
-
end
|
52
|
-
|
53
|
-
def max_delay
|
54
|
-
@max_delay || 15
|
55
|
-
end
|
56
|
-
|
57
|
-
##
|
58
|
-
# The current delay value.
|
59
|
-
def delay
|
60
|
-
@delay || initial_delay
|
61
|
-
end
|
62
|
-
|
63
|
-
def call error
|
64
|
-
return false unless retry? error
|
65
|
-
|
66
|
-
delay!
|
67
|
-
increment_delay!
|
68
|
-
|
69
|
-
true
|
70
|
-
end
|
71
|
-
|
72
|
-
##
|
73
|
-
# @private
|
74
|
-
# Apply default values to the policy object. This does not replace user-provided values, it only overrides empty
|
75
|
-
# values.
|
76
|
-
#
|
77
|
-
# @param retry_policy [Hash] The policy for error retry. keys must match the arguments for
|
78
|
-
# {RpcCall::RetryPolicy.new}.
|
79
|
-
#
|
80
|
-
def apply_defaults retry_policy
|
81
|
-
return unless retry_policy.is_a? Hash
|
82
|
-
|
83
|
-
@retry_codes ||= convert_codes retry_policy[:retry_codes]
|
84
|
-
@initial_delay ||= retry_policy[:initial_delay]
|
85
|
-
@multiplier ||= retry_policy[:multiplier]
|
86
|
-
@max_delay ||= retry_policy[:max_delay]
|
87
|
-
|
88
|
-
self
|
89
|
-
end
|
90
|
-
|
91
|
-
# @private Equality test
|
92
|
-
def eql? other
|
93
|
-
other.is_a?(RetryPolicy) &&
|
94
|
-
other.retry_codes == retry_codes &&
|
95
|
-
other.initial_delay == initial_delay &&
|
96
|
-
other.multiplier == multiplier &&
|
97
|
-
other.max_delay == max_delay
|
98
|
-
end
|
99
|
-
alias == eql?
|
100
|
-
|
101
|
-
# @private Hash code
|
102
|
-
def hash
|
103
|
-
[retry_codes, initial_delay, multiplier, max_delay].hash
|
104
|
-
end
|
105
|
-
|
106
|
-
private
|
107
|
-
|
108
|
-
def retry? error
|
109
|
-
(defined?(::GRPC) && error.is_a?(::GRPC::BadStatus) && retry_codes.include?(error.code)) ||
|
110
|
-
(error.respond_to?(:response_status) &&
|
111
|
-
retry_codes.include?(ErrorCodes.grpc_error_for(error.response_status)))
|
112
|
-
end
|
113
|
-
|
114
|
-
def delay!
|
115
|
-
# Call Kernel.sleep so we can stub it.
|
116
|
-
Kernel.sleep delay
|
117
|
-
end
|
118
|
-
|
119
|
-
def convert_codes input_codes
|
120
|
-
return nil if input_codes.nil?
|
121
|
-
Array(input_codes).map do |obj|
|
122
|
-
case obj
|
123
|
-
when String
|
124
|
-
ErrorCodes::ERROR_STRING_MAPPING[obj]
|
125
|
-
when Integer
|
126
|
-
obj
|
127
|
-
end
|
128
|
-
end.compact
|
129
|
-
end
|
130
|
-
|
131
|
-
##
|
132
|
-
# Calculate and set the next delay value.
|
133
|
-
def increment_delay!
|
134
|
-
@delay = [delay * multiplier, max_delay].min
|
35
|
+
super
|
135
36
|
end
|
136
37
|
end
|
137
38
|
end
|
data/lib/gapic/call_options.rb
CHANGED
@@ -12,8 +12,8 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
require "gapic/call_options/error_codes"
|
16
15
|
require "gapic/call_options/retry_policy"
|
16
|
+
require "gapic/common/error_codes"
|
17
17
|
|
18
18
|
module Gapic
|
19
19
|
##
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright 2025 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
|
+
module Gapic
|
16
|
+
module Common
|
17
|
+
##
|
18
|
+
# @private
|
19
|
+
# The gRPC error codes and their HTTP mapping
|
20
|
+
#
|
21
|
+
module ErrorCodes
|
22
|
+
# @private
|
23
|
+
# See https://grpc.github.io/grpc/core/md_doc_statuscodes.html for a
|
24
|
+
# list of error codes.
|
25
|
+
error_code_mapping = [
|
26
|
+
"OK",
|
27
|
+
"CANCELLED",
|
28
|
+
"UNKNOWN",
|
29
|
+
"INVALID_ARGUMENT",
|
30
|
+
"DEADLINE_EXCEEDED",
|
31
|
+
"NOT_FOUND",
|
32
|
+
"ALREADY_EXISTS",
|
33
|
+
"PERMISSION_DENIED",
|
34
|
+
"RESOURCE_EXHAUSTED",
|
35
|
+
"FAILED_PRECONDITION",
|
36
|
+
"ABORTED",
|
37
|
+
"OUT_OF_RANGE",
|
38
|
+
"UNIMPLEMENTED",
|
39
|
+
"INTERNAL",
|
40
|
+
"UNAVAILABLE",
|
41
|
+
"DATA_LOSS",
|
42
|
+
"UNAUTHENTICATED"
|
43
|
+
].freeze
|
44
|
+
|
45
|
+
# @private
|
46
|
+
ERROR_STRING_MAPPING = error_code_mapping.each_with_index.to_h.freeze
|
47
|
+
|
48
|
+
# @private
|
49
|
+
HTTP_GRPC_CODE_MAP = {
|
50
|
+
400 => 3, # InvalidArgumentError
|
51
|
+
401 => 16, # UnauthenticatedError
|
52
|
+
403 => 7, # PermissionDeniedError
|
53
|
+
404 => 5, # NotFoundError
|
54
|
+
409 => 6, # AlreadyExistsError
|
55
|
+
412 => 9, # FailedPreconditionError
|
56
|
+
429 => 8, # ResourceExhaustedError
|
57
|
+
499 => 1, # CanceledError
|
58
|
+
500 => 13, # InternalError
|
59
|
+
501 => 12, # UnimplementedError
|
60
|
+
503 => 14, # UnavailableError
|
61
|
+
504 => 4 # DeadlineExceededError
|
62
|
+
}.freeze
|
63
|
+
|
64
|
+
# @private
|
65
|
+
# Converts http error codes into corresponding gRPC ones
|
66
|
+
def self.grpc_error_for http_error_code
|
67
|
+
return 2 unless http_error_code
|
68
|
+
|
69
|
+
# The http status codes mapped to their error classes.
|
70
|
+
HTTP_GRPC_CODE_MAP[http_error_code] || 2 # UnknownError
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Copyright 2025 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
|
+
require "gapic/common/retry_policy"
|
16
|
+
|
17
|
+
module Gapic
|
18
|
+
module Common
|
19
|
+
##
|
20
|
+
# Provides a generic mechanism for periodic polling an operation.
|
21
|
+
#
|
22
|
+
# Polling intervals are calculated from the provided retry policy.
|
23
|
+
#
|
24
|
+
# Supports exponential backoff via `multiplier`, and automatically
|
25
|
+
# retries gRPC errors listed in `retry_codes`.
|
26
|
+
#
|
27
|
+
class PollingHarness
|
28
|
+
# @return [Gapic::Common::RetryPolicy] The retry policy associated with this instance.
|
29
|
+
attr_reader :retry_policy
|
30
|
+
|
31
|
+
##
|
32
|
+
# Create new Gapic::Common::PollingHarness instance.
|
33
|
+
#
|
34
|
+
# @param retry_policy [Gapic::Common::RetryPolicy] The retry policy to
|
35
|
+
# use. If not provided, a new retry policy is constructed from the
|
36
|
+
# given kwargs.
|
37
|
+
# @param kwargs [keywords] Keyword arguments used to create a new retry
|
38
|
+
# policy. See {RetryPolicy#initialize}.
|
39
|
+
#
|
40
|
+
def initialize retry_policy: nil, **kwargs
|
41
|
+
@retry_policy = retry_policy ? retry_policy.dup : RetryPolicy.new(**kwargs)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Perform polling with exponential backoff. Repeatedly calls the given
|
46
|
+
# block until it returns a result other than the given sentinel value
|
47
|
+
# (normally `nil`), then returns that final result.
|
48
|
+
#
|
49
|
+
# Uses the retry policy regulate retries, including delays between tries,
|
50
|
+
# retriable errors, and final timeout. If an error code other than those
|
51
|
+
# listed in {RetryPolicy#retry_codes} is raised, it is propagated out. If
|
52
|
+
# the timeout expires, the given timeout result (normally `nil`) is
|
53
|
+
# returned.
|
54
|
+
#
|
55
|
+
# @param wait_sentinel [Object] The return value that should signal the
|
56
|
+
# polling harness to continue waiting. Any other value is treated as
|
57
|
+
# a real result and returned. Defaults to `nil`.
|
58
|
+
# @param timeout_result [Object] The result to return if timeout occurs.
|
59
|
+
# Defaults to `nil`.
|
60
|
+
#
|
61
|
+
# @yieldreturn [Object] The result of the polling logic, either a result
|
62
|
+
# to return, or the `wait_sentinel` value.
|
63
|
+
#
|
64
|
+
def wait wait_sentinel: nil, timeout_result: nil, mock_delay: false
|
65
|
+
unless block_given?
|
66
|
+
raise ArgumentError, "No callback provided to wait method."
|
67
|
+
end
|
68
|
+
retry_policy.start! mock_delay: mock_delay
|
69
|
+
loop do
|
70
|
+
begin
|
71
|
+
response = yield
|
72
|
+
return response unless response == wait_sentinel
|
73
|
+
rescue StandardError => e # Currently retry_error only accounts for ::GRPC::BadStatus.
|
74
|
+
raise unless retry_policy.retry_error? e
|
75
|
+
end
|
76
|
+
retry_policy.perform_delay!
|
77
|
+
return timeout_result unless retry_policy.retry_with_deadline?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
# Copyright 2025 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
|
+
require "gapic/common/error_codes"
|
16
|
+
|
17
|
+
module Gapic
|
18
|
+
module Common
|
19
|
+
##
|
20
|
+
# Gapic Common retry policy base class.
|
21
|
+
#
|
22
|
+
class RetryPolicy
|
23
|
+
# @return [Numeric] Default initial delay in seconds.
|
24
|
+
DEFAULT_INITIAL_DELAY = 1
|
25
|
+
# @return [Numeric] Default maximum delay in seconds.
|
26
|
+
DEFAULT_MAX_DELAY = 15
|
27
|
+
# @return [Numeric] Default delay scaling factor for subsequent retry attempts.
|
28
|
+
DEFAULT_MULTIPLIER = 1.3
|
29
|
+
# @return [Array<String|Integer>] Default list of retry codes.
|
30
|
+
DEFAULT_RETRY_CODES = [].freeze
|
31
|
+
# @return [Numeric] Default timeout threshold value in seconds.
|
32
|
+
DEFAULT_TIMEOUT = 3600 # One hour
|
33
|
+
|
34
|
+
##
|
35
|
+
# Create new Gapic::Common::RetryPolicy instance.
|
36
|
+
#
|
37
|
+
# @param initial_delay [Numeric] Initial delay in seconds.
|
38
|
+
# @param max_delay [Numeric] Maximum delay in seconds.
|
39
|
+
# @param multiplier [Numeric] The delay scaling factor for each subsequent retry attempt.
|
40
|
+
# @param retry_codes [Array<String|Integer>] List of retry codes.
|
41
|
+
# @param timeout [Numeric] Timeout threshold value in seconds.
|
42
|
+
#
|
43
|
+
def initialize initial_delay: nil, max_delay: nil, multiplier: nil, retry_codes: nil, timeout: nil
|
44
|
+
# Instance values are set as `nil` to determine whether values are overriden from default.
|
45
|
+
@initial_delay = initial_delay
|
46
|
+
@max_delay = max_delay
|
47
|
+
@multiplier = multiplier
|
48
|
+
@retry_codes = convert_codes retry_codes
|
49
|
+
@timeout = timeout
|
50
|
+
start!
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Numeric] Initial delay in seconds.
|
54
|
+
def initial_delay
|
55
|
+
@initial_delay || DEFAULT_INITIAL_DELAY
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Numeric] Maximum delay in seconds.
|
59
|
+
def max_delay
|
60
|
+
@max_delay || DEFAULT_MAX_DELAY
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Numeric] The delay scaling factor for each subsequent retry attempt.
|
64
|
+
def multiplier
|
65
|
+
@multiplier || DEFAULT_MULTIPLIER
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Array<Integer>] List of retry codes.
|
69
|
+
def retry_codes
|
70
|
+
@retry_codes || DEFAULT_RETRY_CODES
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Numeric] Timeout threshold value in seconds.
|
74
|
+
def timeout
|
75
|
+
@timeout || DEFAULT_TIMEOUT
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Returns a duplicate in a non-executing state, i.e. with the deadline
|
80
|
+
# and current delay reset.
|
81
|
+
#
|
82
|
+
# @return [RetryPolicy]
|
83
|
+
#
|
84
|
+
def dup
|
85
|
+
RetryPolicy.new initial_delay: @initial_delay,
|
86
|
+
max_delay: @max_delay,
|
87
|
+
multiplier: @multiplier,
|
88
|
+
retry_codes: @retry_codes,
|
89
|
+
timeout: @timeout
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Perform delay if and only if retriable.
|
94
|
+
#
|
95
|
+
# If positional argument `error` is provided, the retriable logic uses
|
96
|
+
# `retry_codes`. Otherwise, `timeout` is used.
|
97
|
+
#
|
98
|
+
# @return [Boolean] Whether the delay was executed.
|
99
|
+
#
|
100
|
+
def call error = nil
|
101
|
+
should_retry = error.nil? ? retry_with_deadline? : retry_error?(error)
|
102
|
+
return false unless should_retry
|
103
|
+
perform_delay!
|
104
|
+
end
|
105
|
+
alias perform_delay call
|
106
|
+
|
107
|
+
##
|
108
|
+
# Perform delay.
|
109
|
+
#
|
110
|
+
# @return [Boolean] Whether the delay was executed.
|
111
|
+
#
|
112
|
+
def perform_delay!
|
113
|
+
delay!
|
114
|
+
increment_delay!
|
115
|
+
@perform_delay_count += 1
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Current delay value in seconds.
|
121
|
+
#
|
122
|
+
# @return [Numeric] Time delay in seconds.
|
123
|
+
#
|
124
|
+
def delay
|
125
|
+
@delay
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Current number of times the delay has been performed
|
130
|
+
#
|
131
|
+
# @return [Integer]
|
132
|
+
#
|
133
|
+
def perform_delay_count
|
134
|
+
@perform_delay_count
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Start tracking the deadline and delay by initializing those values.
|
139
|
+
#
|
140
|
+
# This is normally done when the object is constructed, but it can be
|
141
|
+
# done explicitly in order to reinitialize the state in case this
|
142
|
+
# retry policy was created in the past or is being reused.
|
143
|
+
#
|
144
|
+
# @param mock_delay [boolean,Proc] if truthy, delays are "mocked",
|
145
|
+
# meaning they do not actually take time, but are measured as if they
|
146
|
+
# did, which is useful for tests. If set to a Proc, it will be called
|
147
|
+
# whenever a delay would happen, and passed the delay in seconds,
|
148
|
+
# also useful for testing.
|
149
|
+
#
|
150
|
+
def start! mock_delay: false
|
151
|
+
@mock_time = mock_delay ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : nil
|
152
|
+
@mock_delay_callback = mock_delay.respond_to?(:call) ? mock_delay : nil
|
153
|
+
@deadline = cur_time + timeout
|
154
|
+
@delay = initial_delay
|
155
|
+
@perform_delay_count = 0
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# @private
|
161
|
+
# @return [Boolean] Whether this error should be retried.
|
162
|
+
#
|
163
|
+
def retry_error? error
|
164
|
+
(defined?(::GRPC) && error.is_a?(::GRPC::BadStatus) && retry_codes.include?(error.code)) ||
|
165
|
+
(error.respond_to?(:response_status) &&
|
166
|
+
retry_codes.include?(ErrorCodes.grpc_error_for(error.response_status)))
|
167
|
+
end
|
168
|
+
|
169
|
+
# @private
|
170
|
+
# @return [Boolean] Whether this policy should be retried based on the deadline.
|
171
|
+
def retry_with_deadline?
|
172
|
+
deadline > cur_time
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# @private
|
177
|
+
# Apply default values to the policy object. This does not replace user-provided values,
|
178
|
+
# it only overrides empty values.
|
179
|
+
#
|
180
|
+
# @param retry_policy [Hash] The policy for error retry. Keys must match the arguments for
|
181
|
+
# {Gapic::Common::RetryPolicy.new}.
|
182
|
+
#
|
183
|
+
def apply_defaults retry_policy
|
184
|
+
return unless retry_policy.is_a? Hash
|
185
|
+
@retry_codes ||= convert_codes retry_policy[:retry_codes]
|
186
|
+
@initial_delay ||= retry_policy[:initial_delay]
|
187
|
+
@multiplier ||= retry_policy[:multiplier]
|
188
|
+
@max_delay ||= retry_policy[:max_delay]
|
189
|
+
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
# @private Equality test
|
194
|
+
def eql? other
|
195
|
+
other.is_a?(RetryPolicy) &&
|
196
|
+
other.initial_delay == initial_delay &&
|
197
|
+
other.max_delay == max_delay &&
|
198
|
+
other.multiplier == multiplier &&
|
199
|
+
other.retry_codes == retry_codes &&
|
200
|
+
other.timeout == timeout
|
201
|
+
end
|
202
|
+
alias == eql?
|
203
|
+
|
204
|
+
# @private Hash code
|
205
|
+
def hash
|
206
|
+
[initial_delay, max_delay, multiplier, retry_codes, timeout].hash
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
# @private
|
212
|
+
# @return [Numeric] The performed delay.
|
213
|
+
def delay!
|
214
|
+
if @mock_time
|
215
|
+
@mock_time += delay
|
216
|
+
@mock_delay_callback&.call delay
|
217
|
+
else
|
218
|
+
Kernel.sleep delay
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# @private
|
223
|
+
# @return [Numeric] The new delay in seconds.
|
224
|
+
def increment_delay!
|
225
|
+
@delay = [delay * multiplier, max_delay].min
|
226
|
+
end
|
227
|
+
|
228
|
+
# @private
|
229
|
+
# @return [Numeric] The deadline for timeout-based policies.
|
230
|
+
def deadline
|
231
|
+
@deadline
|
232
|
+
end
|
233
|
+
|
234
|
+
# @private
|
235
|
+
# Mockable way to get time.
|
236
|
+
def cur_time
|
237
|
+
@mock_time || Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
238
|
+
end
|
239
|
+
|
240
|
+
# @private
|
241
|
+
# @return [Array<Integer> Error codes converted to their respective integer values.
|
242
|
+
def convert_codes input_codes
|
243
|
+
return nil if input_codes.nil?
|
244
|
+
Array(input_codes).map do |obj|
|
245
|
+
case obj
|
246
|
+
when String
|
247
|
+
ErrorCodes::ERROR_STRING_MAPPING[obj]
|
248
|
+
when Integer
|
249
|
+
obj
|
250
|
+
end
|
251
|
+
end.compact
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|