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.
data/lib/gapic/common.rb CHANGED
@@ -16,6 +16,7 @@ require "grpc/errors"
16
16
  require "grpc/core/status_codes"
17
17
 
18
18
  require "gapic/common/error"
19
+ require "gapic/common/polling_harness"
19
20
  require "gapic/call_options"
20
21
  require "gapic/headers"
21
22
  require "gapic/operation"
@@ -32,12 +32,13 @@ module Gapic
32
32
  # Creates a new Channel instance
33
33
  #
34
34
  def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil,
35
- on_channel_create: nil
35
+ on_channel_create: nil, stub_logger: nil
36
36
  @grpc_stub_class = grpc_stub_class
37
37
  @endpoint = endpoint
38
38
  @credentials = credentials
39
39
  @channel_args = Hash channel_args
40
40
  @interceptors = Array interceptors
41
+ @stub_logger = stub_logger
41
42
  @concurrent_streams = 0
42
43
  @mutex = Mutex.new
43
44
  setup_grpc_stub
@@ -88,7 +89,8 @@ module Gapic
88
89
  def call_rpc method_name, request, options: nil, &block
89
90
  @mutex.synchronize { @concurrent_streams += 1 }
90
91
  begin
91
- rpc_call = RpcCall.new @grpc_stub.method method_name
92
+ meth = @grpc_stub.method method_name
93
+ rpc_call = RpcCall.new meth, stub_logger: @stub_logger, method_name: method_name
92
94
  response = rpc_call.call request, options: options, &block
93
95
  response
94
96
  ensure
@@ -30,7 +30,12 @@ module Gapic
30
30
  ##
31
31
  # Initialize an instance of ServiceStub::ChannelPool
32
32
  #
33
- def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil, config: nil
33
+ def initialize grpc_stub_class,
34
+ endpoint:, credentials:,
35
+ channel_args: nil,
36
+ interceptors: nil,
37
+ config: nil,
38
+ stub_logger: nil
34
39
  if credentials.is_a? ::GRPC::Core::Channel
35
40
  raise ArgumentError, "Can't create a channel pool with GRPC::Core::Channel as credentials"
36
41
  end
@@ -41,6 +46,7 @@ module Gapic
41
46
  @channel_args = channel_args
42
47
  @interceptors = interceptors
43
48
  @config = config || Configuration.new
49
+ @stub_logger = stub_logger
44
50
 
45
51
  @channels = (1..@config.channel_count).map { create_channel }
46
52
  end
@@ -49,7 +55,8 @@ module Gapic
49
55
  # Creates a new channel.
50
56
  def create_channel
51
57
  Channel.new @grpc_stub_class, endpoint: @endpoint, credentials: @credentials, channel_args: @channel_args,
52
- interceptors: @interceptors, on_channel_create: @config.on_channel_create
58
+ interceptors: @interceptors, on_channel_create: @config.on_channel_create,
59
+ stub_logger: @stub_logger
53
60
  end
54
61
 
55
62
  ##
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "gapic/call_options"
16
+ require "gapic/logging_concerns"
16
17
  require "grpc/errors"
17
18
 
18
19
  module Gapic
@@ -32,8 +33,11 @@ module Gapic
32
33
  #
33
34
  # @param stub_method [Proc] Used to make a bare rpc call.
34
35
  #
35
- def initialize stub_method
36
+ def initialize stub_method, stub_logger: nil, method_name: nil
36
37
  @stub_method = stub_method
38
+ @stub_logger = stub_logger
39
+ @method_name = method_name
40
+ @request_id = LoggingConcerns.random_uuid4
37
41
  end
38
42
 
39
43
  ##
@@ -44,7 +48,8 @@ module Gapic
44
48
  # customize the options object, using keys that match the arguments for {Gapic::CallOptions.new}. This object
45
49
  # should only be used once.
46
50
  #
47
- # @yield [response, operation] Access the response along with the RPC operation.
51
+ # @yield [response, operation] Access the response along with the RPC operation. Additionally, throwing
52
+ # `:response, obj` within the block will change the return value to `obj`.
48
53
  # @yieldparam response [Object] The response object.
49
54
  # @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
50
55
  #
@@ -91,7 +96,7 @@ module Gapic
91
96
  # )
