karafka 2.4.8 → 2.4.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|