karafka 2.4.8 → 2.4.10
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 +0 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +17 -0
- data/Gemfile +8 -5
- data/Gemfile.lock +24 -15
- data/bin/integrations +5 -0
- data/certs/cert.pem +26 -0
- data/config/locales/errors.yml +5 -0
- data/config/locales/pro_errors.yml +34 -0
- data/karafka.gemspec +1 -1
- data/lib/karafka/admin.rb +42 -0
- data/lib/karafka/base_consumer.rb +23 -0
- data/lib/karafka/contracts/config.rb +2 -0
- data/lib/karafka/contracts/consumer_group.rb +17 -0
- data/lib/karafka/errors.rb +3 -2
- data/lib/karafka/instrumentation/logger_listener.rb +3 -0
- data/lib/karafka/instrumentation/notifications.rb +3 -0
- data/lib/karafka/instrumentation/vendors/appsignal/client.rb +32 -11
- data/lib/karafka/instrumentation/vendors/appsignal/errors_listener.rb +1 -1
- data/lib/karafka/messages/message.rb +6 -0
- data/lib/karafka/pro/loader.rb +3 -1
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +16 -1
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +5 -1
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +17 -1
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +17 -1
- data/lib/karafka/pro/processing/strategies/dlq/mom.rb +22 -6
- data/lib/karafka/pro/recurring_tasks/consumer.rb +105 -0
- data/lib/karafka/pro/recurring_tasks/contracts/config.rb +53 -0
- data/lib/karafka/pro/recurring_tasks/contracts/task.rb +41 -0
- data/lib/karafka/pro/recurring_tasks/deserializer.rb +35 -0
- data/lib/karafka/pro/recurring_tasks/dispatcher.rb +87 -0
- data/lib/karafka/pro/recurring_tasks/errors.rb +34 -0
- data/lib/karafka/pro/recurring_tasks/executor.rb +152 -0
- data/lib/karafka/pro/recurring_tasks/listener.rb +38 -0
- data/lib/karafka/pro/recurring_tasks/matcher.rb +38 -0
- data/lib/karafka/pro/recurring_tasks/schedule.rb +63 -0
- data/lib/karafka/pro/recurring_tasks/serializer.rb +113 -0
- data/lib/karafka/pro/recurring_tasks/setup/config.rb +52 -0
- data/lib/karafka/pro/recurring_tasks/task.rb +151 -0
- data/lib/karafka/pro/recurring_tasks.rb +87 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/builder.rb +131 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/config.rb +28 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/contracts/topic.rb +40 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/proxy.rb +27 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/topic.rb +44 -0
- data/lib/karafka/pro/routing/features/recurring_tasks.rb +25 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/builder.rb +131 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/config.rb +28 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/contracts/topic.rb +40 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/proxy.rb +27 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/topic.rb +44 -0
- data/lib/karafka/pro/routing/features/scheduled_messages.rb +24 -0
- data/lib/karafka/pro/scheduled_messages/consumer.rb +185 -0
- data/lib/karafka/pro/scheduled_messages/contracts/config.rb +56 -0
- data/lib/karafka/pro/scheduled_messages/contracts/message.rb +61 -0
- data/lib/karafka/pro/scheduled_messages/daily_buffer.rb +79 -0
- data/lib/karafka/pro/scheduled_messages/day.rb +45 -0
- data/lib/karafka/pro/scheduled_messages/deserializers/headers.rb +46 -0
- data/lib/karafka/pro/scheduled_messages/deserializers/payload.rb +35 -0
- data/lib/karafka/pro/scheduled_messages/dispatcher.rb +122 -0
- data/lib/karafka/pro/scheduled_messages/errors.rb +28 -0
- data/lib/karafka/pro/scheduled_messages/max_epoch.rb +41 -0
- data/lib/karafka/pro/scheduled_messages/proxy.rb +176 -0
- data/lib/karafka/pro/scheduled_messages/schema_validator.rb +37 -0
- data/lib/karafka/pro/scheduled_messages/serializer.rb +55 -0
- data/lib/karafka/pro/scheduled_messages/setup/config.rb +60 -0
- data/lib/karafka/pro/scheduled_messages/state.rb +62 -0
- data/lib/karafka/pro/scheduled_messages/tracker.rb +64 -0
- data/lib/karafka/pro/scheduled_messages.rb +67 -0
- data/lib/karafka/processing/executor.rb +6 -0
- data/lib/karafka/processing/strategies/default.rb +10 -0
- data/lib/karafka/processing/strategies/dlq.rb +16 -2
- data/lib/karafka/processing/strategies/dlq_mom.rb +25 -6
- data/lib/karafka/processing/worker.rb +11 -1
- data/lib/karafka/railtie.rb +11 -42
- data/lib/karafka/routing/features/dead_letter_queue/config.rb +3 -0
- data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +1 -0
- data/lib/karafka/routing/features/dead_letter_queue/topic.rb +7 -2
- data/lib/karafka/routing/features/eofed/contracts/topic.rb +12 -0
- data/lib/karafka/routing/topic.rb +14 -0
- data/lib/karafka/setup/config.rb +3 -0
- data/lib/karafka/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +68 -25
- metadata.gz.sig +0 -0
- data/certs/cert_chain.pem +0 -26
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7601c2daf3eaacae67697fe28dd403ba8bd41387df90da49691912dad7ba0963
|
|
4
|
+
data.tar.gz: 23d6763195d2e6bf17c573859d133637c21f849e3def1eb0f852d5cb4554ae17
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56202acc444f3b69af7a8b643b9e28f77ffbcadeab70858b0ffaa4b4a7a264082c636ff5c0abbaba0ac1cd6f2fb72fd6924bf6e87fe3f6e57549d5f228786e91
|
|
7
|
+
data.tar.gz: b39d96ef2bcd09079b044321058b4a741797673b815ff6508a8123a7a513f08ca364d05aa22dc994a25fe8c64d959bcd19342301156e97bf0b7d005d7abcb7db
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/.github/workflows/ci.yml
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.3.
|
|
1
|
+
3.3.5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Karafka Framework Changelog
|
|
2
2
|
|
|
3
|
+
## 2.4.10 (2024-09-03)
|
|
4
|
+
- **[Feature]** Provide Kafka based Scheduled Messages to be able to send messages in the future via a proxy topic.
|
|
5
|
+
- [Enhancement] Introduce a `#assigned` hook for consumers to be able to trigger actions when consumer is built and assigned but before first consume/ticking, etc.
|
|
6
|
+
- [Enhancement] Provide `Karafka::Messages::Message#tombstone?` to be able to quickly check if a message is a tombstone message.
|
|
7
|
+
- [Enhancement] Provide more flexible API for Recurring Tasks topics reconfiguration.
|
|
8
|
+
- [Enhancement] Remove no longer needed Rails connection releaser.
|
|
9
|
+
- [Enhancement] Update AppSignal client to support newer versions (tombruijn and hieuk09).
|
|
10
|
+
- [Fix] Fix a case where there would be a way to define multiple subscription groups for same topic with different consumer.
|
|
11
|
+
|
|
12
|
+
## 2.4.9 (2024-08-23)
|
|
13
|
+
- **[Feature]** Provide Kafka based Recurring (Cron) Tasks.
|
|
14
|
+
- [Enhancement] Wrap worker work with Rails Reloader/Executor (fusion2004)
|
|
15
|
+
- [Enhancement] Allow for partial topic level kafka scope settings reconfiguration via `inherit` flag.
|
|
16
|
+
- [Enhancement] Validate `eof` kafka scope flag when `eofed` in routing enabled.
|
|
17
|
+
- [Enhancement] Provide `mark_after_dispatch` setting for granular DLQ marking control.
|
|
18
|
+
- [Enhancement] Provide `Karafka::Admin.rename_consumer_group`.
|
|
19
|
+
|
|
3
20
|
## 2.4.8 (2024-08-09)
|
|
4
21
|
- **[Feature]** Introduce ability to react to `#eof` either from `#consume` or from `#eofed` when EOF without new messages.
|
|
5
22
|
- [Enhancement] Provide `Consumer#eofed?` to indicate reaching EOF.
|
data/Gemfile
CHANGED
|
@@ -6,20 +6,23 @@ plugin 'diffend'
|
|
|
6
6
|
|
|
7
7
|
gemspec
|
|
8
8
|
|
|
9
|
-
# Karafka gem does not require activejob
|
|
9
|
+
# Karafka gem does not require activejob, karafka-web or fugit to work
|
|
10
10
|
# They are added here because they are part of the integration suite
|
|
11
11
|
# Since some of those are only needed for some specs, they should never be required automatically
|
|
12
|
+
group :integrations, :test do
|
|
13
|
+
gem 'fugit', require: false
|
|
14
|
+
gem 'rspec', require: false
|
|
15
|
+
end
|
|
16
|
+
|
|
12
17
|
group :integrations do
|
|
13
18
|
gem 'activejob', require: false
|
|
14
|
-
gem 'karafka-testing', '>= 2.4.
|
|
15
|
-
gem 'karafka-web', '>= 0.10.0.
|
|
16
|
-
gem 'rspec', require: false
|
|
19
|
+
gem 'karafka-testing', '>= 2.4.6', require: false
|
|
20
|
+
gem 'karafka-web', '>= 0.10.0.rc2', require: false
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
group :test do
|
|
20
24
|
gem 'byebug'
|
|
21
25
|
gem 'factory_bot'
|
|
22
26
|
gem 'ostruct'
|
|
23
|
-
gem 'rspec'
|
|
24
27
|
gem 'simplecov'
|
|
25
28
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
karafka (2.4.
|
|
4
|
+
karafka (2.4.10)
|
|
5
5
|
base64 (~> 0.2)
|
|
6
6
|
karafka-core (>= 2.4.3, < 2.5.0)
|
|
7
7
|
karafka-rdkafka (>= 0.17.2)
|
|
@@ -11,31 +11,37 @@ PATH
|
|
|
11
11
|
GEM
|
|
12
12
|
remote: https://rubygems.org/
|
|
13
13
|
specs:
|
|
14
|
-
activejob (7.1
|
|
15
|
-
activesupport (= 7.1
|
|
14
|
+
activejob (7.2.1)
|
|
15
|
+
activesupport (= 7.2.1)
|
|
16
16
|
globalid (>= 0.3.6)
|
|
17
|
-
activesupport (7.1
|
|
17
|
+
activesupport (7.2.1)
|
|
18
18
|
base64
|
|
19
19
|
bigdecimal
|
|
20
|
-
concurrent-ruby (~> 1.0, >= 1.
|
|
20
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
21
21
|
connection_pool (>= 2.2.5)
|
|
22
22
|
drb
|
|
23
23
|
i18n (>= 1.6, < 2)
|
|
24
|
+
logger (>= 1.4.2)
|
|
24
25
|
minitest (>= 5.1)
|
|
25
|
-
|
|
26
|
-
tzinfo (~> 2.0)
|
|
26
|
+
securerandom (>= 0.3)
|
|
27
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
27
28
|
base64 (0.2.0)
|
|
28
29
|
bigdecimal (3.1.8)
|
|
29
30
|
byebug (11.1.3)
|
|
30
|
-
concurrent-ruby (1.3.
|
|
31
|
+
concurrent-ruby (1.3.4)
|
|
31
32
|
connection_pool (2.4.1)
|
|
32
33
|
diff-lcs (1.5.1)
|
|
33
34
|
docile (1.4.1)
|
|
34
35
|
drb (2.2.1)
|
|
35
36
|
erubi (1.13.0)
|
|
37
|
+
et-orbi (1.2.11)
|
|
38
|
+
tzinfo
|
|
36
39
|
factory_bot (6.4.6)
|
|
37
40
|
activesupport (>= 5.0.0)
|
|
38
41
|
ffi (1.17.0)
|
|
42
|
+
fugit (1.11.1)
|
|
43
|
+
et-orbi (~> 1, >= 1.2.11)
|
|
44
|
+
raabro (~> 1.4)
|
|
39
45
|
globalid (1.2.1)
|
|
40
46
|
activesupport (>= 6.1)
|
|
41
47
|
i18n (1.14.5)
|
|
@@ -49,19 +55,20 @@ GEM
|
|
|
49
55
|
karafka-testing (2.4.6)
|
|
50
56
|
karafka (>= 2.4.0, < 2.5.0)
|
|
51
57
|
waterdrop (>= 2.7.0)
|
|
52
|
-
karafka-web (0.10.
|
|
58
|
+
karafka-web (0.10.1)
|
|
53
59
|
erubi (~> 1.4)
|
|
54
|
-
karafka (>= 2.4.
|
|
60
|
+
karafka (>= 2.4.9, < 2.5.0)
|
|
55
61
|
karafka-core (>= 2.4.0, < 2.5.0)
|
|
56
62
|
roda (~> 3.68, >= 3.69)
|
|
57
63
|
tilt (~> 2.0)
|
|
64
|
+
logger (1.6.0)
|
|
58
65
|
mini_portile2 (2.8.7)
|
|
59
|
-
minitest (5.
|
|
60
|
-
mutex_m (0.2.0)
|
|
66
|
+
minitest (5.25.1)
|
|
61
67
|
ostruct (0.6.0)
|
|
68
|
+
raabro (1.4.0)
|
|
62
69
|
rack (3.1.7)
|
|
63
70
|
rake (13.2.1)
|
|
64
|
-
roda (3.
|
|
71
|
+
roda (3.83.0)
|
|
65
72
|
rack
|
|
66
73
|
rspec (3.13.0)
|
|
67
74
|
rspec-core (~> 3.13.0)
|
|
@@ -76,6 +83,7 @@ GEM
|
|
|
76
83
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
77
84
|
rspec-support (~> 3.13.0)
|
|
78
85
|
rspec-support (3.13.1)
|
|
86
|
+
securerandom (0.3.1)
|
|
79
87
|
simplecov (0.22.0)
|
|
80
88
|
docile (~> 1.1)
|
|
81
89
|
simplecov-html (~> 0.11)
|
|
@@ -99,9 +107,10 @@ DEPENDENCIES
|
|
|
99
107
|
activejob
|
|
100
108
|
byebug
|
|
101
109
|
factory_bot
|
|
110
|
+
fugit
|
|
102
111
|
karafka!
|
|
103
|
-
karafka-testing (>= 2.4.
|
|
104
|
-
karafka-web (>= 0.10.0.
|
|
112
|
+
karafka-testing (>= 2.4.6)
|
|
113
|
+
karafka-web (>= 0.10.0.rc2)
|
|
105
114
|
ostruct
|
|
106
115
|
rspec
|
|
107
116
|
simplecov
|
data/bin/integrations
CHANGED
|
@@ -240,6 +240,11 @@ ARGV.each do |filter|
|
|
|
240
240
|
end
|
|
241
241
|
end
|
|
242
242
|
|
|
243
|
+
# Remove Rails 7.2 specs from Ruby 3.0 because it requires 3.1
|
|
244
|
+
specs.delete_if do |spec|
|
|
245
|
+
RUBY_VERSION < '3.1' && spec.include?('rails72')
|
|
246
|
+
end
|
|
247
|
+
|
|
243
248
|
raise ArgumentError, "No integration specs with filters: #{ARGV.join(', ')}" if specs.empty?
|
|
244
249
|
|
|
245
250
|
# Randomize order
|
data/certs/cert.pem
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIEcDCCAtigAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MRAwDgYDVQQDDAdjb250
|
|
3
|
+
YWN0MRcwFQYKCZImiZPyLGQBGRYHa2FyYWZrYTESMBAGCgmSJomT8ixkARkWAmlv
|
|
4
|
+
MB4XDTI0MDgyMzEwMTkyMFoXDTQ5MDgxNzEwMTkyMFowPzEQMA4GA1UEAwwHY29u
|
|
5
|
+
dGFjdDEXMBUGCgmSJomT8ixkARkWB2thcmFma2ExEjAQBgoJkiaJk/IsZAEZFgJp
|
|
6
|
+
bzCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAKjLhLjQqUlNayxkXnO+
|
|
7
|
+
PsmCDs/KFIzhrsYMfLZRZNaWmzV3ujljMOdDjd4snM2X06C41iVdQPWjpe3j8vVe
|
|
8
|
+
ZXEWR/twSbOP6Eeg8WVH2wCOo0x5i7yhVn4UBLH4JpfEMCbemVcWQ9ry9OMg4WpH
|
|
9
|
+
Uu4dRwxFV7hzCz3p0QfNLRI4miAxnGWcnlD98IJRjBAksTuR1Llj0vbOrDGsL9ZT
|
|
10
|
+
JeXP2gdRLd8SqzAFJEWrbeTBCBU7gfSh3oMg5SVDLjaqf7Kz5wC/8bDZydzanOxB
|
|
11
|
+
T6CDXPsCnllmvTNx2ei2T5rGYJOzJeNTmJLLK6hJWUlAvaQSvCwZRvFJ0tVGLEoS
|
|
12
|
+
flqSr6uGyyl1eMUsNmsH4BqPEYcAV6P2PKTv2vUR8AP0raDvZ3xL1TKvfRb8xRpo
|
|
13
|
+
vPopCGlY5XBWEc6QERHfVLTIVsjnls2/Ujj4h8/TSfqqYnaHKefIMLbuD/tquMjD
|
|
14
|
+
iWQsW2qStBV0T+U7FijKxVfrfqZP7GxQmDAc9o1iiyAa3QIDAQABo3cwdTAJBgNV
|
|
15
|
+
HRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU3O4dTXmvE7YpAkszGzR9DdL9
|
|
16
|
+
sbEwHQYDVR0RBBYwFIESY29udGFjdEBrYXJhZmthLmlvMB0GA1UdEgQWMBSBEmNv
|
|
17
|
+
bnRhY3RAa2FyYWZrYS5pbzANBgkqhkiG9w0BAQsFAAOCAYEAVKTfoLXn7mqdSxIR
|
|
18
|
+
eqxcR6Huudg1jes81s1+X0uiRTR3hxxKZ3Y82cPsee9zYWyBrN8TA4KA0WILTru7
|
|
19
|
+
Ygxvzha0SRPsSiaKLmgOJ+61ebI4+bOORzIJLpD6GxCxu1r7MI4+0r1u1xe0EWi8
|
|
20
|
+
agkVo1k4Vi8cKMLm6Gl9b3wG9zQBw6fcgKwmpjKiNnOLP+OytzUANrIUJjoq6oal
|
|
21
|
+
TC+f/Uc0TLaRqUaW/bejxzDWWHoM3SU6aoLPuerglzp9zZVzihXwx3jPLUVKDFpF
|
|
22
|
+
Rl2lcBDxlpYGueGo0/oNzGJAAy6js8jhtHC9+19PD53vk7wHtFTZ/0ugDQYnwQ+x
|
|
23
|
+
oml2fAAuVWpTBCgOVFe6XCQpMKopzoxQ1PjKztW2KYxgJdIBX87SnL3aWuBQmhRd
|
|
24
|
+
i9zWxov0mr44TWegTVeypcWGd/0nxu1+QHVNHJrpqlPBRvwQsUm7fwmRInGpcaB8
|
|
25
|
+
ap8wNYvryYzrzvzUxIVFBVM5PacgkFqRmolCa8I7tdKQN+R1
|
|
26
|
+
-----END CERTIFICATE-----
|
data/config/locales/errors.yml
CHANGED
|
@@ -33,6 +33,8 @@ en:
|
|
|
33
33
|
internal.processing.partitioner_class_format: cannot be nil
|
|
34
34
|
internal.processing.strategy_selector_format: cannot be nil
|
|
35
35
|
internal.processing.expansions_selector_format: cannot be nil
|
|
36
|
+
internal.processing.executor_class_format: cannot be nil
|
|
37
|
+
internal.processing.worker_job_call_wrapper_format: 'needs to be false or respond to #wrap'
|
|
36
38
|
|
|
37
39
|
internal.active_job.dispatcher_format: cannot be nil
|
|
38
40
|
internal.active_job.job_options_contract_format: cannot be nil
|
|
@@ -113,10 +115,12 @@ en:
|
|
|
113
115
|
dead_letter_queue.transactional_format: needs to be either true or false
|
|
114
116
|
dead_letter_queue.dispatch_method_format: 'needs to be either #produce_sync or #produce_async'
|
|
115
117
|
dead_letter_queue.marking_method_format: 'needs to be either #mark_as_consumed or #mark_as_consumed!'
|
|
118
|
+
dead_letter_queue.mark_after_dispatch_format: 'needs to be true, false or nil'
|
|
116
119
|
|
|
117
120
|
active_format: needs to be either true or false
|
|
118
121
|
|
|
119
122
|
eofed.active_format: needs to be either true or false
|
|
123
|
+
eofed.kafka_enable: 'cannot be enabled without enable.partition.eof set to true'
|
|
120
124
|
|
|
121
125
|
declaratives.partitions_format: needs to be more or equal to 1
|
|
122
126
|
declaratives.active_format: needs to be true
|
|
@@ -136,6 +140,7 @@ en:
|
|
|
136
140
|
consumer_group:
|
|
137
141
|
missing: needs to be present
|
|
138
142
|
topics_names_not_unique: all topic names within a single consumer group must be unique
|
|
143
|
+
topics_many_consumers_same_topic: 'topic within a single consumer group cannot have distinct consumers'
|
|
139
144
|
id_format: 'needs to be a string with a Kafka accepted format'
|
|
140
145
|
topics_format: needs to be a non-empty array
|
|
141
146
|
topics_namespaced_names_not_unique: |
|
|
@@ -63,6 +63,10 @@ en:
|
|
|
63
63
|
swarm.nodes_format: needs to be a range, array of nodes ids or a hash with direct assignments
|
|
64
64
|
swarm_nodes_with_non_existent_nodes: includes unreachable nodes ids
|
|
65
65
|
|
|
66
|
+
recurring_tasks.active_format: 'needs to be boolean'
|
|
67
|
+
scheduled_messages.active_format: 'needs to be boolean'
|
|
68
|
+
scheduled_messages.active_missing: 'needs to be boolean'
|
|
69
|
+
|
|
66
70
|
direct_assignments.active_missing: needs to be present
|
|
67
71
|
direct_assignments.active_format: 'needs to be boolean'
|
|
68
72
|
direct_assignments.partitions_missing: 'needs to be present'
|
|
@@ -99,5 +103,35 @@ en:
|
|
|
99
103
|
patterns.ttl_format: needs to be an integer bigger than 0
|
|
100
104
|
patterns.ttl_missing: needs to be present
|
|
101
105
|
|
|
106
|
+
recurring_tasks.consumer_class_format: 'needs to inherit from Karafka::BaseConsumer'
|
|
107
|
+
recurring_tasks.group_id_format: 'needs to be a string with a Kafka accepted format'
|
|
108
|
+
recurring_tasks.topics.schedules_format: 'needs to be a string with a Kafka accepted format'
|
|
109
|
+
recurring_tasks.topics.logs_format: 'needs to be a string with a Kafka accepted format'
|
|
110
|
+
recurring_tasks.interval_format: 'needs to be equal or more than 1000 and an integer'
|
|
111
|
+
recurring_tasks.deserializer_format: 'needs to be configured'
|
|
112
|
+
recurring_tasks.logging_format: needs to be a boolean
|
|
113
|
+
|
|
114
|
+
scheduled_messages.consumer_class_format: 'must be a class'
|
|
115
|
+
scheduled_messages.dispatcher_class_format: 'must be a class'
|
|
116
|
+
scheduled_messages.flush_batch_size_format: needs to be an integer bigger than 0
|
|
117
|
+
scheduled_messages.interval_format: needs to be an integer bigger or equal to 1000
|
|
118
|
+
scheduled_messages.deserializers.headers_format: cannot be nil
|
|
119
|
+
scheduled_messages.deserializers.payload_format: cannot be nil
|
|
120
|
+
scheduled_messages.group_id_format: 'needs to be a string with a Kafka accepted format'
|
|
121
|
+
scheduled_messages.states_postfix_format: 'needs to be a string with a Kafka accepted format'
|
|
122
|
+
|
|
102
123
|
routing:
|
|
103
124
|
swarm_nodes_not_used: 'At least one of the nodes has no assignments'
|
|
125
|
+
|
|
126
|
+
recurring_tasks:
|
|
127
|
+
id_format: 'can include only alphanumeric characters (a-z, A-Z, 0-9), hyphens (-), and underscores (_)'
|
|
128
|
+
cron_format: must be a non-empty string
|
|
129
|
+
enabled_format: needs to be a boolean
|
|
130
|
+
changed_format: needs to be a boolean
|
|
131
|
+
previous_time_format: needs to be a numerical or time
|
|
132
|
+
|
|
133
|
+
scheduled_messages_message:
|
|
134
|
+
key_missing: must be present and should be unique within the partition
|
|
135
|
+
key_format: needs to be a non-empty string unique within the partition
|
|
136
|
+
headers_schedule_target_epoch_in_the_past: 'scheduling cannot happen in the past'
|
|
137
|
+
headers_format: are not correct
|
data/karafka.gemspec
CHANGED
|
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
|
33
33
|
spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
spec.cert_chain = %w[certs/
|
|
36
|
+
spec.cert_chain = %w[certs/cert.pem]
|
|
37
37
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
|
38
38
|
spec.executables = %w[karafka]
|
|
39
39
|
spec.require_paths = %w[lib]
|
data/lib/karafka/admin.rb
CHANGED
|
@@ -274,6 +274,48 @@ module Karafka
|
|
|
274
274
|
end
|
|
275
275
|
end
|
|
276
276
|
|
|
277
|
+
# Takes consumer group and its topics and migrates all the offsets to a new named group
|
|
278
|
+
#
|
|
279
|
+
# @param previous_name [String] old consumer group name
|
|
280
|
+
# @param new_name [String] new consumer group name
|
|
281
|
+
# @param topics [Array<String>] topics for which we want to migrate offsets during rename
|
|
282
|
+
# @param delete_previous [Boolean] should we delete previous consumer group after rename.
|
|
283
|
+
# Defaults to true.
|
|
284
|
+
#
|
|
285
|
+
# @note This method should **not** be executed on a running consumer group as it creates a
|
|
286
|
+
# "fake" consumer and uses it to move offsets.
|
|
287
|
+
#
|
|
288
|
+
# @note After migration unless `delete_previous` is set to `false`, old group will be
|
|
289
|
+
# removed.
|
|
290
|
+
#
|
|
291
|
+
# @note If new consumer group exists, old offsets will be added to it.
|
|
292
|
+
def rename_consumer_group(previous_name, new_name, topics, delete_previous: true)
|
|
293
|
+
remap = Hash.new { |h, k| h[k] = {} }
|
|
294
|
+
|
|
295
|
+
old_lags = read_lags_with_offsets({ previous_name => topics })
|
|
296
|
+
|
|
297
|
+
return if old_lags.empty?
|
|
298
|
+
|
|
299
|
+
read_lags_with_offsets({ previous_name => topics })
|
|
300
|
+
.fetch(previous_name)
|
|
301
|
+
.each do |topic, partitions|
|
|
302
|
+
partitions.each do |partition_id, details|
|
|
303
|
+
offset = details[:offset]
|
|
304
|
+
|
|
305
|
+
# No offset on this partition
|
|
306
|
+
next if offset.negative?
|
|
307
|
+
|
|
308
|
+
remap[topic][partition_id] = offset
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
seek_consumer_group(new_name, remap)
|
|
313
|
+
|
|
314
|
+
return unless delete_previous
|
|
315
|
+
|
|
316
|
+
delete_consumer_group(previous_name)
|
|
317
|
+
end
|
|
318
|
+
|
|
277
319
|
# Removes given consumer group (if exists)
|
|
278
320
|
#
|
|
279
321
|
# @param consumer_group_id [String] consumer group name
|
|
@@ -31,6 +31,20 @@ module Karafka
|
|
|
31
31
|
@used = false
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
# Trigger method running after consumer is fully initialized.
|
|
35
|
+
#
|
|
36
|
+
# @private
|
|
37
|
+
def on_initialized
|
|
38
|
+
handle_initialized
|
|
39
|
+
rescue StandardError => e
|
|
40
|
+
Karafka.monitor.instrument(
|
|
41
|
+
'error.occurred',
|
|
42
|
+
error: e,
|
|
43
|
+
caller: self,
|
|
44
|
+
type: 'consumer.initialized.error'
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
34
48
|
# Can be used to run preparation code prior to the job being enqueued
|
|
35
49
|
#
|
|
36
50
|
# @private
|
|
@@ -176,6 +190,15 @@ module Karafka
|
|
|
176
190
|
|
|
177
191
|
private
|
|
178
192
|
|
|
193
|
+
# Method called post-initialization of a consumer when all basic things are assigned.
|
|
194
|
+
# Since initialization via `#initialize` is complex and some states are set a bit later, this
|
|
195
|
+
# hook allows to initialize resources once at a time when topic, partition and other things
|
|
196
|
+
# are assigned to the consumer
|
|
197
|
+
#
|
|
198
|
+
# @note Please keep in mind that it will run many times when persistence is off. Basically once
|
|
199
|
+
# each batch.
|
|
200
|
+
def initialized; end
|
|
201
|
+
|
|
179
202
|
# Method that will perform business logic and on data received from Kafka (it will consume
|
|
180
203
|
# the data)
|
|
181
204
|
# @note This method needs to be implemented in a subclass. We stub it here as a failover if
|
|
@@ -116,6 +116,8 @@ module Karafka
|
|
|
116
116
|
required(:partitioner_class) { |val| !val.nil? }
|
|
117
117
|
required(:strategy_selector) { |val| !val.nil? }
|
|
118
118
|
required(:expansions_selector) { |val| !val.nil? }
|
|
119
|
+
required(:executor_class) { |val| !val.nil? }
|
|
120
|
+
required(:worker_job_call_wrapper) { |val| val == false || val.respond_to?(:wrap) }
|
|
119
121
|
end
|
|
120
122
|
|
|
121
123
|
nested(:active_job) do
|
|
@@ -25,6 +25,23 @@ module Karafka
|
|
|
25
25
|
[[%i[topics], :names_not_unique]]
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# Prevent same topics subscriptions in one CG with different consumer classes
|
|
29
|
+
# This should prevent users from accidentally creating multi-sg one CG setup with weird
|
|
30
|
+
# different consumer usage. If you need to consume same topic twice, use distinct CGs.
|
|
31
|
+
virtual do |data, errors|
|
|
32
|
+
next unless errors.empty?
|
|
33
|
+
|
|
34
|
+
topics_consumers = Hash.new { |h, k| h[k] = Set.new }
|
|
35
|
+
|
|
36
|
+
data.fetch(:topics).map do |topic|
|
|
37
|
+
topics_consumers[topic[:name]] << topic[:consumer]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
next if topics_consumers.values.map(&:size).all? { |count| count == 1 }
|
|
41
|
+
|
|
42
|
+
[[%i[topics], :many_consumers_same_topic]]
|
|
43
|
+
end
|
|
44
|
+
|
|
28
45
|
virtual do |data, errors|
|
|
29
46
|
next unless errors.empty?
|
|
30
47
|
next unless ::Karafka::App.config.strict_topics_namespacing
|
data/lib/karafka/errors.rb
CHANGED
|
@@ -82,10 +82,11 @@ module Karafka
|
|
|
82
82
|
AssignmentLostError = Class.new(BaseError)
|
|
83
83
|
|
|
84
84
|
# Raised if optional dependencies like karafka-web are required in a version that is not
|
|
85
|
-
# supported by the current framework version.
|
|
85
|
+
# supported by the current framework version or when an optional dependency is missing.
|
|
86
86
|
#
|
|
87
87
|
# Because we do not want to require web out of the box and we do not want to lock web with
|
|
88
|
-
# karafka 1:1, we do such a sanity check
|
|
88
|
+
# karafka 1:1, we do such a sanity check. This also applies to cases where some external
|
|
89
|
+
# optional dependencies are needed but not available.
|
|
89
90
|
DependencyConstraintsError = Class.new(BaseError)
|
|
90
91
|
|
|
91
92
|
# Raised when we were not able to open pidfd for given pid
|
|
@@ -275,6 +275,9 @@ module Karafka
|
|
|
275
275
|
details = (error.backtrace || []).join("\n")
|
|
276
276
|
|
|
277
277
|
case type
|
|
278
|
+
when 'consumer.initialized.error'
|
|
279
|
+
error "Consumer initialized error: #{error}"
|
|
280
|
+
error details
|
|
278
281
|
when 'consumer.consume.error'
|
|
279
282
|
error "Consumer consuming error: #{error}"
|
|
280
283
|
error details
|
|
@@ -23,11 +23,16 @@ module Karafka
|
|
|
23
23
|
# @param action_name [String] action name. For processing this should be equal to
|
|
24
24
|
# consumer class + method name
|
|
25
25
|
def start_transaction(action_name)
|
|
26
|
-
transaction =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
transaction =
|
|
27
|
+
if version_4_or_newer?
|
|
28
|
+
::Appsignal::Transaction.create(namespace_name)
|
|
29
|
+
else
|
|
30
|
+
::Appsignal::Transaction.create(
|
|
31
|
+
SecureRandom.uuid,
|
|
32
|
+
namespace_name,
|
|
33
|
+
::Appsignal::Transaction::GenericRequest.new({})
|
|
34
|
+
)
|
|
35
|
+
end
|
|
31
36
|
|
|
32
37
|
transaction.set_action_if_nil(action_name)
|
|
33
38
|
end
|
|
@@ -45,10 +50,10 @@ module Karafka
|
|
|
45
50
|
def metadata=(metadata_hash)
|
|
46
51
|
return unless transaction?
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
current_transaction = transaction
|
|
49
54
|
|
|
50
55
|
stringify_hash(metadata_hash).each do |key, value|
|
|
51
|
-
|
|
56
|
+
current_transaction.set_metadata(key, value)
|
|
52
57
|
end
|
|
53
58
|
end
|
|
54
59
|
|
|
@@ -78,15 +83,20 @@ module Karafka
|
|
|
78
83
|
)
|
|
79
84
|
end
|
|
80
85
|
|
|
81
|
-
#
|
|
86
|
+
# Report the error that occurred to Appsignal
|
|
82
87
|
#
|
|
83
88
|
# @param error [Object] error we want to ship to Appsignal
|
|
84
|
-
def
|
|
89
|
+
def report_error(error)
|
|
90
|
+
if ::Appsignal.respond_to?(:report_error)
|
|
91
|
+
# This helper will always report the error
|
|
92
|
+
::Appsignal.report_error(error) do |transaction|
|
|
93
|
+
transaction.set_namespace(namespace_name)
|
|
94
|
+
end
|
|
85
95
|
# If we have an active transaction we should use it instead of creating a generic one
|
|
86
96
|
# That way proper namespace and other data may be transferred
|
|
87
97
|
#
|
|
88
98
|
# In case there is no transaction, a new generic background job one will be used
|
|
89
|
-
|
|
99
|
+
elsif transaction?
|
|
90
100
|
transaction.set_error(error)
|
|
91
101
|
else
|
|
92
102
|
::Appsignal.send_error(error) do |transaction|
|
|
@@ -99,7 +109,11 @@ module Karafka
|
|
|
99
109
|
# @param name [Symbol] probe name
|
|
100
110
|
# @param probe [Proc] code to run every minute
|
|
101
111
|
def register_probe(name, probe)
|
|
102
|
-
::Appsignal::
|
|
112
|
+
if ::Appsignal::Probes.respond_to?(:register)
|
|
113
|
+
::Appsignal::Probes.register(name, probe)
|
|
114
|
+
else
|
|
115
|
+
::Appsignal::Minutely.probes.register(name, probe)
|
|
116
|
+
end
|
|
103
117
|
end
|
|
104
118
|
|
|
105
119
|
private
|
|
@@ -129,6 +143,13 @@ module Karafka
|
|
|
129
143
|
def namespace_name
|
|
130
144
|
@namespace_name ||= ::Appsignal::Transaction::BACKGROUND_JOB
|
|
131
145
|
end
|
|
146
|
+
|
|
147
|
+
# @return [Boolean] is this v4+ version of Appsignal gem or older. Used for backwards
|
|
148
|
+
# compatibility checks.
|
|
149
|
+
def version_4_or_newer?
|
|
150
|
+
@version_4_or_newer ||=
|
|
151
|
+
Gem::Version.new(Appsignal::VERSION) >= Gem::Version.new('4.0.0')
|
|
152
|
+
end
|
|
132
153
|
end
|
|
133
154
|
end
|
|
134
155
|
end
|
|
@@ -51,6 +51,12 @@ module Karafka
|
|
|
51
51
|
@deserialized
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
# @return [Boolean] true if the message has a key and raw payload is nil, it is a tombstone
|
|
55
|
+
# event. Otherwise it is not.
|
|
56
|
+
def tombstone?
|
|
57
|
+
!raw_key.nil? && @raw_payload.nil?
|
|
58
|
+
end
|
|
59
|
+
|
|
54
60
|
private
|
|
55
61
|
|
|
56
62
|
# @return [Object] deserialized data
|
data/lib/karafka/pro/loader.rb
CHANGED
|
@@ -135,7 +135,12 @@ module Karafka
|
|
|
135
135
|
|
|
136
136
|
dispatch = lambda do
|
|
137
137
|
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
|
138
|
-
|
|
138
|
+
|
|
139
|
+
if mark_after_dispatch?
|
|
140
|
+
mark_dispatched_to_dlq(skippable_message)
|
|
141
|
+
else
|
|
142
|
+
coordinator.seek_offset = skippable_message.offset + 1
|
|
143
|
+
end
|
|
139
144
|
end
|
|
140
145
|
|
|
141
146
|
if dispatch_in_a_transaction?
|
|
@@ -193,6 +198,16 @@ module Karafka
|
|
|
193
198
|
producer.transactional? && topic.dead_letter_queue.transactional?
|
|
194
199
|
end
|
|
195
200
|
|
|
201
|
+
# @return [Boolean] should we mark given message as consumed after dispatch.
|
|
202
|
+
# For default non MOM strategies if user did not explicitly tell us not to, we mark
|
|
203
|
+
# it. Default is `nil`, which means `true` in this case. If user provided alternative
|
|
204
|
+
# value, we go with it.
|
|
205
|
+
def mark_after_dispatch?
|
|
206
|
+
return true if topic.dead_letter_queue.mark_after_dispatch.nil?
|
|
207
|
+
|
|
208
|
+
topic.dead_letter_queue.mark_after_dispatch
|
|
209
|
+
end
|
|
210
|
+
|
|
196
211
|
# Runs the DLQ strategy and based on it it performs certain operations
|
|
197
212
|
#
|
|
198
213
|
# In case of `:skip` and `:dispatch` will run the exact flow provided in a block
|
|
@@ -55,7 +55,11 @@ module Karafka
|
|
|
55
55
|
skippable_message, _marked = find_skippable_message
|
|
56
56
|
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
if mark_after_dispatch?
|
|
59
|
+
mark_dispatched_to_dlq(skippable_message)
|
|
60
|
+
else
|
|
61
|
+
coordinator.seek_offset = skippable_message.offset + 1
|
|
62
|
+
end
|
|
59
63
|
end
|
|
60
64
|
end
|
|
61
65
|
end
|
|
@@ -46,11 +46,27 @@ module Karafka
|
|
|
46
46
|
skippable_message, _marked = find_skippable_message
|
|
47
47
|
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
if mark_after_dispatch?
|
|
50
|
+
mark_dispatched_to_dlq(skippable_message)
|
|
51
|
+
else
|
|
52
|
+
coordinator.seek_offset = skippable_message.offset + 1
|
|
53
|
+
end
|
|
50
54
|
end
|
|
51
55
|
end
|
|
52
56
|
end
|
|
53
57
|
end
|
|
58
|
+
|
|
59
|
+
# @return [Boolean] should we mark given message as consumed after dispatch. For
|
|
60
|
+
# MOM strategies if user did not explicitly tell us to mark, we do not mark. Default
|
|
61
|
+
# is `nil`, which means `false` in this case. If user provided alternative value, we
|
|
62
|
+
# go with it.
|
|
63
|
+
#
|
|
64
|
+
# @note Please note, this is the opposite behavior than in case of AOM strategies.
|
|
65
|
+
def mark_after_dispatch?
|
|
66
|
+
return false if topic.dead_letter_queue.mark_after_dispatch.nil?
|
|
67
|
+
|
|
68
|
+
topic.dead_letter_queue.mark_after_dispatch
|
|
69
|
+
end
|
|
54
70
|
end
|
|
55
71
|
end
|
|
56
72
|
end
|