phobos 1.7.2 → 1.8.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 +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +3 -2
- data/CHANGELOG.md +8 -0
- data/Dockerfile +2 -2
- data/README.md +55 -12
- data/docker-compose.yml +4 -6
- data/lib/phobos.rb +34 -19
- data/lib/phobos/actions/process_message.rb +31 -6
- data/lib/phobos/handler.rb +5 -5
- data/lib/phobos/listener.rb +7 -2
- data/lib/phobos/version.rb +1 -1
- data/phobos.gemspec +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0b37b13898c547e212449d8b598984b80ceb9796d377991b7dd73f6e6acfdeb8
|
4
|
+
data.tar.gz: 66d12f8461a5e3e796b47364644a948c2f1c030d227f61a6dd675baf7e368f62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75e2ddf1d53ea28086f0442779c2b19f7b8e9d78d62c7b7f4257fdf59d37f70d145fc014da37678d3023a4395c7b84b1c6d81145187482344af493114673931d
|
7
|
+
data.tar.gz: da67af5cee56a7867f99f0353b847fd8d1faf23dd7f24e539fc6a90514b9e5c3c38bef04f29d03cbd4344242818127d1f69aafb4bd36d1ac11321179b75d7cb4
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.5.1
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
7
7
|
## UNRELEASED
|
8
8
|
|
9
|
+
## [1.8.0] - 2018-07-22
|
10
|
+
### Added
|
11
|
+
- Possibility to configure a custom logger #81
|
12
|
+
### Changed
|
13
|
+
- Reduce the volume of info-level log messages #78
|
14
|
+
- Phobos Handler `around_consume` is now an instance method #82
|
15
|
+
- Send consumer heartbeats between retry attempts #83
|
16
|
+
|
9
17
|
## [1.7.2] - 2018-05-03
|
10
18
|
### Added
|
11
19
|
- Add ability to override session_timeout, heartbeat_interval, offset_retention_time, offset_commit_interval, and offset_commit_threshold per listener
|
data/Dockerfile
CHANGED
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[](https://travis-ci.org/klarna/phobos)
|
4
4
|
[](https://codeclimate.com/github/klarna/phobos/maintainability)
|
5
5
|
[](https://codeclimate.com/github/klarna/phobos/test_coverage)
|
6
|
+
[](https://depfu.com/github/klarna/phobos?project=Bundler)
|
6
7
|
[](https://discord.gg/rfMUBVD)
|
7
8
|
|
8
9
|
# Phobos
|
@@ -155,7 +156,25 @@ class MyHandler
|
|
155
156
|
end
|
156
157
|
```
|
157
158
|
|
158
|
-
It is also possible to control the execution of `#consume` with the
|
159
|
+
It is also possible to control the execution of `#consume` with the method `#around_consume(payload, metadata)`. This method receives the payload and metadata, and then invokes `#consume` method by means of a block; example:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
class MyHandler
|
163
|
+
include Phobos::Handler
|
164
|
+
|
165
|
+
def around_consume(payload, metadata)
|
166
|
+
Phobos.logger.info "consuming..."
|
167
|
+
output = yield
|
168
|
+
Phobos.logger.info "done, output: #{output}"
|
169
|
+
end
|
170
|
+
|
171
|
+
def consume(payload, metadata)
|
172
|
+
# consume or skip message
|
173
|
+
end
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
Note: `around_consume` was previously defined as a class method. The current code supports both implementations, giving precendence to the class method, but future versions will no longer support `.around_consume`.
|
159
178
|
|
160
179
|
```ruby
|
161
180
|
class MyHandler
|
@@ -173,13 +192,13 @@ class MyHandler
|
|
173
192
|
end
|
174
193
|
```
|
175
194
|
|
176
|
-
Finally, it is also possible to preprocess the message payload before consuming it using the `before_consume` hook which is invoked before
|
195
|
+
Finally, it is also possible to preprocess the message payload before consuming it using the `before_consume` hook which is invoked before `#around_consume` and `#consume`. The result of this operation will be assigned to payload, so it is important to return the modified payload. This can be very useful, for example if you want a single point of decoding Avro messages and want the payload as a hash instead of a binary.
|
177
196
|
|
178
197
|
```ruby
|
179
198
|
class MyHandler
|
180
199
|
include Phobos::Handler
|
181
200
|
|
182
|
-
def before_consume(payload)
|
201
|
+
def before_consume(payload, metadata)
|
183
202
|
# optionally preprocess payload
|
184
203
|
payload
|
185
204
|
end
|
@@ -194,7 +213,7 @@ The hander life cycle can be illustrated as:
|
|
194
213
|
|
195
214
|
or optionally,
|
196
215
|
|
197
|
-
`.start` -> `#before_consume` ->
|
216
|
+
`.start` -> `#before_consume` -> `#around_consume` [ `#consume` ] -> `.stop`
|
198
217
|
|
199
218
|
### <a name="usage-producing-messages-to-kafka"></a> Producing messages to Kafka
|
200
219
|
|
@@ -319,17 +338,24 @@ The configuration file is organized in 6 sections. Take a look at the example fi
|
|
319
338
|
|
320
339
|
The file will be parsed through ERB so ERB syntax/file extension is supported beside the YML format.
|
321
340
|
|
322
|
-
__logger__ configures the logger for all Phobos components
|
341
|
+
__logger__ configures the logger for all Phobos components. It automatically
|
342
|
+
outputs to `STDOUT` and it saves the log in the configured file.
|
323
343
|
|
324
|
-
__kafka__ provides configurations for every `Kafka::Client` created over the application.
|
344
|
+
__kafka__ provides configurations for every `Kafka::Client` created over the application.
|
345
|
+
All [options supported by `ruby-kafka`][ruby-kafka-client] can be provided.
|
325
346
|
|
326
|
-
__producer__ provides configurations for all producers created over the application,
|
347
|
+
__producer__ provides configurations for all producers created over the application,
|
348
|
+
the options are the same for regular and async producers.
|
349
|
+
All [options supported by `ruby-kafka`][ruby-kafka-producer] can be provided.
|
327
350
|
|
328
|
-
__consumer__ provides configurations for all consumer groups created over the application.
|
351
|
+
__consumer__ provides configurations for all consumer groups created over the application.
|
352
|
+
All [options supported by `ruby-kafka`][ruby-kafka-consumer] can be provided.
|
329
353
|
|
330
|
-
__backoff__ Phobos provides automatic retries for your handlers
|
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
|
+
Backoff can also be configured per listener.
|
331
357
|
|
332
|
-
__listeners__ is the list of listeners configured
|
358
|
+
__listeners__ is the list of listeners configured. Each listener represents a consumer group.
|
333
359
|
|
334
360
|
[ruby-kafka-client]: http://www.rubydoc.info/gems/ruby-kafka/Kafka%2FClient%3Ainitialize
|
335
361
|
[ruby-kafka-consumer]: http://www.rubydoc.info/gems/ruby-kafka/Kafka%2FClient%3Aconsumer
|
@@ -349,6 +375,23 @@ $ phobos start -c /var/configs/my.yml -l /var/configs/additional_listeners.yml
|
|
349
375
|
Note that the config file _must_ still specify a listeners section, though it
|
350
376
|
can be empty.
|
351
377
|
|
378
|
+
#### Custom configuration/logging
|
379
|
+
|
380
|
+
Phobos can be configured using a hash rather than the config file directly. This
|
381
|
+
can be useful if you want to do some pre-processing before sending the file
|
382
|
+
to Phobos. One particularly useful aspect is the ability to provide Phobos
|
383
|
+
with a custom logger, e.g. by reusing the Rails logger:
|
384
|
+
|
385
|
+
```ruby
|
386
|
+
Phobos.configure(
|
387
|
+
custom_logger: Rails.logger,
|
388
|
+
custom_kafka_logger: Rails.logger
|
389
|
+
)
|
390
|
+
```
|
391
|
+
|
392
|
+
If these keys are given, they will override the `logger` keys in the Phobos
|
393
|
+
config file.
|
394
|
+
|
352
395
|
### <a name="usage-instrumentation"></a> Instrumentation
|
353
396
|
|
354
397
|
Some operations are instrumented using [Active Support Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html).
|
@@ -481,8 +524,8 @@ describe MyConsumer do
|
|
481
524
|
let(:metadata) { Hash(foo: 'bar') }
|
482
525
|
|
483
526
|
it 'consumes my message' do
|
484
|
-
|
485
|
-
expect_any_instance_of(described_class).to receive(:before_consume).with(payload).once.and_call_original
|
527
|
+
expect_any_instance_of(described_class).to receive(:around_consume).with(payload, metadata).once.and_call_original
|
528
|
+
expect_any_instance_of(described_class).to receive(:before_consume).with(payload, metadata).once.and_call_original
|
486
529
|
expect_any_instance_of(described_class).to receive(:consume).with(payload, metadata).once.and_call_original
|
487
530
|
|
488
531
|
process_message(handler: described_class, payload: payload, metadata: metadata)
|
data/docker-compose.yml
CHANGED
@@ -13,18 +13,16 @@ services:
|
|
13
13
|
- ./coverage:/opt/phobos/coverage
|
14
14
|
|
15
15
|
zookeeper:
|
16
|
-
image:
|
16
|
+
image: wurstmeister/zookeeper:latest
|
17
17
|
ports:
|
18
18
|
- 2181:2181
|
19
19
|
|
20
20
|
kafka:
|
21
21
|
depends_on:
|
22
22
|
- zookeeper
|
23
|
-
image:
|
23
|
+
image: wurstmeister/kafka:0.11.0.1
|
24
24
|
ports:
|
25
25
|
- 9092:9092
|
26
26
|
environment:
|
27
|
-
|
28
|
-
|
29
|
-
- KAFKA_ADVERTISED_PORT=9092
|
30
|
-
- ZOOKEEPER_CONNECTION_STRING=zookeeper:2181
|
27
|
+
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
28
|
+
KAFKA_BROKER_ID: 0
|
data/lib/phobos.rb
CHANGED
@@ -55,38 +55,53 @@ module Phobos
|
|
55
55
|
ExponentialBackoff.new(min, max).tap { |backoff| backoff.randomize_factor = rand }
|
56
56
|
end
|
57
57
|
|
58
|
+
# :nodoc:
|
58
59
|
def configure_logger
|
59
|
-
log_file = config.logger.file
|
60
60
|
ruby_kafka = config.logger.ruby_kafka
|
61
|
-
date_pattern = '%Y-%m-%dT%H:%M:%S:%L%zZ'
|
62
|
-
json_layout = Logging.layouts.json(date_pattern: date_pattern)
|
63
|
-
|
64
|
-
stdout_layout = if config.logger.stdout_json == true
|
65
|
-
json_layout
|
66
|
-
else
|
67
|
-
Logging.layouts.pattern(date_pattern: date_pattern)
|
68
|
-
end
|
69
|
-
|
70
|
-
appenders = [Logging.appenders.stdout(layout: stdout_layout)]
|
71
61
|
|
72
62
|
Logging.backtrace(true)
|
73
63
|
Logging.logger.root.level = silence_log ? :fatal : config.logger.level
|
74
|
-
|
75
|
-
if log_file
|
76
|
-
FileUtils.mkdir_p(File.dirname(log_file))
|
77
|
-
appenders << Logging.appenders.file(log_file, layout: json_layout)
|
78
|
-
end
|
64
|
+
appenders = logger_appenders
|
79
65
|
|
80
66
|
@ruby_kafka_logger = nil
|
81
67
|
|
82
|
-
if
|
68
|
+
if config.custom_kafka_logger
|
69
|
+
@ruby_kafka_logger = config.custom_kafka_logger
|
70
|
+
elsif ruby_kafka
|
83
71
|
@ruby_kafka_logger = Logging.logger['RubyKafka']
|
84
72
|
@ruby_kafka_logger.appenders = appenders
|
85
73
|
@ruby_kafka_logger.level = silence_log ? :fatal : ruby_kafka.level
|
86
74
|
end
|
87
75
|
|
88
|
-
|
89
|
-
|
76
|
+
if config.custom_logger
|
77
|
+
@logger = config.custom_logger
|
78
|
+
else
|
79
|
+
@logger = Logging.logger[self]
|
80
|
+
@logger.appenders = appenders
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def logger_appenders
|
85
|
+
date_pattern = '%Y-%m-%dT%H:%M:%S:%L%zZ'
|
86
|
+
json_layout = Logging.layouts.json(date_pattern: date_pattern)
|
87
|
+
log_file = config.logger.file
|
88
|
+
stdout_layout = if config.logger.stdout_json == true
|
89
|
+
json_layout
|
90
|
+
else
|
91
|
+
Logging.layouts.pattern(date_pattern: date_pattern)
|
92
|
+
end
|
93
|
+
|
94
|
+
appenders = [Logging.appenders.stdout(layout: stdout_layout)]
|
95
|
+
|
96
|
+
if log_file
|
97
|
+
FileUtils.mkdir_p(File.dirname(log_file))
|
98
|
+
appenders << Logging.appenders.file(log_file, layout: json_layout)
|
99
|
+
end
|
100
|
+
appenders
|
101
|
+
end
|
102
|
+
|
103
|
+
def deprecate(message)
|
104
|
+
warn "DEPRECATION WARNING: #{message} #{Kernel.caller.first}"
|
90
105
|
end
|
91
106
|
|
92
107
|
private
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Phobos
|
2
2
|
module Actions
|
3
3
|
class ProcessMessage
|
4
|
+
MAX_SLEEP_INTERVAL = 3
|
5
|
+
|
4
6
|
include Phobos::Instrumentation
|
5
7
|
|
6
8
|
attr_reader :metadata
|
@@ -38,16 +40,26 @@ module Phobos
|
|
38
40
|
{ message: "error processing message, waiting #{interval}s" }.merge(error).merge(@metadata)
|
39
41
|
end
|
40
42
|
|
41
|
-
|
43
|
+
snooze(interval)
|
42
44
|
end
|
43
45
|
|
44
|
-
raise Phobos::AbortError if @listener.should_stop?
|
45
|
-
|
46
46
|
@metadata.merge!(retry_count: retry_count + 1)
|
47
47
|
retry
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
def snooze(interval)
|
52
|
+
remaining_interval = interval
|
53
|
+
|
54
|
+
@listener.send_heartbeat_if_necessary
|
55
|
+
|
56
|
+
while remaining_interval.positive?
|
57
|
+
sleep [remaining_interval, MAX_SLEEP_INTERVAL].min
|
58
|
+
remaining_interval -= MAX_SLEEP_INTERVAL
|
59
|
+
@listener.send_heartbeat_if_necessary
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
51
63
|
private
|
52
64
|
|
53
65
|
def force_encoding(value)
|
@@ -57,10 +69,23 @@ module Phobos
|
|
57
69
|
def process_message(payload)
|
58
70
|
instrument('listener.process_message', @metadata) do
|
59
71
|
handler = @listener.handler_class.new
|
60
|
-
preprocessed_payload =
|
72
|
+
preprocessed_payload = begin
|
73
|
+
handler.before_consume(payload, @metadata)
|
74
|
+
rescue ArgumentError => e
|
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) }
|
61
80
|
|
62
|
-
@listener.handler_class.around_consume
|
63
|
-
|
81
|
+
if @listener.handler_class.respond_to?(:around_consume)
|
82
|
+
# around_consume class method implementation
|
83
|
+
Phobos.deprecate("around_consume has been moved to instance method, please update your consumer."\
|
84
|
+
" This will not be backwards compatible in the future.")
|
85
|
+
@listener.handler_class.around_consume(preprocessed_payload, @metadata, &consume_block)
|
86
|
+
else
|
87
|
+
# around_consume instance method implementation
|
88
|
+
handler.around_consume(preprocessed_payload, @metadata, &consume_block)
|
64
89
|
end
|
65
90
|
end
|
66
91
|
end
|
data/lib/phobos/handler.rb
CHANGED
@@ -4,7 +4,7 @@ module Phobos
|
|
4
4
|
base.extend(ClassMethods)
|
5
5
|
end
|
6
6
|
|
7
|
-
def before_consume(payload)
|
7
|
+
def before_consume(payload, metadata)
|
8
8
|
payload
|
9
9
|
end
|
10
10
|
|
@@ -12,16 +12,16 @@ module Phobos
|
|
12
12
|
raise NotImplementedError
|
13
13
|
end
|
14
14
|
|
15
|
+
def around_consume(payload, metadata)
|
16
|
+
yield
|
17
|
+
end
|
18
|
+
|
15
19
|
module ClassMethods
|
16
20
|
def start(kafka_client)
|
17
21
|
end
|
18
22
|
|
19
23
|
def stop
|
20
24
|
end
|
21
|
-
|
22
|
-
def around_consume(payload, metadata)
|
23
|
-
yield
|
24
|
-
end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/phobos/listener.rb
CHANGED
@@ -103,7 +103,7 @@ module Phobos
|
|
103
103
|
)
|
104
104
|
|
105
105
|
batch_processor.execute
|
106
|
-
Phobos.logger.
|
106
|
+
Phobos.logger.debug { Hash(message: 'Committed offset').merge(batch_processor.metadata) }
|
107
107
|
return if should_stop?
|
108
108
|
end
|
109
109
|
end
|
@@ -117,7 +117,7 @@ module Phobos
|
|
117
117
|
)
|
118
118
|
|
119
119
|
message_processor.execute
|
120
|
-
Phobos.logger.
|
120
|
+
Phobos.logger.debug { Hash(message: 'Committed offset').merge(message_processor.metadata) }
|
121
121
|
return if should_stop?
|
122
122
|
end
|
123
123
|
end
|
@@ -139,6 +139,11 @@ module Phobos
|
|
139
139
|
@signal_to_stop == true
|
140
140
|
end
|
141
141
|
|
142
|
+
def send_heartbeat_if_necessary
|
143
|
+
raise Phobos::AbortError if should_stop?
|
144
|
+
@consumer&.send_heartbeat_if_necessary
|
145
|
+
end
|
146
|
+
|
142
147
|
private
|
143
148
|
|
144
149
|
def listener_metadata
|
data/lib/phobos/version.rb
CHANGED
data/phobos.gemspec
CHANGED
@@ -45,7 +45,7 @@ Gem::Specification.new do |spec|
|
|
45
45
|
spec.required_ruby_version = '>= 2.3'
|
46
46
|
|
47
47
|
spec.add_development_dependency 'bundler'
|
48
|
-
spec.add_development_dependency 'rake', '~>
|
48
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
49
49
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
50
50
|
spec.add_development_dependency 'pry-byebug'
|
51
51
|
spec.add_development_dependency 'simplecov'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phobos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Túlio Ornelas
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2018-
|
17
|
+
date: 2018-07-22 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: bundler
|
@@ -36,14 +36,14 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '12.3'
|
40
40
|
type: :development
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
46
|
+
version: '12.3'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -275,7 +275,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
275
275
|
version: '0'
|
276
276
|
requirements: []
|
277
277
|
rubyforge_project:
|
278
|
-
rubygems_version: 2.6
|
278
|
+
rubygems_version: 2.7.6
|
279
279
|
signing_key:
|
280
280
|
specification_version: 4
|
281
281
|
summary: Simplifying Kafka for ruby apps
|