92
97
  # response = echo_call.call request, options: options
93
98
  #
94
- # @example Accessing the response and RPC operation using a block:
99
+ # @example Accessing the RPC operation using a block:
95
100
  # require "google/showcase/v1beta1/echo_pb"
96
101
  # require "google/showcase/v1beta1/echo_services_pb"
97
102
  # require "gapic"
@@ -107,8 +112,8 @@ module Gapic
107
112
  # echo_call = Gapic::ServiceStub::RpcCall.new echo_stub.method :echo
108
113
  #
109
114
  # request = Google::Showcase::V1beta1::EchoRequest.new
110
- # echo_call.call request do |response, operation|
111
- # operation.trailing_metadata
115
+ # metadata = echo_call.call request do |_response, operation|
116
+ # throw :response, operation.trailing_metadata
112
117
  # end
113
118
  #
114
119
  def call request, options: nil
@@ -117,21 +122,27 @@ module Gapic
117
122
  deadline = calculate_deadline options
118
123
  metadata = options.metadata
119
124
 
125
+ try_number = 1
120
126
  retried_exception = nil
121
127
  begin
128
+ request = log_request request, metadata, try_number
122
129
  operation = stub_method.call request, deadline: deadline, metadata: metadata, return_op: true
123
130
  response = operation.execute
124
- yield response, operation if block_given?
125
- response
131
+ catch :response do
132
+ response = log_response response, try_number
133
+ yield response, operation if block_given?
134
+ response
135
+ end
126
136
  rescue ::GRPC::DeadlineExceeded => e
137
+ log_response e, try_number
127
138
  raise Gapic::GRPC::DeadlineExceededError.new e.message, root_cause: retried_exception
128
139
  rescue StandardError => e
129
- if e.is_a?(::GRPC::Unavailable) && /Signet::AuthorizationError/ =~ e.message
130
- e = Gapic::GRPC::AuthorizationError.new e.message.gsub(%r{^\d+:}, "")
131
- end
140
+ e = normalize_exception e
141
+ log_response e, try_number
132
142
 
133
143
  if check_retry?(deadline) && options.retry_policy.call(e)
134
144
  retried_exception = e
145
+ try_number += 1
135
146
  retry
136
147
  end
137
148
 
@@ -145,13 +156,110 @@ module Gapic
145
156
  return if options.timeout.nil?
146
157
  return if options.timeout.negative?
147
158
 
148
- Time.now + options.timeout
159
+ current_time + options.timeout
149
160
  end
150
161
 
151
162
  def check_retry? deadline
152
163
  return true if deadline.nil?
153
164
 
