karafka 2.0.22 → 2.0.24
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
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +2 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +39 -1
- data/Gemfile.lock +9 -9
- data/README.md +3 -3
- data/config/{errors.yml → locales/errors.yml} +1 -1
- data/config/locales/pro_errors.yml +18 -0
- data/docker-compose.yml +3 -0
- data/karafka.gemspec +2 -3
- data/lib/karafka/active_job/job_options_contract.rb +1 -1
- data/lib/karafka/admin.rb +84 -6
- data/lib/karafka/app.rb +15 -4
- data/lib/karafka/base_consumer.rb +43 -1
- data/lib/karafka/connection/client.rb +4 -4
- data/lib/karafka/connection/listener.rb +1 -1
- data/lib/karafka/contracts/config.rb +1 -1
- data/lib/karafka/contracts/consumer_group.rb +1 -1
- data/lib/karafka/contracts/server_cli_options.rb +1 -1
- data/lib/karafka/contracts/topic.rb +1 -1
- data/lib/karafka/instrumentation/logger_listener.rb +32 -0
- data/lib/karafka/instrumentation/notifications.rb +3 -0
- data/lib/karafka/messages/message.rb +14 -2
- data/lib/karafka/messages/parser.rb +14 -0
- data/lib/karafka/pro/active_job/job_options_contract.rb +1 -1
- data/lib/karafka/pro/encryption/cipher.rb +58 -0
- data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
- data/lib/karafka/pro/encryption/errors.rb +24 -0
- data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
- data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
- data/lib/karafka/pro/encryption/setup/config.rb +48 -0
- data/lib/karafka/pro/encryption.rb +47 -0
- data/lib/karafka/pro/loader.rb +23 -2
- data/lib/karafka/pro/processing/strategies/aj_dlq_mom.rb +2 -2
- data/lib/karafka/pro/processing/strategies/aj_lrj_mom_vp.rb +2 -2
- data/lib/karafka/pro/processing/strategies/aj_mom_vp.rb +1 -1
- data/lib/karafka/pro/processing/strategies/default.rb +5 -1
- data/lib/karafka/pro/processing/strategies/dlq.rb +4 -2
- data/lib/karafka/pro/processing/strategies/dlq_lrj.rb +3 -1
- data/lib/karafka/pro/processing/strategies/dlq_lrj_mom.rb +1 -1
- data/lib/karafka/pro/processing/strategies/dlq_mom.rb +2 -2
- data/lib/karafka/pro/processing/strategies/lrj.rb +4 -2
- data/lib/karafka/pro/processing/strategies/lrj_mom.rb +2 -2
- data/lib/karafka/pro/processing/strategies/mom.rb +1 -1
- data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +2 -2
- data/lib/karafka/pro/routing/features/long_running_job/contract.rb +2 -2
- data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +2 -2
- data/lib/karafka/processing/coordinator.rb +15 -0
- data/lib/karafka/processing/executor.rb +1 -1
- data/lib/karafka/processing/strategies/aj_dlq_mom.rb +2 -2
- data/lib/karafka/processing/strategies/default.rb +7 -1
- data/lib/karafka/processing/strategies/dlq.rb +4 -2
- data/lib/karafka/processing/strategies/dlq_mom.rb +2 -2
- data/lib/karafka/processing/strategies/mom.rb +1 -1
- data/lib/karafka/processing/worker.rb +1 -1
- data/lib/karafka/railtie.rb +3 -0
- data/lib/karafka/routing/builder.rb +1 -1
- data/lib/karafka/routing/consumer_group.rb +3 -3
- data/lib/karafka/routing/features/active_job/contract.rb +1 -1
- data/lib/karafka/routing/features/dead_letter_queue/contract.rb +1 -1
- data/lib/karafka/routing/features/manual_offset_management/contract.rb +1 -1
- data/lib/karafka/server.rb +14 -14
- data/lib/karafka/setup/config.rb +15 -2
- data/lib/karafka/status.rb +27 -9
- data/lib/karafka/templates/karafka.rb.erb +1 -2
- data/lib/karafka/time_trackers/pause.rb +3 -1
- data/lib/karafka/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +17 -23
- metadata.gz.sig +2 -4
- data/lib/karafka/instrumentation.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25eb837923f78f1bf35402bf24469228d8f5af12090b2d406718a0077ce42598
|
|
4
|
+
data.tar.gz: 23520ecab430080061046e683c0dcb47316d1216d1ad9843894caefff76e24cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e84c4c4dedd7a160ebcbd6cff53e0dfcf601a31eb19469d1a86eabb3f33507f3b1871c8f4489b743651118ed47e52925f12dba4fc67e462bd8be04d71dfa3bb
|
|
7
|
+
data.tar.gz: 8b9754f5566c5ef213c4803cfba2a4263553a5112e23fd12e42a963fd493f8f20c1409b12303628519ba7ef5bcf07aa09fabc6dd4ffc2a2114a6bad8f94cbc72
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -46,6 +46,7 @@ jobs:
|
|
|
46
46
|
run: \curl -sSL https://api.coditsu.io/run/ci | bash
|
|
47
47
|
|
|
48
48
|
specs:
|
|
49
|
+
timeout-minutes: 30
|
|
49
50
|
runs-on: ubuntu-latest
|
|
50
51
|
needs: diffend
|
|
51
52
|
strategy:
|
|
@@ -84,6 +85,7 @@ jobs:
|
|
|
84
85
|
run: bin/rspecs
|
|
85
86
|
|
|
86
87
|
integrations:
|
|
88
|
+
timeout-minutes: 30
|
|
87
89
|
runs-on: ubuntu-latest
|
|
88
90
|
needs: diffend
|
|
89
91
|
strategy:
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.1.
|
|
1
|
+
3.1.3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Karafka framework changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.24 (2022-12-19)
|
|
4
|
+
- **[Feature]** Provide out of the box encryption support for Pro.
|
|
5
|
+
- [Improvement] Add instrumentation upon `#pause`.
|
|
6
|
+
- [Improvement] Add instrumentation upon retries.
|
|
7
|
+
- [Improvement] Assign `#id` to consumers similar to other entities for ease of debugging.
|
|
8
|
+
- [Improvement] Add retries and pausing to the default `LoggerListener`.
|
|
9
|
+
- [Improvement] Introduce a new final `terminated` state that will kick in prior to exit but after all the instrumentation and other things are done.
|
|
10
|
+
- [Improvement] Ensure that state transitions are thread-safe and ensure state transitions can occur in one direction.
|
|
11
|
+
- [Improvement] Optimize status methods proxying to `Karafka::App`.
|
|
12
|
+
- [Improvement] Allow for easier state usage by introducing explicit `#to_s` for reporting.
|
|
13
|
+
- [Improvement] Change auto-generated id from `SecureRandom#uuid` to `SecureRandom#hex(6)`
|
|
14
|
+
- [Improvement] Emit statistic every 5 seconds by default.
|
|
15
|
+
- [Improvement] Introduce general messages parser that can be swapped when needed.
|
|
16
|
+
- [Fix] Do not trigger code reloading when `consumer_persistence` is enabled.
|
|
17
|
+
- [Fix] Shutdown producer after all the consumer components are down and the status is stopped. This will ensure, that any instrumentation related Kafka messaging can still operate.
|
|
18
|
+
|
|
19
|
+
### Upgrade notes
|
|
20
|
+
|
|
21
|
+
If you want to disable `librdkafka` statistics because you do not use them at all, update the `kafka` `statistics.interval.ms` setting and set it to `0`:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
class KarafkaApp < Karafka::App
|
|
25
|
+
setup do |config|
|
|
26
|
+
# Other settings...
|
|
27
|
+
config.kafka = {
|
|
28
|
+
'statistics.interval.ms': 0
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 2.0.23 (2022-12-07)
|
|
35
|
+
- [Maintenance] Align with `waterdrop` and `karafka-core`
|
|
36
|
+
- [Improvement] Provide `Admin#read_topic` API to get topic data without subscribing.
|
|
37
|
+
- [Improvement] Upon an end user `#pause`, do not commit the offset in automatic offset management mode. This will prevent from a scenario where pause is needed but during it a rebalance occurs and a different assigned process starts not from the pause location but from the automatic offset that may be different. This still allows for using the `#mark_as_consumed`.
|
|
38
|
+
- [Fix] Fix a scenario where manual `#pause` would be overwritten by a resume initiated by the strategy.
|
|
39
|
+
- [Fix] Fix a scenario where manual `#pause` in LRJ would cause infinite pause.
|
|
40
|
+
|
|
3
41
|
## 2.0.22 (2022-12-02)
|
|
4
42
|
- [Improvement] Load Pro components upon Karafka require so they can be altered prior to setup.
|
|
5
43
|
- [Improvement] Do not run LRJ jobs that were added to the jobs queue but were revoked meanwhile.
|
|
@@ -418,7 +456,7 @@ There are several things in the plan already for 2.1 and beyond, including a web
|
|
|
418
456
|
- Small integration specs refactoring + specs for pausing scenarios
|
|
419
457
|
|
|
420
458
|
## 2.0.0-alpha6 (2022-04-17)
|
|
421
|
-
- Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
|
|
459
|
+
- Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
|
|
422
460
|
- Fix an issue with parallel pristine specs colliding with each other during `bundle install` (#820)
|
|
423
461
|
- Replace `consumer.consume` with `consumer.consumed` event to match the behaviour
|
|
424
462
|
- Make sure, that offset committing happens before the `consumer.consumed` event is propagated
|
data/Gemfile.lock
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
karafka (2.0.
|
|
5
|
-
karafka-core (>= 2.0.
|
|
6
|
-
rdkafka (>= 0.12)
|
|
4
|
+
karafka (2.0.24)
|
|
5
|
+
karafka-core (>= 2.0.7, < 3.0.0)
|
|
7
6
|
thor (>= 0.20)
|
|
8
|
-
waterdrop (>= 2.4.
|
|
7
|
+
waterdrop (>= 2.4.7, < 3.0.0)
|
|
9
8
|
zeitwerk (~> 2.3)
|
|
10
9
|
|
|
11
10
|
GEM
|
|
@@ -30,8 +29,9 @@ GEM
|
|
|
30
29
|
activesupport (>= 5.0)
|
|
31
30
|
i18n (1.12.0)
|
|
32
31
|
concurrent-ruby (~> 1.0)
|
|
33
|
-
karafka-core (2.0.
|
|
32
|
+
karafka-core (2.0.7)
|
|
34
33
|
concurrent-ruby (>= 1.1)
|
|
34
|
+
rdkafka (>= 0.12)
|
|
35
35
|
mini_portile2 (2.8.0)
|
|
36
36
|
minitest (5.16.3)
|
|
37
37
|
rake (13.0.6)
|
|
@@ -48,7 +48,7 @@ GEM
|
|
|
48
48
|
rspec-expectations (3.12.0)
|
|
49
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
50
50
|
rspec-support (~> 3.12.0)
|
|
51
|
-
rspec-mocks (3.12.
|
|
51
|
+
rspec-mocks (3.12.1)
|
|
52
52
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
53
53
|
rspec-support (~> 3.12.0)
|
|
54
54
|
rspec-support (3.12.0)
|
|
@@ -61,13 +61,13 @@ GEM
|
|
|
61
61
|
thor (1.2.1)
|
|
62
62
|
tzinfo (2.0.5)
|
|
63
63
|
concurrent-ruby (~> 1.0)
|
|
64
|
-
waterdrop (2.4.
|
|
65
|
-
karafka-core (>= 2.0.
|
|
66
|
-
rdkafka (>= 0.10)
|
|
64
|
+
waterdrop (2.4.7)
|
|
65
|
+
karafka-core (>= 2.0.7, < 3.0.0)
|
|
67
66
|
zeitwerk (~> 2.3)
|
|
68
67
|
zeitwerk (2.6.6)
|
|
69
68
|
|
|
70
69
|
PLATFORMS
|
|
70
|
+
x86_64-darwin-21
|
|
71
71
|
x86_64-linux
|
|
72
72
|
|
|
73
73
|
DEPENDENCIES
|
data/README.md
CHANGED
|
@@ -72,10 +72,10 @@ Karafka.producer.produce_sync(topic: 'example', payload: { 'ping' => 'pong' }.to
|
|
|
72
72
|
```bash
|
|
73
73
|
bundle exec karafka server
|
|
74
74
|
|
|
75
|
-
[
|
|
76
|
-
[
|
|
75
|
+
[86d47f0b92f7] Polled 1 message in 1000ms
|
|
76
|
+
[3732873c8a74] Consume job for ExampleConsumer on example started
|
|
77
77
|
{"ping"=>"pong"}
|
|
78
|
-
[
|
|
78
|
+
[3732873c8a74] Consume job for ExampleConsumer on example finished in 0ms
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
## Want to Upgrade? LGPL is not for you? Want to help?
|
|
@@ -63,7 +63,7 @@ en:
|
|
|
63
63
|
|
|
64
64
|
pro_topic:
|
|
65
65
|
virtual_partitions.partitioner_respond_to_call: needs to be defined and needs to respond to `#call`
|
|
66
|
-
virtual_partitions.max_partitions_format: needs to be
|
|
66
|
+
virtual_partitions.max_partitions_format: needs to be equal or more than 1
|
|
67
67
|
manual_offset_management_not_with_virtual_partitions: cannot be used together with Virtual Partitions
|
|
68
68
|
long_running_job.active_format: needs to be either true or false
|
|
69
69
|
dead_letter_queue_not_with_virtual_partitions: cannot be used together with Virtual Partitions
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
en:
|
|
2
|
+
validations:
|
|
3
|
+
topic:
|
|
4
|
+
virtual_partitions.partitioner_respond_to_call: needs to be defined and needs to respond to `#call`
|
|
5
|
+
virtual_partitions.max_partitions_format: needs to be equal or more than 1
|
|
6
|
+
manual_offset_management_not_with_virtual_partitions: cannot be used together with Virtual Partitions
|
|
7
|
+
long_running_job.active_format: needs to be either true or false
|
|
8
|
+
dead_letter_queue_not_with_virtual_partitions: cannot be used together with Virtual Partitions
|
|
9
|
+
|
|
10
|
+
config:
|
|
11
|
+
encryption.active_format: 'needs to be either true or false'
|
|
12
|
+
encryption.public_key_invalid: 'is not a valid public RSA key'
|
|
13
|
+
encryption.public_key_needs_to_be_public: 'is a private RSA key not a public one'
|
|
14
|
+
encryption.private_keys_format: 'needs to be a hash of version and private key value'
|
|
15
|
+
encryption.private_keys_need_to_be_private: 'all keys need to be private'
|
|
16
|
+
encryption.version_format: must be a non-empty string
|
|
17
|
+
encryption.public_key_format: 'is not a valid public RSA key'
|
|
18
|
+
encryption.private_keys_invalid: 'contains an invalid private RSA key string'
|
data/docker-compose.yml
CHANGED
|
@@ -3,8 +3,10 @@ services:
|
|
|
3
3
|
zookeeper:
|
|
4
4
|
container_name: karafka_20_zookeeper
|
|
5
5
|
image: wurstmeister/zookeeper
|
|
6
|
+
restart: on-failure
|
|
6
7
|
ports:
|
|
7
8
|
- '2181:2181'
|
|
9
|
+
|
|
8
10
|
kafka:
|
|
9
11
|
container_name: karafka_20_kafka
|
|
10
12
|
image: wurstmeister/kafka
|
|
@@ -22,3 +24,4 @@ services:
|
|
|
22
24
|
benchmarks_00_10:10:1"
|
|
23
25
|
volumes:
|
|
24
26
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
27
|
+
restart: on-failure
|
data/karafka.gemspec
CHANGED
|
@@ -21,10 +21,9 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
without having to focus on things that are not your business domain.
|
|
22
22
|
DESC
|
|
23
23
|
|
|
24
|
-
spec.add_dependency 'karafka-core', '>= 2.0.
|
|
25
|
-
spec.add_dependency 'rdkafka', '>= 0.12'
|
|
24
|
+
spec.add_dependency 'karafka-core', '>= 2.0.7', '< 3.0.0'
|
|
26
25
|
spec.add_dependency 'thor', '>= 0.20'
|
|
27
|
-
spec.add_dependency 'waterdrop', '>= 2.4.
|
|
26
|
+
spec.add_dependency 'waterdrop', '>= 2.4.7', '< 3.0.0'
|
|
28
27
|
spec.add_dependency 'zeitwerk', '~> 2.3'
|
|
29
28
|
|
|
30
29
|
spec.required_ruby_version = '>= 2.7.0'
|
|
@@ -10,7 +10,7 @@ module Karafka
|
|
|
10
10
|
configure do |config|
|
|
11
11
|
config.error_messages = YAML.safe_load(
|
|
12
12
|
File.read(
|
|
13
|
-
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
|
13
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
|
14
14
|
)
|
|
15
15
|
).fetch('en').fetch('validations').fetch('job_options')
|
|
16
16
|
end
|
data/lib/karafka/admin.rb
CHANGED
|
@@ -9,7 +9,70 @@ module Karafka
|
|
|
9
9
|
# @note It always uses the primary defined cluster and does not support multi-cluster work.
|
|
10
10
|
# If you need this, just replace the cluster info for the time you use this
|
|
11
11
|
module Admin
|
|
12
|
+
# A fake admin topic representation that we use for messages fetched using this API
|
|
13
|
+
# We cannot use the topics directly because we may want to request data from topics that we
|
|
14
|
+
# do not have in the routing
|
|
15
|
+
Topic = Struct.new(:name, :deserializer)
|
|
16
|
+
|
|
17
|
+
# Defaults for config
|
|
18
|
+
CONFIG_DEFAULTS = {
|
|
19
|
+
'group.id': 'karafka_admin',
|
|
20
|
+
# We want to know when there is no more data not to end up with an endless loop
|
|
21
|
+
'enable.partition.eof': true,
|
|
22
|
+
'statistics.interval.ms': 0
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
private_constant :Topic, :CONFIG_DEFAULTS
|
|
26
|
+
|
|
12
27
|
class << self
|
|
28
|
+
# Allows us to read messages from the topic
|
|
29
|
+
#
|
|
30
|
+
# @param name [String, Symbol] topic name
|
|
31
|
+
# @param partition [Integer] partition
|
|
32
|
+
# @param count [Integer] how many messages we want to get at most
|
|
33
|
+
# @param offset [Integer] offset from which we should start. If -1 is provided (default) we
|
|
34
|
+
# will start from the latest offset
|
|
35
|
+
#
|
|
36
|
+
# @return [Array<Karafka::Messages::Message>] array with messages
|
|
37
|
+
def read_topic(name, partition, count, offset = -1)
|
|
38
|
+
messages = []
|
|
39
|
+
tpl = Rdkafka::Consumer::TopicPartitionList.new
|
|
40
|
+
|
|
41
|
+
with_consumer do |consumer|
|
|
42
|
+
if offset.negative?
|
|
43
|
+
offsets = consumer.query_watermark_offsets(name, partition)
|
|
44
|
+
offset = offsets.last - count
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
offset = offset.negative? ? 0 : offset
|
|
48
|
+
|
|
49
|
+
tpl.add_topic_and_partitions_with_offsets(name, partition => offset)
|
|
50
|
+
consumer.assign(tpl)
|
|
51
|
+
|
|
52
|
+
# We should poll as long as we don't have all the messages that we need or as long as
|
|
53
|
+
# we do not read all the messages from the topic
|
|
54
|
+
loop do
|
|
55
|
+
break if messages.size >= count
|
|
56
|
+
|
|
57
|
+
message = consumer.poll(200)
|
|
58
|
+
messages << message if message
|
|
59
|
+
rescue Rdkafka::RdkafkaError => e
|
|
60
|
+
# End of partition
|
|
61
|
+
break if e.code == :partition_eof
|
|
62
|
+
|
|
63
|
+
raise e
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
messages.map do |message|
|
|
68
|
+
Messages::Builders::Message.call(
|
|
69
|
+
message,
|
|
70
|
+
Topic.new(name, Karafka::App.config.deserializer),
|
|
71
|
+
Time.now
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
13
76
|
# Creates Kafka topic with given settings
|
|
14
77
|
#
|
|
15
78
|
# @param name [String] topic name
|
|
@@ -52,15 +115,30 @@ module Karafka
|
|
|
52
115
|
|
|
53
116
|
# Creates admin instance and yields it. After usage it closes the admin instance
|
|
54
117
|
def with_admin
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
admin = ::Rdkafka::Config.new(config).admin
|
|
59
|
-
result = yield(admin)
|
|
60
|
-
result
|
|
118
|
+
admin = config(:producer).admin
|
|
119
|
+
yield(admin)
|
|
61
120
|
ensure
|
|
62
121
|
admin&.close
|
|
63
122
|
end
|
|
123
|
+
|
|
124
|
+
# Creates consumer instance and yields it. After usage it closes the consumer instance
|
|
125
|
+
def with_consumer
|
|
126
|
+
consumer = config(:consumer).consumer
|
|
127
|
+
yield(consumer)
|
|
128
|
+
ensure
|
|
129
|
+
consumer&.close
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @param type [Symbol] type of config we want
|
|
133
|
+
# @return [::Rdkafka::Config] rdkafka config
|
|
134
|
+
def config(type)
|
|
135
|
+
config_hash = Karafka::Setup::AttributesMap.public_send(
|
|
136
|
+
type,
|
|
137
|
+
Karafka::App.config.kafka.dup.merge(CONFIG_DEFAULTS)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
::Rdkafka::Config.new(config_hash)
|
|
141
|
+
end
|
|
64
142
|
end
|
|
65
143
|
end
|
|
66
144
|
end
|
data/lib/karafka/app.rb
CHANGED
|
@@ -35,10 +35,21 @@ module Karafka
|
|
|
35
35
|
# Just a nicer name for the consumer groups
|
|
36
36
|
alias routes consumer_groups
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
# Allow for easier status management via `Karafka::App` by aliasing status methods here
|
|
39
|
+
Status::STATES.each do |state, transition|
|
|
40
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
41
|
+
def #{state}
|
|
42
|
+
App.config.internal.status.#{state}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def #{state}?
|
|
46
|
+
App.config.internal.status.#{state}?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def #{transition}
|
|
50
|
+
App.config.internal.status.#{transition}
|
|
51
|
+
end
|
|
52
|
+
RUBY
|
|
42
53
|
end
|
|
43
54
|
|
|
44
55
|
# Methods that should be delegated to Karafka module
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
module Karafka
|
|
5
5
|
# Base consumer from which all Karafka consumers should inherit
|
|
6
6
|
class BaseConsumer
|
|
7
|
+
# @return [String] id of the current consumer
|
|
8
|
+
attr_reader :id
|
|
7
9
|
# @return [Karafka::Routing::Topic] topic to which a given consumer is subscribed
|
|
8
10
|
attr_accessor :topic
|
|
9
11
|
# @return [Karafka::Messages::Messages] current messages batch
|
|
@@ -15,6 +17,11 @@ module Karafka
|
|
|
15
17
|
# @return [Waterdrop::Producer] producer instance
|
|
16
18
|
attr_accessor :producer
|
|
17
19
|
|
|
20
|
+
# Creates new consumer and assigns it an id
|
|
21
|
+
def initialize
|
|
22
|
+
@id = SecureRandom.hex(6)
|
|
23
|
+
end
|
|
24
|
+
|
|
18
25
|
# Can be used to run preparation code prior to the job being enqueued
|
|
19
26
|
#
|
|
20
27
|
# @private
|
|
@@ -190,7 +197,10 @@ module Karafka
|
|
|
190
197
|
# @param offset [Integer] offset from which we want to restart the processing
|
|
191
198
|
# @param timeout [Integer, nil] how long in milliseconds do we want to pause or nil to use the
|
|
192
199
|
# default exponential pausing strategy defined for retries
|
|
193
|
-
|
|
200
|
+
# @param manual_pause [Boolean] Flag to differentiate between user pause and system/strategy
|
|
201
|
+
# based pause. While they both pause in exactly the same way, the strategy application
|
|
202
|
+
# may need to differentiate between them.
|
|
203
|
+
def pause(offset, timeout = nil, manual_pause = true)
|
|
194
204
|
timeout ? coordinator.pause_tracker.pause(timeout) : coordinator.pause_tracker.pause
|
|
195
205
|
|
|
196
206
|
client.pause(
|
|
@@ -198,6 +208,20 @@ module Karafka
|
|
|
198
208
|
messages.metadata.partition,
|
|
199
209
|
offset
|
|
200
210
|
)
|
|
211
|
+
|
|
212
|
+
# Indicate, that user took a manual action of pausing
|
|
213
|
+
coordinator.manual_pause if manual_pause
|
|
214
|
+
|
|
215
|
+
Karafka.monitor.instrument(
|
|
216
|
+
'consumer.consuming.pause',
|
|
217
|
+
caller: self,
|
|
218
|
+
manual: manual_pause,
|
|
219
|
+
topic: messages.metadata.topic,
|
|
220
|
+
partition: messages.metadata.partition,
|
|
221
|
+
offset: offset,
|
|
222
|
+
timeout: coordinator.pause_tracker.current_timeout,
|
|
223
|
+
attempt: coordinator.pause_tracker.attempt
|
|
224
|
+
)
|
|
201
225
|
end
|
|
202
226
|
|
|
203
227
|
# Resumes processing of the current topic partition
|
|
@@ -226,5 +250,23 @@ module Karafka
|
|
|
226
250
|
def revoked?
|
|
227
251
|
coordinator.revoked?
|
|
228
252
|
end
|
|
253
|
+
|
|
254
|
+
# Pauses the processing from the last offset to retry on given message
|
|
255
|
+
# @private
|
|
256
|
+
def retry_after_pause
|
|
257
|
+
pause(coordinator.seek_offset, nil, false)
|
|
258
|
+
|
|
259
|
+
# Instrumentation needs to run **after** `#pause` invocation because we rely on the states
|
|
260
|
+
# set by `#pause`
|
|
261
|
+
Karafka.monitor.instrument(
|
|
262
|
+
'consumer.consuming.retry',
|
|
263
|
+
caller: self,
|
|
264
|
+
topic: messages.metadata.topic,
|
|
265
|
+
partition: messages.metadata.partition,
|
|
266
|
+
offset: coordinator.seek_offset,
|
|
267
|
+
timeout: coordinator.pause_tracker.current_timeout,
|
|
268
|
+
attempt: coordinator.pause_tracker.attempt
|
|
269
|
+
)
|
|
270
|
+
end
|
|
229
271
|
end
|
|
230
272
|
end
|
|
@@ -308,8 +308,8 @@ module Karafka
|
|
|
308
308
|
@closed = true
|
|
309
309
|
|
|
310
310
|
# Remove callbacks runners that were registered
|
|
311
|
-
::Karafka::Instrumentation.statistics_callbacks.delete(@subscription_group.id)
|
|
312
|
-
::Karafka::Instrumentation.error_callbacks.delete(@subscription_group.id)
|
|
311
|
+
::Karafka::Core::Instrumentation.statistics_callbacks.delete(@subscription_group.id)
|
|
312
|
+
::Karafka::Core::Instrumentation.error_callbacks.delete(@subscription_group.id)
|
|
313
313
|
|
|
314
314
|
@kafka.close
|
|
315
315
|
@buffer.clear
|
|
@@ -397,7 +397,7 @@ module Karafka
|
|
|
397
397
|
@name = consumer.name
|
|
398
398
|
|
|
399
399
|
# Register statistics runner for this particular type of callbacks
|
|
400
|
-
::Karafka::Instrumentation.statistics_callbacks.add(
|
|
400
|
+
::Karafka::Core::Instrumentation.statistics_callbacks.add(
|
|
401
401
|
@subscription_group.id,
|
|
402
402
|
Instrumentation::Callbacks::Statistics.new(
|
|
403
403
|
@subscription_group.id,
|
|
@@ -408,7 +408,7 @@ module Karafka
|
|
|
408
408
|
)
|
|
409
409
|
|
|
410
410
|
# Register error tracking callback
|
|
411
|
-
::Karafka::Instrumentation.error_callbacks.add(
|
|
411
|
+
::Karafka::Core::Instrumentation.error_callbacks.add(
|
|
412
412
|
@subscription_group.id,
|
|
413
413
|
Instrumentation::Callbacks::Error.new(
|
|
414
414
|
@subscription_group.id,
|
|
@@ -21,7 +21,7 @@ module Karafka
|
|
|
21
21
|
def initialize(consumer_group_coordinator, subscription_group, jobs_queue)
|
|
22
22
|
proc_config = ::Karafka::App.config.internal.processing
|
|
23
23
|
|
|
24
|
-
@id = SecureRandom.
|
|
24
|
+
@id = SecureRandom.hex(6)
|
|
25
25
|
@consumer_group_coordinator = consumer_group_coordinator
|
|
26
26
|
@subscription_group = subscription_group
|
|
27
27
|
@jobs_queue = jobs_queue
|
|
@@ -12,7 +12,7 @@ module Karafka
|
|
|
12
12
|
configure do |config|
|
|
13
13
|
config.error_messages = YAML.safe_load(
|
|
14
14
|
File.read(
|
|
15
|
-
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
|
15
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
|
16
16
|
)
|
|
17
17
|
).fetch('en').fetch('validations').fetch('config')
|
|
18
18
|
end
|
|
@@ -7,7 +7,7 @@ module Karafka
|
|
|
7
7
|
configure do |config|
|
|
8
8
|
config.error_messages = YAML.safe_load(
|
|
9
9
|
File.read(
|
|
10
|
-
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
|
10
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
|
11
11
|
)
|
|
12
12
|
).fetch('en').fetch('validations').fetch('consumer_group')
|
|
13
13
|
end
|
|
@@ -7,7 +7,7 @@ module Karafka
|
|
|
7
7
|
configure do |config|
|
|
8
8
|
config.error_messages = YAML.safe_load(
|
|
9
9
|
File.read(
|
|
10
|
-
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
|
10
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
|
11
11
|
)
|
|
12
12
|
).fetch('en').fetch('validations').fetch('server_cli_options')
|
|
13
13
|
end
|
|
@@ -7,7 +7,7 @@ module Karafka
|
|
|
7
7
|
configure do |config|
|
|
8
8
|
config.error_messages = YAML.safe_load(
|
|
9
9
|
File.read(
|
|
10
|
-
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
|
10
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
|
11
11
|
)
|
|
12
12
|
).fetch('en').fetch('validations').fetch('topic')
|
|
13
13
|
end
|
|
@@ -63,6 +63,38 @@ module Karafka
|
|
|
63
63
|
info "[#{job.id}] #{job_type} job for #{consumer} on #{topic} finished in #{time}ms"
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
# Prints info about a pause occurrence. Irrelevant if user or system initiated.
|
|
67
|
+
#
|
|
68
|
+
# @param event [Karafka::Core::Monitoring::Event] event details including payload
|
|
69
|
+
def on_consumer_consuming_pause(event)
|
|
70
|
+
topic = event[:topic]
|
|
71
|
+
partition = event[:partition]
|
|
72
|
+
offset = event[:offset]
|
|
73
|
+
consumer = event[:caller]
|
|
74
|
+
timeout = event[:timeout]
|
|
75
|
+
|
|
76
|
+
info <<~MSG.tr("\n", ' ').strip!
|
|
77
|
+
[#{consumer.id}] Pausing partition #{partition} of topic #{topic}
|
|
78
|
+
on offset #{offset} for #{timeout} ms.
|
|
79
|
+
MSG
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Prints info about retry of processing after an error
|
|
83
|
+
#
|
|
84
|
+
# @param event [Karafka::Core::Monitoring::Event] event details including payload
|
|
85
|
+
def on_consumer_consuming_retry(event)
|
|
86
|
+
topic = event[:topic]
|
|
87
|
+
partition = event[:partition]
|
|
88
|
+
offset = event[:offset]
|
|
89
|
+
consumer = event[:caller]
|
|
90
|
+
timeout = event[:timeout]
|
|
91
|
+
|
|
92
|
+
info <<~MSG.tr("\n", ' ').strip!
|
|
93
|
+
[#{consumer.id}] Retrying of #{consumer.class} after #{timeout} ms
|
|
94
|
+
on partition #{partition} of topic #{topic} from offset #{offset}
|
|
95
|
+
MSG
|
|
96
|
+
end
|
|
97
|
+
|
|
66
98
|
# Logs info about system signals that Karafka received and prints backtrace for threads in
|
|
67
99
|
# case of ttin
|
|
68
100
|
#
|
|
@@ -9,7 +9,19 @@ module Karafka
|
|
|
9
9
|
class Message
|
|
10
10
|
extend Forwardable
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
class << self
|
|
13
|
+
# @return [Object] general parser
|
|
14
|
+
# @note We cache it here for performance reasons. It is 2.5x times faster than getting it
|
|
15
|
+
# via the config chain.
|
|
16
|
+
def parser
|
|
17
|
+
@parser ||= App.config.internal.messages.parser
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :metadata
|
|
22
|
+
# raw payload needs to be mutable as we want to have option to change it in the parser
|
|
23
|
+
# prior to the final deserialization
|
|
24
|
+
attr_accessor :raw_payload
|
|
13
25
|
|
|
14
26
|
def_delegators :metadata, *Metadata.members
|
|
15
27
|
|
|
@@ -42,7 +54,7 @@ module Karafka
|
|
|
42
54
|
|
|
43
55
|
# @return [Object] deserialized data
|
|
44
56
|
def deserialize
|
|
45
|
-
|
|
57
|
+
self.class.parser.call(self)
|
|
46
58
|
end
|
|
47
59
|
end
|
|
48
60
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Messages
|
|
5
|
+
# Default message parser. The only thing it does, is calling the deserializer
|
|
6
|
+
class Parser
|
|
7
|
+
# @param message [::Karafka::Messages::Message]
|
|
8
|
+
# @return [Object] deserialized payload
|
|
9
|
+
def call(message)
|
|
10
|
+
message.metadata.deserializer.call(message)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -20,7 +20,7 @@ module Karafka
|
|
|
20
20
|
configure do |config|
|
|
21
21
|
config.error_messages = YAML.safe_load(
|
|
22
22
|
File.read(
|
|
23
|
-
File.join(Karafka.gem_root, 'config', 'errors.yml')
|
|
23
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
|
24
24
|
)
|
|
25
25
|
).fetch('en').fetch('validations').fetch('job_options')
|
|
26
26
|
end
|