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
data/lib/phobos/cli.rb
CHANGED
data/lib/phobos/cli/runner.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phobos
|
2
4
|
module CLI
|
3
5
|
class Runner
|
4
|
-
|
5
|
-
SIGNALS = %i( INT TERM QUIT ).freeze
|
6
|
+
SIGNALS = [:INT, :TERM, :QUIT].freeze
|
6
7
|
|
7
8
|
def initialize
|
8
9
|
@signal_queue = []
|
@@ -42,7 +43,6 @@ module Phobos
|
|
42
43
|
writer.write_nonblock('.')
|
43
44
|
signal_queue << signal
|
44
45
|
end
|
45
|
-
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
data/lib/phobos/cli/start.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'phobos/cli/runner'
|
2
4
|
|
3
5
|
module Phobos
|
4
6
|
module CLI
|
5
7
|
class Start
|
6
8
|
def initialize(options)
|
7
|
-
unless options[:skip_config]
|
8
|
-
@config_file = File.expand_path(options[:config])
|
9
|
-
end
|
9
|
+
@config_file = File.expand_path(options[:config]) unless options[:skip_config]
|
10
10
|
@boot_file = File.expand_path(options[:boot])
|
11
11
|
|
12
|
-
if options[:listeners]
|
13
|
-
@listeners_file = File.expand_path(options[:listeners])
|
14
|
-
end
|
12
|
+
@listeners_file = File.expand_path(options[:listeners]) if options[:listeners]
|
15
13
|
end
|
16
14
|
|
17
15
|
def execute
|
@@ -22,9 +20,7 @@ module Phobos
|
|
22
20
|
Phobos.configure(config_file)
|
23
21
|
end
|
24
22
|
|
25
|
-
if listeners_file
|
26
|
-
Phobos.add_listeners(listeners_file)
|
27
|
-
end
|
23
|
+
Phobos.add_listeners(listeners_file) if listeners_file
|
28
24
|
|
29
25
|
validate_listeners!
|
30
26
|
|
@@ -36,37 +32,33 @@ module Phobos
|
|
36
32
|
attr_reader :config_file, :boot_file, :listeners_file
|
37
33
|
|
38
34
|
def validate_config_file!
|
39
|
-
|
40
|
-
Phobos::CLI.logger.error { Hash(message: "Config file not found (#{config_file})") }
|
41
|
-
exit(1)
|
42
|
-
end
|
35
|
+
File.exist?(config_file) || error_exit("Config file not found (#{config_file})")
|
43
36
|
end
|
44
37
|
|
45
38
|
def validate_listeners!
|
46
39
|
Phobos.config.listeners.each do |listener|
|
47
|
-
|
40
|
+
handler = listener.handler
|
48
41
|
|
49
|
-
|
50
|
-
handler_class.constantize
|
51
|
-
rescue NameError
|
52
|
-
Phobos::CLI.logger.error { Hash(message: "Handler '#{handler_class}' not defined") }
|
53
|
-
exit(1)
|
54
|
-
end
|
42
|
+
Object.const_defined?(handler) || error_exit("Handler '#{handler}' not defined")
|
55
43
|
|
56
44
|
delivery = listener.delivery
|
57
45
|
if delivery.nil?
|
58
46
|
Phobos::CLI.logger.warn do
|
59
|
-
Hash(message: "Delivery option should be specified, defaulting to 'batch'
|
47
|
+
Hash(message: "Delivery option should be specified, defaulting to 'batch'"\
|
48
|
+
' - specify this option to silence this message')
|
60
49
|
end
|
61
50
|
elsif !Listener::DELIVERY_OPTS.include?(delivery)
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
exit(1)
|
51
|
+
error_exit("Invalid delivery option '#{delivery}'. Please specify one of: "\
|
52
|
+
"#{Listener::DELIVERY_OPTS.join(', ')}")
|
66
53
|
end
|
67
54
|
end
|
68
55
|
end
|
69
56
|
|
57
|
+
def error_exit(msg)
|
58
|
+
Phobos::CLI.logger.error { Hash(message: msg) }
|
59
|
+
exit(1)
|
60
|
+
end
|
61
|
+
|
70
62
|
def load_boot_file
|
71
63
|
load(boot_file) if File.exist?(boot_file)
|
72
64
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phobos
|
4
|
+
module Constants
|
5
|
+
LOG_DATE_PATTERN = '%Y-%m-%dT%H:%M:%S:%L%zZ'
|
6
|
+
|
7
|
+
KAFKA_CONSUMER_OPTS = [
|
8
|
+
:session_timeout,
|
9
|
+
:offset_commit_interval,
|
10
|
+
:offset_commit_threshold,
|
11
|
+
:heartbeat_interval,
|
12
|
+
:offset_retention_time
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
LISTENER_OPTS = [
|
16
|
+
:handler,
|
17
|
+
:group_id,
|
18
|
+
:topic,
|
19
|
+
:min_bytes,
|
20
|
+
:max_wait_time,
|
21
|
+
:force_encoding,
|
22
|
+
:start_from_beginning,
|
23
|
+
:max_bytes_per_partition,
|
24
|
+
:backoff,
|
25
|
+
:delivery,
|
26
|
+
:session_timeout,
|
27
|
+
:offset_commit_interval,
|
28
|
+
:offset_commit_threshold,
|
29
|
+
:heartbeat_interval,
|
30
|
+
:offset_retention_time
|
31
|
+
].freeze
|
32
|
+
end
|
33
|
+
end
|
data/lib/phobos/deep_struct.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Please use this with at least the same consideration as you would when using OpenStruct.
|
2
4
|
# Right now we only use this to parse our internal configuration files. It is not meant to
|
3
5
|
# be used on incoming data.
|
@@ -5,36 +7,33 @@ module Phobos
|
|
5
7
|
class DeepStruct < OpenStruct
|
6
8
|
# Based on
|
7
9
|
# https://docs.omniref.com/ruby/2.3.0/files/lib/ostruct.rb#line=88
|
8
|
-
def initialize(hash=nil)
|
10
|
+
def initialize(hash = nil)
|
9
11
|
@table = {}
|
10
12
|
@hash_table = {}
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@hash_table[k] = v
|
17
|
-
end
|
14
|
+
hash&.each_pair do |key, value|
|
15
|
+
key = key.to_sym
|
16
|
+
@table[key] = to_deep_struct(value)
|
17
|
+
@hash_table[key] = value
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_h
|
22
22
|
@hash_table.dup
|
23
23
|
end
|
24
|
-
|
24
|
+
alias to_hash to_h
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
-
def to_deep_struct(
|
29
|
-
case
|
28
|
+
def to_deep_struct(value)
|
29
|
+
case value
|
30
30
|
when Hash
|
31
|
-
self.class.new(
|
31
|
+
self.class.new(value)
|
32
32
|
when Enumerable
|
33
|
-
|
33
|
+
value.map { |el| to_deep_struct(el) }
|
34
34
|
else
|
35
|
-
|
35
|
+
value
|
36
36
|
end
|
37
37
|
end
|
38
|
-
protected :to_deep_struct
|
39
38
|
end
|
40
39
|
end
|
data/lib/phobos/echo_handler.rb
CHANGED
data/lib/phobos/errors.rb
CHANGED
data/lib/phobos/executor.rb
CHANGED
@@ -1,23 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phobos
|
2
4
|
class Executor
|
3
5
|
include Phobos::Instrumentation
|
4
|
-
|
5
|
-
handler
|
6
|
-
group_id
|
7
|
-
topic
|
8
|
-
min_bytes
|
9
|
-
max_wait_time
|
10
|
-
force_encoding
|
11
|
-
start_from_beginning
|
12
|
-
max_bytes_per_partition
|
13
|
-
backoff
|
14
|
-
delivery
|
15
|
-
session_timeout
|
16
|
-
offset_commit_interval
|
17
|
-
offset_commit_threshold
|
18
|
-
heartbeat_interval
|
19
|
-
offset_retention_time
|
20
|
-
).freeze
|
6
|
+
include Phobos::Log
|
21
7
|
|
22
8
|
def initialize
|
23
9
|
@threads = Concurrent::Array.new
|
@@ -26,7 +12,7 @@ module Phobos
|
|
26
12
|
listener_configs = config.to_hash.deep_symbolize_keys
|
27
13
|
max_concurrency = listener_configs[:max_concurrency] || 1
|
28
14
|
Array.new(max_concurrency).map do
|
29
|
-
configs = listener_configs.select { |k| LISTENER_OPTS.include?(k) }
|
15
|
+
configs = listener_configs.select { |k| Constants::LISTENER_OPTS.include?(k) }
|
30
16
|
Phobos::Listener.new(configs.merge(handler: handler_class))
|
31
17
|
end
|
32
18
|
end
|
@@ -51,10 +37,17 @@ module Phobos
|
|
51
37
|
|
52
38
|
def stop
|
53
39
|
return if @signal_to_stop
|
40
|
+
|
54
41
|
instrument('executor.stop') do
|
55
42
|
@signal_to_stop = true
|
56
43
|
@listeners.each(&:stop)
|
57
|
-
@threads.select(&:alive?).each
|
44
|
+
@threads.select(&:alive?).each do |thread|
|
45
|
+
begin
|
46
|
+
thread.wakeup
|
47
|
+
rescue StandardError
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
58
51
|
@thread_pool&.shutdown
|
59
52
|
@thread_pool&.wait_for_termination
|
60
53
|
Phobos.logger.info { Hash(message: 'Executor stopped') }
|
@@ -63,44 +56,48 @@ module Phobos
|
|
63
56
|
|
64
57
|
private
|
65
58
|
|
66
|
-
def error_metadata(
|
59
|
+
def error_metadata(exception)
|
67
60
|
{
|
68
|
-
exception_class:
|
69
|
-
exception_message:
|
70
|
-
backtrace:
|
61
|
+
exception_class: exception.class.name,
|
62
|
+
exception_message: exception.message,
|
63
|
+
backtrace: exception.backtrace
|
71
64
|
}
|
72
65
|
end
|
73
66
|
|
67
|
+
# rubocop:disable Lint/RescueException
|
74
68
|
def run_listener(listener)
|
75
69
|
retry_count = 0
|
76
|
-
backoff = listener.create_exponential_backoff
|
77
70
|
|
78
71
|
begin
|
79
72
|
listener.start
|
80
73
|
rescue Exception => e
|
81
|
-
|
82
|
-
# When "listener#start" is interrupted it's safe to assume that the consumer
|
83
|
-
# and the kafka client were properly stopped, it's safe to call start
|
84
|
-
# again
|
85
|
-
#
|
86
|
-
interval = backoff.interval_at(retry_count).round(2)
|
87
|
-
metadata = {
|
88
|
-
listener_id: listener.id,
|
89
|
-
retry_count: retry_count,
|
90
|
-
waiting_time: interval
|
91
|
-
}.merge(error_metadata(e))
|
92
|
-
|
93
|
-
instrument('executor.retry_listener_error', metadata) do
|
94
|
-
Phobos.logger.error { Hash(message: "Listener crashed, waiting #{interval}s (#{e.message})").merge(metadata)}
|
95
|
-
sleep interval
|
96
|
-
end
|
97
|
-
|
74
|
+
handle_crashed_listener(listener, e, retry_count)
|
98
75
|
retry_count += 1
|
99
76
|
retry unless @signal_to_stop
|
100
77
|
end
|
101
78
|
rescue Exception => e
|
102
|
-
|
79
|
+
log_error("Failed to run listener (#{e.message})", error_metadata(e))
|
103
80
|
raise e
|
104
81
|
end
|
82
|
+
# rubocop:enable Lint/RescueException
|
83
|
+
|
84
|
+
# When "listener#start" is interrupted it's safe to assume that the consumer
|
85
|
+
# and the kafka client were properly stopped, it's safe to call start
|
86
|
+
# again
|
87
|
+
def handle_crashed_listener(listener, error, retry_count)
|
88
|
+
backoff = listener.create_exponential_backoff
|
89
|
+
interval = backoff.interval_at(retry_count).round(2)
|
90
|
+
|
91
|
+
metadata = {
|
92
|
+
listener_id: listener.id,
|
93
|
+
retry_count: retry_count,
|
94
|
+
waiting_time: interval
|
95
|
+
}.merge(error_metadata(error))
|
96
|
+
|
97
|
+
instrument('executor.retry_listener_error', metadata) do
|
98
|
+
log_error("Listener crashed, waiting #{interval}s (#{error.message})", metadata)
|
99
|
+
sleep interval
|
100
|
+
end
|
101
|
+
end
|
105
102
|
end
|
106
103
|
end
|
data/lib/phobos/handler.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phobos
|
2
4
|
module Handler
|
3
5
|
def self.included(base)
|
4
6
|
base.extend(ClassMethods)
|
5
7
|
end
|
6
8
|
|
7
|
-
def before_consume(payload,
|
9
|
+
def before_consume(payload, _metadata)
|
8
10
|
payload
|
9
11
|
end
|
10
12
|
|
11
|
-
def consume(
|
13
|
+
def consume(_payload, _metadata)
|
12
14
|
raise NotImplementedError
|
13
15
|
end
|
14
16
|
|
15
|
-
def around_consume(
|
17
|
+
def around_consume(_payload, _metadata)
|
16
18
|
yield
|
17
19
|
end
|
18
20
|
|
19
21
|
module ClassMethods
|
20
|
-
def start(kafka_client)
|
21
|
-
end
|
22
|
+
def start(kafka_client); end
|
22
23
|
|
23
|
-
def stop
|
24
|
-
end
|
24
|
+
def stop; end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/notifications'
|
2
4
|
|
3
5
|
module Phobos
|
@@ -15,8 +17,8 @@ module Phobos
|
|
15
17
|
end
|
16
18
|
|
17
19
|
def instrument(event, extra = {})
|
18
|
-
ActiveSupport::Notifications.instrument("#{NAMESPACE}.#{event}", extra) do |
|
19
|
-
yield(
|
20
|
+
ActiveSupport::Notifications.instrument("#{NAMESPACE}.#{event}", extra) do |args|
|
21
|
+
yield(args) if block_given?
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
data/lib/phobos/listener.rb
CHANGED
@@ -1,30 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phobos
|
4
|
+
# rubocop:disable Metrics/ParameterLists, Metrics/ClassLength
|
2
5
|
class Listener
|
3
6
|
include Phobos::Instrumentation
|
7
|
+
include Phobos::Log
|
4
8
|
|
5
|
-
|
6
|
-
session_timeout
|
7
|
-
offset_commit_interval
|
8
|
-
offset_commit_threshold
|
9
|
-
heartbeat_interval
|
10
|
-
offset_retention_time
|
11
|
-
).freeze
|
12
|
-
|
13
|
-
DEFAULT_MAX_BYTES_PER_PARTITION = 1048576 # 1 MB
|
9
|
+
DEFAULT_MAX_BYTES_PER_PARTITION = 1_048_576 # 1 MB
|
14
10
|
DELIVERY_OPTS = %w[batch message].freeze
|
15
11
|
|
16
12
|
attr_reader :group_id, :topic, :id
|
17
13
|
attr_reader :handler_class, :encoding
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
start_from_beginning: true, backoff: nil,
|
22
|
-
delivery: 'batch',
|
23
|
-
max_bytes_per_partition: DEFAULT_MAX_BYTES_PER_PARTITION,
|
15
|
+
# rubocop:disable Metrics/MethodLength
|
16
|
+
def initialize(handler:, group_id:, topic:, min_bytes: nil, max_wait_time: nil,
|
17
|
+
force_encoding: nil, start_from_beginning: true, backoff: nil,
|
18
|
+
delivery: 'batch', max_bytes_per_partition: DEFAULT_MAX_BYTES_PER_PARTITION,
|
24
19
|
session_timeout: nil, offset_commit_interval: nil,
|
25
20
|
heartbeat_interval: nil, offset_commit_threshold: nil,
|
26
|
-
offset_retention_time: nil
|
27
|
-
)
|
21
|
+
offset_retention_time: nil)
|
28
22
|
@id = SecureRandom.hex[0...6]
|
29
23
|
@handler_class = handler
|
30
24
|
@group_id = group_id
|
@@ -32,14 +26,11 @@ module Phobos
|
|
32
26
|
@backoff = backoff
|
33
27
|
@delivery = delivery.to_s
|
34
28
|
@subscribe_opts = {
|
35
|
-
start_from_beginning: start_from_beginning,
|
36
|
-
max_bytes_per_partition: max_bytes_per_partition
|
29
|
+
start_from_beginning: start_from_beginning, max_bytes_per_partition: max_bytes_per_partition
|
37
30
|
}
|
38
31
|
@kafka_consumer_opts = compact(
|
39
|
-
session_timeout: session_timeout,
|
40
|
-
offset_commit_interval: offset_commit_interval,
|
41
|
-
heartbeat_interval: heartbeat_interval,
|
42
|
-
offset_retention_time: offset_retention_time,
|
32
|
+
session_timeout: session_timeout, offset_retention_time: offset_retention_time,
|
33
|
+
offset_commit_interval: offset_commit_interval, heartbeat_interval: heartbeat_interval,
|
43
34
|
offset_commit_threshold: offset_commit_threshold
|
44
35
|
)
|
45
36
|
@encoding = Encoding.const_get(force_encoding.to_sym) if force_encoding
|
@@ -47,9 +38,59 @@ module Phobos
|
|
47
38
|
@kafka_client = Phobos.create_kafka_client
|
48
39
|
@producer_enabled = @handler_class.ancestors.include?(Phobos::Producer)
|
49
40
|
end
|
41
|
+
# rubocop:enable Metrics/MethodLength
|
50
42
|
|
51
43
|
def start
|
52
44
|
@signal_to_stop = false
|
45
|
+
|
46
|
+
start_listener
|
47
|
+
|
48
|
+
begin
|
49
|
+
start_consumer_loop
|
50
|
+
rescue Kafka::ProcessingError, Phobos::AbortError
|
51
|
+
# Abort is an exception to prevent the consumer from committing the offset.
|
52
|
+
# Since "listener" had a message being retried while "stop" was called
|
53
|
+
# it's wise to not commit the batch offset to avoid data loss. This will
|
54
|
+
# cause some messages to be reprocessed
|
55
|
+
instrument('listener.retry_aborted', listener_metadata) do
|
56
|
+
log_info('Retry loop aborted, listener is shutting down', listener_metadata)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
ensure
|
60
|
+
stop_listener
|
61
|
+
end
|
62
|
+
|
63
|
+
def stop
|
64
|
+
return if should_stop?
|
65
|
+
|
66
|
+
instrument('listener.stopping', listener_metadata) do
|
67
|
+
log_info('Listener stopping', listener_metadata)
|
68
|
+
@consumer&.stop
|
69
|
+
@signal_to_stop = true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_exponential_backoff
|
74
|
+
Phobos.create_exponential_backoff(@backoff)
|
75
|
+
end
|
76
|
+
|
77
|
+
def should_stop?
|
78
|
+
@signal_to_stop == true
|
79
|
+
end
|
80
|
+
|
81
|
+
def send_heartbeat_if_necessary
|
82
|
+
raise Phobos::AbortError if should_stop?
|
83
|
+
|
84
|
+
@consumer&.send_heartbeat_if_necessary
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def listener_metadata
|
90
|
+
{ listener_id: id, group_id: group_id, topic: topic, handler: handler_class.to_s }
|
91
|
+
end
|
92
|
+
|
93
|
+
def start_listener
|
53
94
|
instrument('listener.start', listener_metadata) do
|
54
95
|
@consumer = create_kafka_consumer
|
55
96
|
@consumer.subscribe(topic, @subscribe_opts)
|
@@ -58,25 +99,14 @@ module Phobos
|
|
58
99
|
# since "start" blocks a thread might be used to call it
|
59
100
|
@handler_class.producer.configure_kafka_client(@kafka_client) if @producer_enabled
|
60
101
|
|
61
|
-
instrument('listener.start_handler', listener_metadata)
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
begin
|
66
|
-
@delivery == 'batch' ? consume_each_batch : consume_each_message
|
67
|
-
|
68
|
-
# Abort is an exception to prevent the consumer from committing the offset.
|
69
|
-
# Since "listener" had a message being retried while "stop" was called
|
70
|
-
# it's wise to not commit the batch offset to avoid data loss. This will
|
71
|
-
# cause some messages to be reprocessed
|
72
|
-
#
|
73
|
-
rescue Kafka::ProcessingError, Phobos::AbortError
|
74
|
-
instrument('listener.retry_aborted', listener_metadata) do
|
75
|
-
Phobos.logger.info({ message: 'Retry loop aborted, listener is shutting down' }.merge(listener_metadata))
|
102
|
+
instrument('listener.start_handler', listener_metadata) do
|
103
|
+
@handler_class.start(@kafka_client)
|
76
104
|
end
|
105
|
+
log_info('Listener started', listener_metadata)
|
77
106
|
end
|
107
|
+
end
|
78
108
|
|
79
|
-
|
109
|
+
def stop_listener
|
80
110
|
instrument('listener.stop', listener_metadata) do
|
81
111
|
instrument('listener.stop_handler', listener_metadata) { @handler_class.stop }
|
82
112
|
|
@@ -88,12 +118,14 @@ module Phobos
|
|
88
118
|
end
|
89
119
|
|
90
120
|
@kafka_client.close
|
91
|
-
if should_stop?
|
92
|
-
Phobos.logger.info { Hash(message: 'Listener stopped').merge(listener_metadata) }
|
93
|
-
end
|
121
|
+
log_info('Listener stopped', listener_metadata) if should_stop?
|
94
122
|
end
|
95
123
|
end
|
96
124
|
|
125
|
+
def start_consumer_loop
|
126
|
+
@delivery == 'batch' ? consume_each_batch : consume_each_message
|
127
|
+
end
|
128
|
+
|
97
129
|
def consume_each_batch
|
98
130
|
@consumer.each_batch(@message_processing_opts) do |batch|
|
99
131
|
batch_processor = Phobos::Actions::ProcessBatch.new(
|
@@ -103,8 +135,8 @@ module Phobos
|
|
103
135
|
)
|
104
136
|
|
105
137
|
batch_processor.execute
|
106
|
-
|
107
|
-
return if should_stop?
|
138
|
+
log_debug('Committed offset', batch_processor.metadata)
|
139
|
+
return nil if should_stop?
|
108
140
|
end
|
109
141
|
end
|
110
142
|
|
@@ -117,41 +149,15 @@ module Phobos
|
|
117
149
|
)
|
118
150
|
|
119
151
|
message_processor.execute
|
120
|
-
|
121
|
-
return if should_stop?
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def stop
|
126
|
-
return if should_stop?
|
127
|
-
instrument('listener.stopping', listener_metadata) do
|
128
|
-
Phobos.logger.info { Hash(message: 'Listener stopping').merge(listener_metadata) }
|
129
|
-
@consumer&.stop
|
130
|
-
@signal_to_stop = true
|
152
|
+
log_debug('Committed offset', message_processor.metadata)
|
153
|
+
return nil if should_stop?
|
131
154
|
end
|
132
155
|
end
|
133
156
|
|
134
|
-
def create_exponential_backoff
|
135
|
-
Phobos.create_exponential_backoff(@backoff)
|
136
|
-
end
|
137
|
-
|
138
|
-
def should_stop?
|
139
|
-
@signal_to_stop == true
|
140
|
-
end
|
141
|
-
|
142
|
-
def send_heartbeat_if_necessary
|
143
|
-
raise Phobos::AbortError if should_stop?
|
144
|
-
@consumer&.send_heartbeat_if_necessary
|
145
|
-
end
|
146
|
-
|
147
|
-
private
|
148
|
-
|
149
|
-
def listener_metadata
|
150
|
-
{ listener_id: id, group_id: group_id, topic: topic, handler: handler_class.to_s }
|
151
|
-
end
|
152
|
-
|
153
157
|
def create_kafka_consumer
|
154
|
-
configs = Phobos.config.consumer_hash.select
|
158
|
+
configs = Phobos.config.consumer_hash.select do |k|
|
159
|
+
Constants::KAFKA_CONSUMER_OPTS.include?(k)
|
160
|
+
end
|
155
161
|
configs.merge!(@kafka_consumer_opts)
|
156
162
|
@kafka_client.consumer({ group_id: group_id }.merge(configs))
|
157
163
|
end
|
@@ -160,4 +166,5 @@ module Phobos
|
|
160
166
|
hash.delete_if { |_, v| v.nil? }
|
161
167
|
end
|
162
168
|
end
|
169
|
+
# rubocop:enable Metrics/ParameterLists, Metrics/ClassLength
|
163
170
|
end
|