154
- deadline > Time.now
165
+ deadline > current_time
166
+ end
167
+
168
+ def current_time
169
+ # An alternative way of saying Time.now without actually calling
170
+ # Time.now. This allows clients that replace Time.now (e.g. via the
171
+ # timecop gem) to do so without interfering with the deadline.
172
+ nanos = Process.clock_gettime Process::CLOCK_REALTIME, :nanosecond
173
+ secs_part = nanos / 1_000_000_000
174
+ nsecs_part = nanos % 1_000_000_000
175
+ Time.at secs_part, nsecs_part, :nanosecond
176
+ end
177
+
178
+ def normalize_exception exception
179
+ if exception.is_a?(::GRPC::Unavailable) && /Signet::AuthorizationError/ =~ exception.message
180
+ exception = Gapic::GRPC::AuthorizationError.new exception.message.gsub(%r{^\d+:}, "")
181
+ end
182
+ exception
183
+ end
184
+
185
+ def log_request request, metadata, try_number
186
+ return request unless @stub_logger
187
+ @stub_logger.info do |entry|
188
+ entry.set_system_name
189
+ entry.set_service
190
+ entry.set "rpcName", @method_name
191
+ entry.set "retryAttempt", try_number
192
+ entry.set "requestId", @request_id
193
+ entry.message =
194
+ if request.is_a? Enumerable
195
+ "Sending stream to #{entry.service}.#{@method_name} (try #{try_number})"
196
+ else
197
+ "Sending request to #{entry.service}.#{@method_name} (try #{try_number})"
198
+ end
199
+ end
200
+ loggable_metadata = metadata.to_h rescue {}
201
+ if request.is_a? Enumerable
202
+ request.lazy.map do |req|
203
+ log_single_request req, loggable_metadata
204
+ end
205
+ else
206
+ log_single_request request, loggable_metadata
207
+ end
208
+ end
209
+
210
+ def log_single_request request, metadata
211
+ request_content = request.respond_to?(:to_h) ? (request.to_h rescue {}) : request.to_s
212
+ if !request_content.empty? || !metadata.empty?
213
+ @stub_logger.debug do |entry|
214
+ entry.set "requestId", @request_id
215
+ entry.set "request", request_content
216
+ entry.set "headers", metadata
217
+ entry.message = "(request payload as #{request.class})"
218
+ end
219
+ end
220
+ request
221
+ end
222
+
223
+ def log_response response, try_number
224
+ return response unless @stub_logger
225
+ @stub_logger.info do |entry|
226
+ entry.set_system_name
227
+ entry.set_service
228
+ entry.set "rpcName", @method_name
229
+ entry.set "retryAttempt", try_number
230
+ entry.set "requestId", @request_id
231
+ case response
232
+ when StandardError
233
+ entry.set "exception", response.to_s
234
+ entry.message = "Received error for #{entry.service}.#{@method_name} (try #{try_number}): #{response}"
235
+ when Enumerable
236
+ entry.message = "Receiving stream for #{entry.service}.#{@method_name} (try #{try_number})"
237
+ else
238
+ entry.message = "Received response for #{entry.service}.#{@method_name} (try #{try_number})"
239
+ end
240
+ end
241
+ case response
242
+ when StandardError
243
+ response
244
+ when Enumerable
245
+ response.lazy.map do |resp|
246
+ log_single_response resp
247
+ end
248
+ else
249
+ log_single_response response
250
+ end
251
+ end
252
+
253
+ def log_single_response response
254
+ response_content = response.respond_to?(:to_h) ? (response.to_h rescue {}) : response.to_s
255
+ unless response_content.empty?
256
+ @stub_logger.debug do |entry|
257
+ entry.set "requestId", @request_id
258
+ entry.set "response", response_content
259
+ entry.message = "(response payload as #{response.class})"
260
+ end
261
+ end
262
+ response
155
263
  end
156
264
  end
157
265
  end
@@ -17,6 +17,7 @@ require "googleauth"
17
17
  require "gapic/grpc/service_stub/rpc_call"
18
18
  require "gapic/grpc/service_stub/channel"
19
19
  require "gapic/grpc/service_stub/channel_pool"
20
+ require "gapic/logging_concerns"
20
21
  require "gapic/universe_domain_concerns"
21
22
 
22
23
  module Gapic
@@ -32,6 +33,7 @@ module Gapic
32
33
  #
33
34
  class ServiceStub
34
35
  include UniverseDomainConcerns
36
+ include LoggingConcerns
35
37
 
36
38
  attr_reader :grpc_stub
37
39
  attr_reader :channel_pool
@@ -64,6 +66,9 @@ module Gapic
64
66
  # be used for intercepting calls before they are executed Interceptors are an EXPERIMENTAL API.
65
67
  # @param channel_pool_config [::Gapic::ServiceStub:ChannelPool::Configuration] The configuration for channel
66
68
  # pool. This argument will raise error when `credentials` is provided as a `::GRPC::Core::Channel`.
69
+ # @param logger [Logger,:default,nil] An explicit logger to use, or one
70
+ # of the values `:default` (the default) to construct a default logger,
71
+ # or `nil` to disable logging explicitly.
67
72
  #
68
73
  def initialize grpc_stub_class,
69
74
  credentials:,
@@ -72,13 +77,19 @@ module Gapic
72
77
  universe_domain: nil,
73
78
  channel_args: nil,
74
79
  interceptors: nil,
75
- channel_pool_config: nil
80
+ channel_pool_config: nil,
81
+ logger: :default
76
82
  raise ArgumentError, "grpc_stub_class is required" if grpc_stub_class.nil?
77
83
 
78
84
  setup_universe_domain universe_domain: universe_domain,
79
85
  endpoint: endpoint,
80
86
  endpoint_template: endpoint_template,
