phobos 1.8.0 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +26 -0
- data/.rubocop_common.yml +29 -0
- data/.rubocop_todo.yml +7 -0
- data/.rubosync.yml +2 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +2 -0
- data/README.md +11 -7
- data/Rakefile +5 -3
- data/bin/console +3 -1
- data/bin/phobos +1 -0
- data/examples/handler_saving_events_database.rb +4 -2
- data/examples/handler_using_async_producer.rb +3 -1
- data/examples/publishing_messages_without_consumer.rb +8 -4
- data/lib/phobos.rb +61 -31
- data/lib/phobos/actions/process_batch.rb +3 -1
- data/lib/phobos/actions/process_message.rb +54 -31
- data/lib/phobos/cli.rb +2 -1
- data/lib/phobos/cli/runner.rb +3 -3
- data/lib/phobos/cli/start.rb +17 -25
- data/lib/phobos/constants.rb +33 -0
- data/lib/phobos/deep_struct.rb +13 -14
- data/lib/phobos/echo_handler.rb +2 -0
- data/lib/phobos/errors.rb +2 -0
- data/lib/phobos/executor.rb +39 -42
- data/lib/phobos/handler.rb +7 -7
- data/lib/phobos/instrumentation.rb +4 -2
- data/lib/phobos/listener.rb +81 -74
- data/lib/phobos/log.rb +23 -0
- data/lib/phobos/producer.rb +18 -14
- data/lib/phobos/test.rb +2 -0
- data/lib/phobos/test/helper.rb +4 -4
- data/lib/phobos/version.rb +3 -1
- data/phobos.gemspec +21 -14
- metadata +44 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2521d4d12e282bf2f2fc1ae720c52cfdab4637e2464840e196c7fd275f316c9d
|
4
|
+
data.tar.gz: 067d38a797a93bd7300e31c34b16e2bdea1dfd43ff6eddaaeac678b33ce9944c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2808ac665d54a407da693e3ff0c8635ce0ef0daaf9815acf22c47628f268866cb350925a77477631f338cff360f79cc4e55b113a83c66e582333698bc19c800
|
7
|
+
data.tar.gz: 896136ecf3e4e41eb255c043b3982c55eb008a057ea97b47082b44826f2c59c8c525255f2fa3b9f2271df3e494dcc826d17c1136d7308c10b43bc63291fa4121
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
###########################
|
2
|
+
# Configuration for rubocop
|
3
|
+
# in .rubocop.yml
|
4
|
+
|
5
|
+
##############
|
6
|
+
# Global rules
|
7
|
+
# see .rubocop_common.yml
|
8
|
+
|
9
|
+
##############
|
10
|
+
# Inherit default rules first, and then override those rules with
|
11
|
+
# our violation whitelist.
|
12
|
+
inherit_from:
|
13
|
+
- .rubocop_common.yml
|
14
|
+
- .rubocop_todo.yml
|
15
|
+
|
16
|
+
##############
|
17
|
+
# Project specific overrides here, example:
|
18
|
+
# Metrics/BlockLength:
|
19
|
+
# Exclude:
|
20
|
+
# - 'tasks/the_huge_task.rake'
|
21
|
+
|
22
|
+
AllCops:
|
23
|
+
Exclude:
|
24
|
+
- spec/**/*
|
25
|
+
- examples/**/*
|
26
|
+
TargetRubyVersion: 2.3
|
data/.rubocop_common.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
##############
|
2
|
+
# Global rules
|
3
|
+
AllCops:
|
4
|
+
Exclude:
|
5
|
+
- db/**/*
|
6
|
+
TargetRubyVersion: 2.3
|
7
|
+
|
8
|
+
Rails:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Style/SymbolArray:
|
12
|
+
EnforcedStyle: brackets
|
13
|
+
|
14
|
+
Metrics/LineLength:
|
15
|
+
Max: 100
|
16
|
+
|
17
|
+
Metrics/BlockLength:
|
18
|
+
Exclude:
|
19
|
+
- '*.gemspec'
|
20
|
+
- 'spec/**/*.rb'
|
21
|
+
|
22
|
+
Documentation:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Metrics/MethodLength:
|
26
|
+
Max: 15
|
27
|
+
|
28
|
+
Metrics/AbcSize:
|
29
|
+
Max: 17
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2018-10-22 14:47:09 +0200 using RuboCop version 0.59.2.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
data/.rubosync.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
7
7
|
## UNRELEASED
|
8
8
|
|
9
|
+
## [1.8.1] - 2018-11-23
|
10
|
+
### Added
|
11
|
+
- Added ability to send partition keys separate from messsage keys.
|
12
|
+
|
9
13
|
## [1.8.0] - 2018-07-22
|
10
14
|
### Added
|
11
15
|
- Possibility to configure a custom logger #81
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -338,21 +338,21 @@ The configuration file is organized in 6 sections. Take a look at the example fi
|
|
338
338
|
|
339
339
|
The file will be parsed through ERB so ERB syntax/file extension is supported beside the YML format.
|
340
340
|
|
341
|
-
__logger__ configures the logger for all Phobos components. It automatically
|
341
|
+
__logger__ configures the logger for all Phobos components. It automatically
|
342
342
|
outputs to `STDOUT` and it saves the log in the configured file.
|
343
343
|
|
344
|
-
__kafka__ provides configurations for every `Kafka::Client` created over the application.
|
344
|
+
__kafka__ provides configurations for every `Kafka::Client` created over the application.
|
345
345
|
All [options supported by `ruby-kafka`][ruby-kafka-client] can be provided.
|
346
346
|
|
347
|
-
__producer__ provides configurations for all producers created over the application,
|
348
|
-
the options are the same for regular and async producers.
|
347
|
+
__producer__ provides configurations for all producers created over the application,
|
348
|
+
the options are the same for regular and async producers.
|
349
349
|
All [options supported by `ruby-kafka`][ruby-kafka-producer] can be provided.
|
350
350
|
|
351
|
-
__consumer__ provides configurations for all consumer groups created over the application.
|
351
|
+
__consumer__ provides configurations for all consumer groups created over the application.
|
352
352
|
All [options supported by `ruby-kafka`][ruby-kafka-consumer] can be provided.
|
353
353
|
|
354
|
-
__backoff__ Phobos provides automatic retries for your handlers. If an exception
|
355
|
-
is raised, the listener will retry following the back off configured here.
|
354
|
+
__backoff__ Phobos provides automatic retries for your handlers. If an exception
|
355
|
+
is raised, the listener will retry following the back off configured here.
|
356
356
|
Backoff can also be configured per listener.
|
357
357
|
|
358
358
|
__listeners__ is the list of listeners configured. Each listener represents a consumer group.
|
@@ -537,6 +537,10 @@ end
|
|
537
537
|
|
538
538
|
Bug reports and pull requests are welcome on GitHub at https://github.com/klarna/phobos.
|
539
539
|
|
540
|
+
## Linting
|
541
|
+
|
542
|
+
Phobos projects Rubocop to lint the code, and in addition all projects use [Rubocop Rules](https://github.com/klippx/rubocop_rules) to maintain a shared rubocop configuration. Updates to the shared configurations are done in [phobos/shared](https://github.com/phobos/shared) repo, where you can also find instructions on how to apply the new settings to the Phobos projects.
|
543
|
+
|
540
544
|
## Acknowledgements
|
541
545
|
|
542
546
|
Thanks to Sebastian Norde for the awesome logo!
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'irb'
|
4
5
|
require 'bundler/setup'
|
@@ -11,7 +12,8 @@ require 'phobos'
|
|
11
12
|
# require 'pry'
|
12
13
|
# Pry.start
|
13
14
|
|
14
|
-
config_path = ENV['CONFIG_PATH'] ||
|
15
|
+
config_path = ENV['CONFIG_PATH'] ||
|
16
|
+
(File.exist?('config/phobos.yml') ? 'config/phobos.yml' : 'config/phobos.yml.example')
|
15
17
|
Phobos.configure(config_path)
|
16
18
|
|
17
19
|
IRB.start
|
data/bin/phobos
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
4
|
# This example assumes that you want to save all events in your database for
|
3
5
|
# recovery purposes. The consumer will process the message and perform other
|
@@ -10,7 +12,7 @@ class HandlerSavingEventsDatabase
|
|
10
12
|
include Phobos::Handler
|
11
13
|
include Phobos::Producer
|
12
14
|
|
13
|
-
def self.around_consume(payload,
|
15
|
+
def self.around_consume(payload, _metadata)
|
14
16
|
#
|
15
17
|
# Let's assume `::from_message` will initialize our object with `payload`
|
16
18
|
#
|
@@ -39,7 +41,7 @@ class HandlerSavingEventsDatabase
|
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
42
|
-
def consume(payload,
|
44
|
+
def consume(payload, _metadata)
|
43
45
|
#
|
44
46
|
# Process the event, it might index it to elasticsearch or notify other
|
45
47
|
# system, you should process your message inside this method.
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
4
|
# This example assumes you want to process the event and publish another
|
3
5
|
# one to kafka. A new event is always published thus we want to use the async producer
|
@@ -9,7 +11,7 @@ class HandlerUsingAsyncProducer
|
|
9
11
|
|
10
12
|
PUBLISH_TO 'another-topic'
|
11
13
|
|
12
|
-
def consume(payload,
|
14
|
+
def consume(payload, _metadata)
|
13
15
|
producer.async_publish(PUBLISH_TO, "#{payload}-#{rand}")
|
14
16
|
end
|
15
17
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
4
|
# This example assumes you want to create a threaded kafka generator which
|
3
5
|
# publish a stream of kafka messages without consuming them. It also shows
|
@@ -19,9 +21,9 @@ end
|
|
19
21
|
# Trapping signals to properly stop this generator
|
20
22
|
#
|
21
23
|
@stop = false
|
22
|
-
|
24
|
+
[:INT, :TERM, :QUIT].each do |signal|
|
23
25
|
Signal.trap(signal) do
|
24
|
-
puts
|
26
|
+
puts 'Stopping'
|
25
27
|
@stop = true
|
26
28
|
end
|
27
29
|
end
|
@@ -32,6 +34,7 @@ Thread.new do
|
|
32
34
|
|
33
35
|
loop do
|
34
36
|
break if @stop
|
37
|
+
|
35
38
|
key = SecureRandom.uuid
|
36
39
|
payload = Time.now.utc.to_json
|
37
40
|
|
@@ -50,7 +53,7 @@ Thread.new do
|
|
50
53
|
# the producer can write to Kafka. Eventually we'll get some buffer overflows
|
51
54
|
#
|
52
55
|
rescue Kafka::BufferOverflow => e
|
53
|
-
puts
|
56
|
+
puts '| waiting'
|
54
57
|
sleep(1)
|
55
58
|
retry
|
56
59
|
end
|
@@ -67,7 +70,8 @@ Thread.new do
|
|
67
70
|
.async_producer_shutdown
|
68
71
|
|
69
72
|
#
|
70
|
-
# Since no client was configured (we can do this with
|
73
|
+
# Since no client was configured (we can do this with
|
74
|
+
# `MyProducer.producer.configure_kafka_client`)
|
71
75
|
# we must get the auto generated one and close it properly
|
72
76
|
#
|
73
77
|
MyProducer
|
data/lib/phobos.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'ostruct'
|
2
4
|
require 'securerandom'
|
3
5
|
require 'yaml'
|
@@ -13,6 +15,8 @@ require 'erb'
|
|
13
15
|
|
14
16
|
require 'phobos/deep_struct'
|
15
17
|
require 'phobos/version'
|
18
|
+
require 'phobos/constants'
|
19
|
+
require 'phobos/log'
|
16
20
|
require 'phobos/instrumentation'
|
17
21
|
require 'phobos/errors'
|
18
22
|
require 'phobos/listener'
|
@@ -31,16 +35,15 @@ module Phobos
|
|
31
35
|
attr_accessor :silence_log
|
32
36
|
|
33
37
|
def configure(configuration)
|
34
|
-
@config =
|
38
|
+
@config = fetch_configuration(configuration)
|
35
39
|
@config.class.send(:define_method, :producer_hash) { Phobos.config.producer&.to_hash }
|
36
40
|
@config.class.send(:define_method, :consumer_hash) { Phobos.config.consumer&.to_hash }
|
37
41
|
@config.listeners ||= []
|
38
42
|
configure_logger
|
39
|
-
logger.info { Hash(message: 'Phobos configured', env: ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'N/A') }
|
40
43
|
end
|
41
44
|
|
42
|
-
def add_listeners(
|
43
|
-
listeners_config =
|
45
|
+
def add_listeners(configuration)
|
46
|
+
listeners_config = fetch_configuration(configuration)
|
44
47
|
@config.listeners += listeners_config.listeners
|
45
48
|
end
|
46
49
|
|
@@ -55,61 +58,88 @@ module Phobos
|
|
55
58
|
ExponentialBackoff.new(min, max).tap { |backoff| backoff.randomize_factor = rand }
|
56
59
|
end
|
57
60
|
|
61
|
+
def deprecate(message)
|
62
|
+
warn "DEPRECATION WARNING: #{message} #{Kernel.caller.first}"
|
63
|
+
end
|
64
|
+
|
58
65
|
# :nodoc:
|
59
66
|
def configure_logger
|
60
|
-
ruby_kafka = config.logger.ruby_kafka
|
61
|
-
|
62
67
|
Logging.backtrace(true)
|
63
68
|
Logging.logger.root.level = silence_log ? :fatal : config.logger.level
|
64
|
-
appenders = logger_appenders
|
65
69
|
|
66
|
-
|
70
|
+
configure_ruby_kafka_logger
|
71
|
+
configure_phobos_logger
|
67
72
|
|
68
|
-
|
69
|
-
|
70
|
-
elsif ruby_kafka
|
71
|
-
@ruby_kafka_logger = Logging.logger['RubyKafka']
|
72
|
-
@ruby_kafka_logger.appenders = appenders
|
73
|
-
@ruby_kafka_logger.level = silence_log ? :fatal : ruby_kafka.level
|
73
|
+
logger.info do
|
74
|
+
Hash(message: 'Phobos configured', env: ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'N/A')
|
74
75
|
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def fetch_configuration(configuration)
|
81
|
+
DeepStruct.new(read_configuration(configuration))
|
82
|
+
end
|
83
|
+
|
84
|
+
def read_configuration(configuration)
|
85
|
+
return configuration.to_h if configuration.respond_to?(:to_h)
|
86
|
+
|
87
|
+
YAML.safe_load(
|
88
|
+
ERB.new(
|
89
|
+
File.read(File.expand_path(configuration))
|
90
|
+
).result,
|
91
|
+
[Symbol],
|
92
|
+
[],
|
93
|
+
true
|
94
|
+
)
|
95
|
+
end
|
75
96
|
|
97
|
+
def configure_phobos_logger
|
76
98
|
if config.custom_logger
|
77
99
|
@logger = config.custom_logger
|
78
100
|
else
|
79
101
|
@logger = Logging.logger[self]
|
80
|
-
@logger.appenders =
|
102
|
+
@logger.appenders = logger_appenders
|
81
103
|
end
|
82
104
|
end
|
83
105
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
106
|
+
def configure_ruby_kafka_logger
|
107
|
+
if config.custom_kafka_logger
|
108
|
+
@ruby_kafka_logger = config.custom_kafka_logger
|
109
|
+
elsif config.logger.ruby_kafka
|
110
|
+
@ruby_kafka_logger = Logging.logger['RubyKafka']
|
111
|
+
@ruby_kafka_logger.appenders = logger_appenders
|
112
|
+
@ruby_kafka_logger.level = silence_log ? :fatal : config.logger.ruby_kafka.level
|
113
|
+
else
|
114
|
+
@ruby_kafka_logger = nil
|
115
|
+
end
|
116
|
+
end
|
93
117
|
|
118
|
+
def logger_appenders
|
94
119
|
appenders = [Logging.appenders.stdout(layout: stdout_layout)]
|
95
120
|
|
96
121
|
if log_file
|
97
122
|
FileUtils.mkdir_p(File.dirname(log_file))
|
98
123
|
appenders << Logging.appenders.file(log_file, layout: json_layout)
|
99
124
|
end
|
125
|
+
|
100
126
|
appenders
|
101
127
|
end
|
102
128
|
|
103
|
-
def
|
104
|
-
|
129
|
+
def log_file
|
130
|
+
config.logger.file
|
105
131
|
end
|
106
132
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
return configuration.to_h if configuration.respond_to?(:to_h)
|
133
|
+
def json_layout
|
134
|
+
Logging.layouts.json(date_pattern: Constants::LOG_DATE_PATTERN)
|
135
|
+
end
|
111
136
|
|
112
|
-
|
137
|
+
def stdout_layout
|
138
|
+
if config.logger.stdout_json == true
|
139
|
+
json_layout
|
140
|
+
else
|
141
|
+
Logging.layouts.pattern(date_pattern: Constants::LOG_DATE_PATTERN)
|
142
|
+
end
|
113
143
|
end
|
114
144
|
end
|
115
145
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phobos
|
2
4
|
module Actions
|
3
5
|
class ProcessBatch
|
@@ -17,7 +19,7 @@ module Phobos
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def execute
|
20
|
-
instrument('listener.process_batch', @metadata) do |
|
22
|
+
instrument('listener.process_batch', @metadata) do |_metadata|
|
21
23
|
@batch.messages.each do |message|
|
22
24
|
Phobos::Actions::ProcessMessage.new(
|
23
25
|
listener: @listener,
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phobos
|
2
4
|
module Actions
|
3
5
|
class ProcessMessage
|
@@ -19,31 +21,12 @@ module Phobos
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def execute
|
22
|
-
backoff = @listener.create_exponential_backoff
|
23
24
|
payload = force_encoding(@message.value)
|
24
25
|
|
25
26
|
begin
|
26
27
|
process_message(payload)
|
27
|
-
rescue => e
|
28
|
-
|
29
|
-
interval = backoff.interval_at(retry_count).round(2)
|
30
|
-
|
31
|
-
error = {
|
32
|
-
waiting_time: interval,
|
33
|
-
exception_class: e.class.name,
|
34
|
-
exception_message: e.message,
|
35
|
-
backtrace: e.backtrace
|
36
|
-
}
|
37
|
-
|
38
|
-
instrument('listener.retry_handler_error', error.merge(@metadata)) do
|
39
|
-
Phobos.logger.error do
|
40
|
-
{ message: "error processing message, waiting #{interval}s" }.merge(error).merge(@metadata)
|
41
|
-
end
|
42
|
-
|
43
|
-
snooze(interval)
|
44
|
-
end
|
45
|
-
|
46
|
-
@metadata.merge!(retry_count: retry_count + 1)
|
28
|
+
rescue StandardError => e
|
29
|
+
handle_error(e)
|
47
30
|
retry
|
48
31
|
end
|
49
32
|
end
|
@@ -69,19 +52,14 @@ module Phobos
|
|
69
52
|
def process_message(payload)
|
70
53
|
instrument('listener.process_message', @metadata) do
|
71
54
|
handler = @listener.handler_class.new
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
Phobos.deprecate("before_consume now expects metadata as second argument, please update your consumer."\
|
76
|
-
" This will not be backwards compatible in the future.")
|
77
|
-
handler.before_consume(payload)
|
78
|
-
end
|
79
|
-
consume_block = Proc.new { handler.consume(preprocessed_payload, @metadata) }
|
55
|
+
|
56
|
+
preprocessed_payload = before_consume(handler, payload)
|
57
|
+
consume_block = proc { handler.consume(preprocessed_payload, @metadata) }
|
80
58
|
|
81
59
|
if @listener.handler_class.respond_to?(:around_consume)
|
82
60
|
# around_consume class method implementation
|
83
|
-
Phobos.deprecate(
|
84
|
-
|
61
|
+
Phobos.deprecate('around_consume has been moved to instance method, '\
|
62
|
+
'please update your consumer. This will not be backwards compatible in the future.')
|
85
63
|
@listener.handler_class.around_consume(preprocessed_payload, @metadata, &consume_block)
|
86
64
|
else
|
87
65
|
# around_consume instance method implementation
|
@@ -89,6 +67,51 @@ module Phobos
|
|
89
67
|
end
|
90
68
|
end
|
91
69
|
end
|
70
|
+
|
71
|
+
def before_consume(handler, payload)
|
72
|
+
handler.before_consume(payload, @metadata)
|
73
|
+
rescue ArgumentError
|
74
|
+
Phobos.deprecate('before_consume now expects metadata as second argument, '\
|
75
|
+
'please update your consumer. This will not be backwards compatible in the future.')
|
76
|
+
handler.before_consume(payload)
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_error(error)
|
80
|
+
error_hash = {
|
81
|
+
waiting_time: backoff_interval,
|
82
|
+
exception_class: error.class.name,
|
83
|
+
exception_message: error.message,
|
84
|
+
backtrace: error.backtrace
|
85
|
+
}
|
86
|
+
|
87
|
+
instrument('listener.retry_handler_error', error_hash.merge(@metadata)) do
|
88
|
+
Phobos.logger.error do
|
89
|
+
{ message: "error processing message, waiting #{backoff_interval}s" }
|
90
|
+
.merge(error_hash)
|
91
|
+
.merge(@metadata)
|
92
|
+
end
|
93
|
+
|
94
|
+
snooze(backoff_interval)
|
95
|
+
end
|
96
|
+
|
97
|
+
increment_retry_count
|
98
|
+
end
|
99
|
+
|
100
|
+
def retry_count
|
101
|
+
@metadata[:retry_count]
|
102
|
+
end
|
103
|
+
|
104
|
+
def increment_retry_count
|
105
|
+
@metadata[:retry_count] = retry_count + 1
|
106
|
+
end
|
107
|
+
|
108
|
+
def backoff
|
109
|
+
@backoff ||= @listener.create_exponential_backoff
|
110
|
+
end
|
111
|
+
|
112
|
+
def backoff_interval
|
113
|
+
backoff.interval_at(retry_count).round(2)
|
114
|
+
end
|
92
115
|
end
|
93
116
|
end
|
94
117
|
end
|