81
87
  credentials: credentials
88
+ setup_logging logger: logger,
89
+ system_name: grpc_stub_class,
90
+ service: grpc_stub_class,
91
+ endpoint: self.endpoint,
92
+ client_id: object_id
82
93
 
83
94
  @channel_pool = nil
84
95
  @grpc_stub = nil
@@ -102,7 +113,7 @@ module Gapic
102
113
  end
103
114
  @channel_pool = ChannelPool.new grpc_stub_class, endpoint: endpoint, credentials: credentials,
104
115
  channel_args: channel_args, interceptors: interceptors,
105
- config: channel_pool_config
116
+ config: channel_pool_config, stub_logger: stub_logger
106
117
  end
107
118
 
108
119
  def create_grpc_stub grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil
@@ -201,7 +212,8 @@ module Gapic
201
212
  #
202
213
  def call_rpc method_name, request, options: nil, &block
203
214
  if @channel_pool.nil?
204
- rpc_call = RpcCall.new @grpc_stub.method method_name
215
+ meth = @grpc_stub.method method_name
216
+ rpc_call = RpcCall.new meth, stub_logger: stub_logger, method_name: method_name
205
217
  rpc_call.call request, options: options, &block
206
218
  else
207
219
  @channel_pool.call_rpc method_name, request, options: options, &block
data/lib/gapic/headers.rb CHANGED
@@ -19,6 +19,8 @@ require "gapic/common/version"
19
19
  module Gapic
20
20
  # A collection of common header values.
21
21
  module Headers
22
+ # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
23
+
22
24
  ##
23
25
  # @param ruby_version [String] The ruby version. Defaults to `RUBY_VERSION`.
24
26
  # @param lib_name [String] The client library name.
@@ -32,10 +34,10 @@ module Gapic
32
34
  # `:grpc` to send the GRPC library version (if defined)
33
35
  # `:rest` to send the REST library version (if defined)
34
36
  # Defaults to `[:grpc]`
37
+ #
35
38
  def self.x_goog_api_client ruby_version: nil, lib_name: nil, lib_version: nil, gax_version: nil,
36
39
  gapic_version: nil, grpc_version: nil, rest_version: nil, protobuf_version: nil,
37
40
  transports_version_send: [:grpc]
38
-
39
41
  ruby_version ||= ::RUBY_VERSION
40
42
  gax_version ||= ::Gapic::Common::VERSION
41
43
  grpc_version ||= ::GRPC::VERSION if defined? ::GRPC::VERSION
@@ -52,4 +54,6 @@ module Gapic
52
54
  x_goog_api_client_header.join " ".freeze
53
55
  end
54
56
  end
57
+
58
+ # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
55
59
  end
@@ -0,0 +1,210 @@
1
+ # Copyright 2024 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 "google/cloud/env"
16
+ require "google/logging/message"
17
+ require "google/logging/structured_formatter"
18
+
19
+ module Gapic
20
+ ##
21
+ # A mixin module that handles logging setup for a stub.
22
+ #
23
+ module LoggingConcerns
24
+ ##
25
+ # The logger for this object.
26
+ #
27
+ # @return [Logger]
28
+ #
29
+ attr_reader :logger
30
+
31
+ ##
32
+ # @private
33
+ # A convenience object used by stub-based logging.
34
+ #
35
+ class StubLogger
36
+ OMIT_FILES = [
37
+ /^#{Regexp.escape __dir__}/
38
+ ].freeze
39
+
40
+ def initialize logger: nil, **kwargs
41
+ @logger = logger
42
+ @kwargs = kwargs
43
+ end
44
+
45
+ def log severity
46
+ return unless @logger
47
+ locations = caller_locations
48
+ @logger.add severity do
49
+ builder = LogEntryBuilder.new(**@kwargs)
50
+ builder.set_source_location_from locations
51
+ yield builder
52
+ builder.build
53
+ rescue StandardError
54
+ # Do nothing
55
+ end
56
+ end
57
+
58
+ def info &block
59
+ log Logger::INFO, &block
60
+ end
61
+
62
+ def debug &block
63
+ log Logger::DEBUG, &block
64
+ end
65
+
66
+ ##
67
+ # @private
68
+ # Builder for a log entry, passed to {StubLogger#log}.
69
+ #
70
+ class LogEntryBuilder
71
+ def initialize system_name: nil,
72
+ service: nil,
73
+ endpoint: nil,
74
+ client_id: nil
75
+ @system_name = system_name
76
+ @service = service
77
+ @endpoint = endpoint
78
+ @client_id = client_id
79
+ @message = nil
80
+ @caller_locations = caller_locations
81
+ @fields = { "clientId" => @client_id }
82
+ end
83
+
84
+ attr_reader :system_name
85
+
86
+ attr_reader :service
87
+
88
+ attr_reader :endpoint
89
+
90
+ attr_accessor :message
91
+
92
+ attr_writer :source_location
93
+
94
+ attr_reader :fields
95
+
96
+ def set name, value
97
+ fields[name] = value
98
+ end
99
+
100
+ def set_system_name
101
+ set "system", system_name
102
+ end
103
+
104
+ def set_service
105
+ set "serviceName", service
106
+ end
107
+
108
+ def set_credentials_fields creds
109
+ creds = creds.client if creds.respond_to? :client
110
+ set "credentialsId", creds.object_id
111
+ set "credentialsType", creds.class.name
112
+ set "credentialsScope", creds.scope if creds.respond_to? :scope
113
+ set "useSelfSignedJWT", creds.enable_self_signed_jwt? if creds.respond_to? :enable_self_signed_jwt?
114
+ set "universeDomain", creds.universe_domain if creds.respond_to? :universe_domain
115
+ end
116
+
117
+ def source_location
118
+ @source_location ||= Google::Logging::SourceLocation.for_caller omit_files: OMIT_FILES,
119
+ locations: @caller_locations
120
+ end
121
+
122
+ def set_source_location_from locations
123
+ @caller_locations = locations
124
+ @source_location = nil
125
+ end
126
+
127
+ def build
128
+ Google::Logging::Message.from message: message, source_location: source_location, fields: fields
129
+ end
130
+ end
131
+ end
132
+
133
+ ##
134
+ # @private
135
+ # Initialize logging concerns.
136
+ #
137
+ def setup_logging logger: :default,
138
+ stream: nil,
139
+ formatter: nil,
140
+ level: nil,
141
+ system_name: nil,
142
+ service: nil,
143
+ endpoint: nil,
144
+ client_id: nil
145
+ service = LoggingConcerns.normalize_service service
146
+ system_name = LoggingConcerns.normalize_system_name system_name
147
+ logging_env = ENV["GOOGLE_SDK_RUBY_LOGGING_GEMS"].to_s.downcase
148
+ logger = nil if ["false", "none"].include? logging_env
149
+ if logger == :default
150
+ logger = nil
151
+ if ["true", "all"].include?(logging_env) || logging_env.split(",").include?(system_name)
152
+ stream ||= $stderr
153
+ level ||= "DEBUG"
154
+ formatter ||= Google::Logging::StructuredFormatter.new if Google::Cloud::Env.get.logging_agent_expected?
155
+ logger = Logger.new stream, progname: system_name, level: level, formatter: formatter
156
+ end
157
+ end
158
+ @logger = logger
159
+ @stub_logger = StubLogger.new logger: logger,
160
+ system_name: system_name,
161
+ service: service,
162
+ endpoint: endpoint,
163
+ client_id: client_id
164
+ end
165
+
166
+ # @private
167
+ attr_reader :stub_logger
168
+
169
+ class << self
170
+ # @private
171
+ def random_uuid4
172
+ ary = Random.bytes 16
173
+ ary.setbyte 6, ((ary.getbyte(6) & 0x0f) | 0x40)
174
+ ary.setbyte 8, ((ary.getbyte(8) & 0x3f) | 0x80)
175
+ ary.unpack("H8H4H4H4H12").join "-"
176
+ end
177
+
178
+ # @private
179
+ def normalize_system_name input
180
+ case input
181
+ when String
182
+ input
183
+ when Class
184
+ input.name.split("::")[..-3]
185
+ .map { |elem| elem.scan(/[A-Z][A-Z]*(?=[A-Z][a-z0-9]|$)|[A-Z][a-z0-9]+/).map(&:downcase).join("_") }
186
+ .join("-")
187
+ else
188
+ "googleapis"
189
+ end
190
+ end
191
+
192
+ # @private
193
+ def normalize_service input
194
+ case input
195
+ when String
196
+ input
197
+ when Class
198
+ mod = input.name.split("::")[..-2].inject(Object) { |m, n| m.const_get n }
199
+ if mod.const_defined? "Service"
200
+ mod.const_get("Service").service_name
201
+ else
202
+ name_segments = input.name.split("::")[..-3]
203
+ mod = name_segments.inject(Object) { |m, n| m.const_get n }
204
+ name_segments.join "." if mod.const_defined? "Rest"
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -12,80 +12,40 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require "gapic/common/retry_policy"
16
+
15
17
  module Gapic
16
18
  class Operation
17
19
  ##
18
- # The policy for retrying operation reloads using an incremental backoff. A new object instance should be used for
19
- # every Operation invocation.
20
+ # The policy for retrying operation reloads using an incremental backoff.
21
+ #
22
+ # A new object instance should be used for every Operation invocation.
20
23
  #
21
- class RetryPolicy
24
+ class RetryPolicy < Gapic::Common::RetryPolicy
25
+ # @return [Numeric] Default initial delay in seconds.
26
+ DEFAULT_INITIAL_DELAY = 10
27
+ # @return [Numeric] Default maximum delay in seconds.
28
+ DEFAULT_MAX_DELAY = 300 # Five minutes
29
+ # @return [Numeric] Default delay scaling factor for subsequent retry attempts.
30
+ DEFAULT_MULTIPLIER = 1.3
31
+ # @return [Numeric] Default timeout threshold value in seconds.
32
+ DEFAULT_TIMEOUT = 3600 # One hour
33
+
22
34
  ##
23
35
  # Create new Operation RetryPolicy.
24
36
  #
25
- # @param initial_delay [Numeric] client-side timeout
26
- # @param multiplier [Numeric] client-side timeout
27
- # @param max_delay [Numeric] client-side timeout
28
- # @param timeout [Numeric] client-side timeout
37
+ # @param initial_delay [Numeric] Initial delay in seconds.
38
+ # @param multiplier [Numeric] The delay scaling factor for each subsequent retry attempt.
39
+ # @param max_delay [Numeric] Maximum delay in seconds.
40
+ # @param timeout [Numeric] Timeout threshold value in seconds.
29
41
  #
30
42
  def initialize initial_delay: nil, multiplier: nil, max_delay: nil, timeout: nil
31
- @initial_delay = initial_delay
32
- @multiplier = multiplier
33
- @max_delay = max_delay
34
- @timeout = timeout
35
- @delay = nil
36
- end
37
-
38
- def initial_delay
39
- @initial_delay || 10
40
- end
41
-
42
- def multiplier
43
- @multiplier || 1.3
44
- end
45
-
46
- def max_delay
47
- @max_delay || 300 # Five minutes
48
- end
49
-
50
- def timeout
51
- @timeout || 3600 # One hour
52
- end
53
-
54
- def call
55
- return unless retry?
56
-
57
- delay!
58
- increment_delay!
59
-
60
- true
61
- end
62
-
63
- private
64
-
65
- def deadline
66
- # memoize the deadline
67
- @deadline ||= Time.now + timeout
68
- end
69
-
70
- def retry?
71
- deadline > Time.now
72
- end
73
-
74
- ##
75
- # The current delay value.
76
- def delay
77
- @delay || initial_delay
78
- end
79
-
80
- def delay!
81
- # Call Kernel.sleep so we can stub it.
82
- Kernel.sleep delay
83
- end
84
-
85
- ##
86
- # Calculate and set the next delay value.
87
- def increment_delay!
88
- @delay = [delay * multiplier, max_delay].min
43
+ super(
44
+ initial_delay: initial_delay || DEFAULT_INITIAL_DELAY,
45
+ max_delay: max_delay || DEFAULT_MAX_DELAY,
46
+ multiplier: multiplier || DEFAULT_MULTIPLIER,
47
+ timeout: timeout || DEFAULT_TIMEOUT
48
+ )
89
49
  end
90
50
  end
91
51
  end