karafka 2.4.14 → 2.4.16
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 +5 -5
- data/.ruby-version +1 -1
- data/CHANGELOG.md +14 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +19 -16
- data/config/locales/errors.yml +1 -0
- data/docker-compose.yml +1 -3
- data/lib/karafka/base_consumer.rb +63 -9
- data/lib/karafka/connection/client.rb +2 -16
- data/lib/karafka/connection/rebalance_manager.rb +24 -13
- data/lib/karafka/instrumentation/assignments_tracker.rb +7 -2
- data/lib/karafka/instrumentation/logger_listener.rb +3 -0
- data/lib/karafka/instrumentation/notifications.rb +3 -0
- data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +1 -26
- data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +2 -0
- data/lib/karafka/pro/processing/jobs/eofed_non_blocking.rb +2 -0
- data/lib/karafka/pro/processing/jobs/periodic.rb +2 -0
- data/lib/karafka/pro/processing/jobs/periodic_non_blocking.rb +2 -0
- data/lib/karafka/pro/processing/jobs/revoked_non_blocking.rb +2 -0
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +2 -2
- data/lib/karafka/pro/processing/strategies/default.rb +98 -13
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +1 -1
- data/lib/karafka/pro/processing/strategies/ftr/default.rb +2 -2
- data/lib/karafka/pro/processing/strategies/lrj/default.rb +2 -2
- data/lib/karafka/pro/processing/strategies/lrj/mom.rb +2 -2
- data/lib/karafka/pro/processing/strategies/vp/default.rb +3 -0
- data/lib/karafka/processing/executor.rb +7 -0
- data/lib/karafka/processing/jobs/base.rb +13 -0
- data/lib/karafka/processing/jobs/consume.rb +2 -0
- data/lib/karafka/processing/jobs/eofed.rb +2 -0
- data/lib/karafka/processing/jobs/idle.rb +2 -0
- data/lib/karafka/processing/jobs/revoked.rb +2 -0
- data/lib/karafka/processing/jobs/shutdown.rb +2 -0
- data/lib/karafka/processing/strategies/default.rb +22 -11
- data/lib/karafka/processing/strategies/dlq.rb +1 -1
- data/lib/karafka/processing/worker.rb +21 -18
- data/lib/karafka/routing/builder.rb +15 -1
- data/lib/karafka/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +3 -6
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8510d261a0722b764aff2c59e214bb2b87481ce606488e8f1323c4bf29ee4528
|
|
4
|
+
data.tar.gz: 4574f71706749ec5effa830b9c04a6e3072c880220a08fa33f81648f7d3c79be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37c43b9634b43e6608fa7f2ab9320d49d00b2582c211095968d3200412029e0301d17556d31be2e24d8401457fd660473cc46adc4d5835de968a2eba9c51fbef
|
|
7
|
+
data.tar.gz: 15d2e2dbd7ee99ab1c726a47a76959f1e3d877cd723f084550711fd9ba4662a30eb39af26f1354847ab906aaec9580e0cde5f7858b0cda521f5f39844195a3c3
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -27,7 +27,7 @@ jobs:
|
|
|
27
27
|
- name: Set up Ruby
|
|
28
28
|
uses: ruby/setup-ruby@v1
|
|
29
29
|
with:
|
|
30
|
-
ruby-version: 3.
|
|
30
|
+
ruby-version: 3.4
|
|
31
31
|
bundler-cache: true
|
|
32
32
|
|
|
33
33
|
- name: Install Diffend plugin
|
|
@@ -73,12 +73,12 @@ jobs:
|
|
|
73
73
|
fail-fast: false
|
|
74
74
|
matrix:
|
|
75
75
|
ruby:
|
|
76
|
-
- '3.4
|
|
76
|
+
- '3.4'
|
|
77
77
|
- '3.3'
|
|
78
78
|
- '3.2'
|
|
79
79
|
- '3.1'
|
|
80
80
|
include:
|
|
81
|
-
- ruby: '3.
|
|
81
|
+
- ruby: '3.4'
|
|
82
82
|
coverage: 'true'
|
|
83
83
|
steps:
|
|
84
84
|
- uses: actions/checkout@v4
|
|
@@ -120,7 +120,7 @@ jobs:
|
|
|
120
120
|
fail-fast: false
|
|
121
121
|
matrix:
|
|
122
122
|
ruby:
|
|
123
|
-
- '3.4
|
|
123
|
+
- '3.4'
|
|
124
124
|
- '3.3'
|
|
125
125
|
- '3.2'
|
|
126
126
|
- '3.1'
|
|
@@ -178,7 +178,7 @@ jobs:
|
|
|
178
178
|
fail-fast: false
|
|
179
179
|
matrix:
|
|
180
180
|
ruby:
|
|
181
|
-
- '3.4
|
|
181
|
+
- '3.4'
|
|
182
182
|
- '3.3'
|
|
183
183
|
- '3.2'
|
|
184
184
|
- '3.1'
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.4.1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Karafka Framework Changelog
|
|
2
2
|
|
|
3
|
+
## 2.4.16 (2024-12-27)
|
|
4
|
+
- [Enhancement] Improve post-rebalance revocation messages filtering.
|
|
5
|
+
- [Enhancement] Introduce `Consumer#wrap` for connection pooling management and other wrapped operations.
|
|
6
|
+
- [Enhancement] Guard transactional operations from marking beyond assignment ownership under some extreme edge-cases.
|
|
7
|
+
- [Enhancement] Improve VPs work with transactional producers.
|
|
8
|
+
- [Enhancement] Prevent non-transactional operations leakage into transactional managed offset management consumers.
|
|
9
|
+
- [Fix] Prevent transactions from being marked with a non-transactional default producer when automatic offset management and other advanced features are on.
|
|
10
|
+
- [Fix] Fix `kafka_format` `KeyError` that occurs when a non-hash is assigned to the kafka scope of the settings.
|
|
11
|
+
- [Fix] Non cooperative-sticky transactional offset management can refetch reclaimed partitions.
|
|
12
|
+
|
|
13
|
+
## 2.4.15 (2024-12-04)
|
|
14
|
+
- [Fix] Assignment tracker current state fetch during a rebalance loop can cause an error on multi CG setup.
|
|
15
|
+
- [Fix] Prevent double post-transaction offset dispatch to Kafka.
|
|
16
|
+
|
|
3
17
|
## 2.4.14 (2024-11-25)
|
|
4
18
|
- [Enhancement] Improve low-level critical error reporting.
|
|
5
19
|
- [Enhancement] Expand Kubernetes Liveness state reporting with critical errors detection.
|
data/Gemfile
CHANGED
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.16)
|
|
5
5
|
base64 (~> 0.2)
|
|
6
6
|
karafka-core (>= 2.4.4, < 2.5.0)
|
|
7
7
|
karafka-rdkafka (>= 0.17.2)
|
|
@@ -11,10 +11,10 @@ PATH
|
|
|
11
11
|
GEM
|
|
12
12
|
remote: https://rubygems.org/
|
|
13
13
|
specs:
|
|
14
|
-
activejob (8.0.
|
|
15
|
-
activesupport (= 8.0.
|
|
14
|
+
activejob (8.0.1)
|
|
15
|
+
activesupport (= 8.0.1)
|
|
16
16
|
globalid (>= 0.3.6)
|
|
17
|
-
activesupport (8.0.
|
|
17
|
+
activesupport (8.0.1)
|
|
18
18
|
base64
|
|
19
19
|
benchmark (>= 0.3)
|
|
20
20
|
bigdecimal
|
|
@@ -49,27 +49,28 @@ GEM
|
|
|
49
49
|
activesupport (>= 6.1)
|
|
50
50
|
i18n (1.14.6)
|
|
51
51
|
concurrent-ruby (~> 1.0)
|
|
52
|
-
karafka-core (2.4.
|
|
53
|
-
karafka-rdkafka (>= 0.
|
|
54
|
-
|
|
52
|
+
karafka-core (2.4.8)
|
|
53
|
+
karafka-rdkafka (>= 0.17.6, < 0.19.0)
|
|
54
|
+
logger (>= 1.6.0)
|
|
55
|
+
karafka-rdkafka (0.18.1)
|
|
55
56
|
ffi (~> 1.15)
|
|
56
57
|
mini_portile2 (~> 2.6)
|
|
57
58
|
rake (> 12)
|
|
58
59
|
karafka-testing (2.4.6)
|
|
59
60
|
karafka (>= 2.4.0, < 2.5.0)
|
|
60
61
|
waterdrop (>= 2.7.0)
|
|
61
|
-
karafka-web (0.10.
|
|
62
|
+
karafka-web (0.10.4)
|
|
62
63
|
erubi (~> 1.4)
|
|
63
64
|
karafka (>= 2.4.10, < 2.5.0)
|
|
64
65
|
karafka-core (>= 2.4.0, < 2.5.0)
|
|
65
66
|
roda (~> 3.68, >= 3.69)
|
|
66
67
|
tilt (~> 2.0)
|
|
67
|
-
logger (1.6.
|
|
68
|
-
mini_portile2 (2.8.
|
|
69
|
-
minitest (5.25.
|
|
68
|
+
logger (1.6.3)
|
|
69
|
+
mini_portile2 (2.8.8)
|
|
70
|
+
minitest (5.25.4)
|
|
70
71
|
ostruct (0.6.1)
|
|
71
72
|
raabro (1.4.0)
|
|
72
|
-
rack (3.1.
|
|
73
|
+
rack (3.1.8)
|
|
73
74
|
rake (13.2.1)
|
|
74
75
|
roda (3.84.0)
|
|
75
76
|
rack
|
|
@@ -86,22 +87,23 @@ GEM
|
|
|
86
87
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
87
88
|
rspec-support (~> 3.13.0)
|
|
88
89
|
rspec-support (3.13.1)
|
|
89
|
-
securerandom (0.3.
|
|
90
|
+
securerandom (0.3.2)
|
|
90
91
|
simplecov (0.22.0)
|
|
91
92
|
docile (~> 1.1)
|
|
92
93
|
simplecov-html (~> 0.11)
|
|
93
94
|
simplecov_json_formatter (~> 0.1)
|
|
94
95
|
simplecov-html (0.12.3)
|
|
95
96
|
simplecov_json_formatter (0.1.4)
|
|
97
|
+
stringio (3.1.2)
|
|
96
98
|
tilt (2.4.0)
|
|
97
99
|
tzinfo (2.0.6)
|
|
98
100
|
concurrent-ruby (~> 1.0)
|
|
99
|
-
uri (1.0.
|
|
100
|
-
waterdrop (2.8.
|
|
101
|
+
uri (1.0.2)
|
|
102
|
+
waterdrop (2.8.1)
|
|
101
103
|
karafka-core (>= 2.4.3, < 3.0.0)
|
|
102
104
|
karafka-rdkafka (>= 0.17.5)
|
|
103
105
|
zeitwerk (~> 2.3)
|
|
104
|
-
zeitwerk (2.
|
|
106
|
+
zeitwerk (2.7.1)
|
|
105
107
|
|
|
106
108
|
PLATFORMS
|
|
107
109
|
ruby
|
|
@@ -118,6 +120,7 @@ DEPENDENCIES
|
|
|
118
120
|
ostruct
|
|
119
121
|
rspec
|
|
120
122
|
simplecov
|
|
123
|
+
stringio
|
|
121
124
|
|
|
122
125
|
BUNDLED WITH
|
|
123
126
|
2.4.22
|
data/config/locales/errors.yml
CHANGED
|
@@ -95,6 +95,7 @@ en:
|
|
|
95
95
|
|
|
96
96
|
topic:
|
|
97
97
|
kafka: needs to be a hash with kafka scope settings details
|
|
98
|
+
kafka_format: needs to be a filled hash
|
|
98
99
|
missing: needs to be present
|
|
99
100
|
max_messages_format: 'needs to be an integer bigger than 0'
|
|
100
101
|
max_wait_time_format: 'needs to be an integer bigger than 0'
|
data/docker-compose.yml
CHANGED
|
@@ -6,6 +6,9 @@ module Karafka
|
|
|
6
6
|
class BaseConsumer
|
|
7
7
|
# Allow for consumer instance tagging for instrumentation
|
|
8
8
|
include ::Karafka::Core::Taggable
|
|
9
|
+
include Helpers::ConfigImporter.new(
|
|
10
|
+
monitor: %i[monitor]
|
|
11
|
+
)
|
|
9
12
|
|
|
10
13
|
extend Forwardable
|
|
11
14
|
|
|
@@ -39,7 +42,7 @@ module Karafka
|
|
|
39
42
|
def on_initialized
|
|
40
43
|
handle_initialized
|
|
41
44
|
rescue StandardError => e
|
|
42
|
-
|
|
45
|
+
monitor.instrument(
|
|
43
46
|
'error.occurred',
|
|
44
47
|
error: e,
|
|
45
48
|
caller: self,
|
|
@@ -73,6 +76,23 @@ module Karafka
|
|
|
73
76
|
handle_before_consume
|
|
74
77
|
end
|
|
75
78
|
|
|
79
|
+
# Executes the default wrapping flow
|
|
80
|
+
#
|
|
81
|
+
# @private
|
|
82
|
+
#
|
|
83
|
+
# @param action [Symbol]
|
|
84
|
+
# @param block [Proc]
|
|
85
|
+
def on_wrap(action, &block)
|
|
86
|
+
handle_wrap(action, &block)
|
|
87
|
+
rescue StandardError => e
|
|
88
|
+
monitor.instrument(
|
|
89
|
+
'error.occurred',
|
|
90
|
+
error: e,
|
|
91
|
+
caller: self,
|
|
92
|
+
type: 'consumer.wrap.error'
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
76
96
|
# Executes the default consumer flow.
|
|
77
97
|
#
|
|
78
98
|
# @private
|
|
@@ -85,7 +105,7 @@ module Karafka
|
|
|
85
105
|
def on_consume
|
|
86
106
|
handle_consume
|
|
87
107
|
rescue StandardError => e
|
|
88
|
-
|
|
108
|
+
monitor.instrument(
|
|
89
109
|
'error.occurred',
|
|
90
110
|
error: e,
|
|
91
111
|
caller: self,
|
|
@@ -105,7 +125,7 @@ module Karafka
|
|
|
105
125
|
def on_after_consume
|
|
106
126
|
handle_after_consume
|
|
107
127
|
rescue StandardError => e
|
|
108
|
-
|
|
128
|
+
monitor.instrument(
|
|
109
129
|
'error.occurred',
|
|
110
130
|
error: e,
|
|
111
131
|
caller: self,
|
|
@@ -125,7 +145,7 @@ module Karafka
|
|
|
125
145
|
def on_eofed
|
|
126
146
|
handle_eofed
|
|
127
147
|
rescue StandardError => e
|
|
128
|
-
|
|
148
|
+
monitor.instrument(
|
|
129
149
|
'error.occurred',
|
|
130
150
|
error: e,
|
|
131
151
|
caller: self,
|
|
@@ -161,7 +181,7 @@ module Karafka
|
|
|
161
181
|
def on_revoked
|
|
162
182
|
handle_revoked
|
|
163
183
|
rescue StandardError => e
|
|
164
|
-
|
|
184
|
+
monitor.instrument(
|
|
165
185
|
'error.occurred',
|
|
166
186
|
error: e,
|
|
167
187
|
caller: self,
|
|
@@ -182,7 +202,7 @@ module Karafka
|
|
|
182
202
|
def on_shutdown
|
|
183
203
|
handle_shutdown
|
|
184
204
|
rescue StandardError => e
|
|
185
|
-
|
|
205
|
+
monitor.instrument(
|
|
186
206
|
'error.occurred',
|
|
187
207
|
error: e,
|
|
188
208
|
caller: self,
|
|
@@ -209,6 +229,40 @@ module Karafka
|
|
|
209
229
|
raise NotImplementedError, 'Implement this in a subclass'
|
|
210
230
|
end
|
|
211
231
|
|
|
232
|
+
# This method can be redefined to build a wrapping API around user code + karafka flow control
|
|
233
|
+
# code starting from the user code (operations prior to that are not part of this).
|
|
234
|
+
# The wrapping relates to a single job flow.
|
|
235
|
+
#
|
|
236
|
+
# Karafka framework may require user configured "state" like for example a selected
|
|
237
|
+
# transactional producer that should be used not only by the user but also by the framework.
|
|
238
|
+
# By using this API user can checkout a producer and return it to the pool.
|
|
239
|
+
#
|
|
240
|
+
# @param _action [Symbol] what action are we wrapping. Useful if we want for example to only
|
|
241
|
+
# wrap the `:consume` action.
|
|
242
|
+
# @yield Runs the execution block
|
|
243
|
+
#
|
|
244
|
+
# @note User related errors should not leak to this level of execution. This should not be used
|
|
245
|
+
# for anything consumption related but only for setting up state that that Karafka code
|
|
246
|
+
# may need outside of user code.
|
|
247
|
+
#
|
|
248
|
+
# @example Redefine to use a producer from a pool for consume
|
|
249
|
+
# def wrap(action)
|
|
250
|
+
# # Do not checkout producer for any other actions
|
|
251
|
+
# return yield unless action == :consume
|
|
252
|
+
#
|
|
253
|
+
# default_producer = self.producer
|
|
254
|
+
#
|
|
255
|
+
# $producers.with do |producer|
|
|
256
|
+
# self.producer = producer
|
|
257
|
+
# yield
|
|
258
|
+
# end
|
|
259
|
+
#
|
|
260
|
+
# self.producer = default_producer
|
|
261
|
+
# end
|
|
262
|
+
def wrap(_action)
|
|
263
|
+
yield
|
|
264
|
+
end
|
|
265
|
+
|
|
212
266
|
# Method that will be executed when a given topic partition reaches eof without any new
|
|
213
267
|
# incoming messages alongside
|
|
214
268
|
def eofed; end
|
|
@@ -255,7 +309,7 @@ module Karafka
|
|
|
255
309
|
# Indicate, that user took a manual action of pausing
|
|
256
310
|
coordinator.manual_pause if manual_pause
|
|
257
311
|
|
|
258
|
-
|
|
312
|
+
monitor.instrument(
|
|
259
313
|
'consumer.consuming.pause',
|
|
260
314
|
caller: self,
|
|
261
315
|
manual: manual_pause,
|
|
@@ -299,7 +353,7 @@ module Karafka
|
|
|
299
353
|
offset
|
|
300
354
|
)
|
|
301
355
|
|
|
302
|
-
|
|
356
|
+
monitor.instrument(
|
|
303
357
|
'consumer.consuming.seek',
|
|
304
358
|
caller: self,
|
|
305
359
|
topic: topic.name,
|
|
@@ -347,7 +401,7 @@ module Karafka
|
|
|
347
401
|
|
|
348
402
|
# Instrumentation needs to run **after** `#pause` invocation because we rely on the states
|
|
349
403
|
# set by `#pause`
|
|
350
|
-
|
|
404
|
+
monitor.instrument(
|
|
351
405
|
'consumer.consuming.retry',
|
|
352
406
|
caller: self,
|
|
353
407
|
topic: topic.name,
|
|
@@ -66,7 +66,7 @@ module Karafka
|
|
|
66
66
|
@subscription_group = subscription_group
|
|
67
67
|
@buffer = RawMessagesBuffer.new
|
|
68
68
|
@tick_interval = ::Karafka::App.config.internal.tick_interval
|
|
69
|
-
@rebalance_manager = RebalanceManager.new(@subscription_group.id)
|
|
69
|
+
@rebalance_manager = RebalanceManager.new(@subscription_group.id, @buffer)
|
|
70
70
|
@rebalance_callback = Instrumentation::Callbacks::Rebalance.new(@subscription_group)
|
|
71
71
|
|
|
72
72
|
@interval_runner = Helpers::IntervalRunner.new do
|
|
@@ -141,7 +141,7 @@ module Karafka
|
|
|
141
141
|
# Since rebalances do not occur often, we can run events polling as well without
|
|
142
142
|
# any throttling
|
|
143
143
|
events_poll
|
|
144
|
-
|
|
144
|
+
|
|
145
145
|
break
|
|
146
146
|
end
|
|
147
147
|
|
|
@@ -717,20 +717,6 @@ module Karafka
|
|
|
717
717
|
consumer
|
|
718
718
|
end
|
|
719
719
|
|
|
720
|
-
# We may have a case where in the middle of data polling, we've lost a partition.
|
|
721
|
-
# In a case like this we should remove all the pre-buffered messages from list partitions as
|
|
722
|
-
# we are no longer responsible in a given process for processing those messages and they
|
|
723
|
-
# should have been picked up by a different process.
|
|
724
|
-
def remove_revoked_and_duplicated_messages
|
|
725
|
-
@rebalance_manager.lost_partitions.each do |topic, partitions|
|
|
726
|
-
partitions.each do |partition|
|
|
727
|
-
@buffer.delete(topic, partition)
|
|
728
|
-
end
|
|
729
|
-
end
|
|
730
|
-
|
|
731
|
-
@buffer.uniq!
|
|
732
|
-
end
|
|
733
|
-
|
|
734
720
|
# @return [Rdkafka::Consumer] librdkafka consumer instance
|
|
735
721
|
def kafka
|
|
736
722
|
return @kafka if @kafka
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Karafka
|
|
4
4
|
module Connection
|
|
5
|
-
# Manager for tracking changes in the partitions assignment after the assignment is done
|
|
5
|
+
# Manager for tracking changes in the partitions assignment after the assignment is done and
|
|
6
|
+
# for ensuring, that proper buffer related operations that may be impacted by the rebalance
|
|
7
|
+
# state are applied.
|
|
6
8
|
#
|
|
7
9
|
# We need tracking of those to clean up consumers that will no longer process given partitions
|
|
8
10
|
# as they were taken away.
|
|
@@ -30,13 +32,15 @@ module Karafka
|
|
|
30
32
|
private_constant :EMPTY_ARRAY
|
|
31
33
|
|
|
32
34
|
# @param subscription_group_id [String] subscription group id
|
|
35
|
+
# @param buffer [Karafka::Connection::RawMessagesBuffer]
|
|
33
36
|
# @return [RebalanceManager]
|
|
34
|
-
def initialize(subscription_group_id)
|
|
37
|
+
def initialize(subscription_group_id, buffer)
|
|
35
38
|
@assigned_partitions = {}
|
|
36
39
|
@revoked_partitions = {}
|
|
37
40
|
@changed = false
|
|
38
41
|
@active = false
|
|
39
42
|
@subscription_group_id = subscription_group_id
|
|
43
|
+
@buffer = buffer
|
|
40
44
|
|
|
41
45
|
# Connects itself to the instrumentation pipeline so rebalances can be tracked
|
|
42
46
|
::Karafka.monitor.subscribe(self)
|
|
@@ -64,17 +68,6 @@ module Karafka
|
|
|
64
68
|
@active
|
|
65
69
|
end
|
|
66
70
|
|
|
67
|
-
# We consider as lost only partitions that were taken away and not re-assigned back to us
|
|
68
|
-
def lost_partitions
|
|
69
|
-
lost_partitions = {}
|
|
70
|
-
|
|
71
|
-
revoked_partitions.each do |topic, partitions|
|
|
72
|
-
lost_partitions[topic] = partitions - assigned_partitions.fetch(topic, EMPTY_ARRAY)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
lost_partitions
|
|
76
|
-
end
|
|
77
|
-
|
|
78
71
|
# Callback that kicks in inside of rdkafka, when new partitions were assigned.
|
|
79
72
|
#
|
|
80
73
|
# @private
|
|
@@ -99,6 +92,24 @@ module Karafka
|
|
|
99
92
|
@active = true
|
|
100
93
|
@revoked_partitions = event[:tpl].to_h.transform_values { |part| part.map(&:partition) }
|
|
101
94
|
@changed = true
|
|
95
|
+
|
|
96
|
+
remove_revoked_and_duplicated_messages
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
# We may have a case where in the middle of data polling, we've lost a partition.
|
|
102
|
+
# In a case like this we should remove all the pre-buffered messages from list partitions as
|
|
103
|
+
# we are no longer responsible in a given process for processing those messages and they
|
|
104
|
+
# should have been picked up by a different process.
|
|
105
|
+
def remove_revoked_and_duplicated_messages
|
|
106
|
+
@revoked_partitions.each do |topic, partitions|
|
|
107
|
+
partitions.each do |partition|
|
|
108
|
+
@buffer.delete(topic, partition)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
@buffer.uniq!
|
|
102
113
|
end
|
|
103
114
|
end
|
|
104
115
|
end
|
|
@@ -31,8 +31,13 @@ module Karafka
|
|
|
31
31
|
def current
|
|
32
32
|
assignments = {}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
# Since the `@assignments` state can change during a rebalance, if we would iterate over
|
|
35
|
+
# it exactly during state change, we would end up with the following error:
|
|
36
|
+
# RuntimeError: can't add a new key into hash during iteration
|
|
37
|
+
@mutex.synchronize do
|
|
38
|
+
@assignments.each do |topic, partitions|
|
|
39
|
+
assignments[topic] = partitions.dup.freeze
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
|
|
38
43
|
assignments.freeze
|
|
@@ -293,6 +293,9 @@ module Karafka
|
|
|
293
293
|
when 'consumer.initialized.error'
|
|
294
294
|
error "Consumer initialized error: #{error}"
|
|
295
295
|
error details
|
|
296
|
+
when 'consumer.wrap.error'
|
|
297
|
+
error "Consumer wrap failed due to an error: #{error}"
|
|
298
|
+
error details
|
|
296
299
|
when 'consumer.consume.error'
|
|
297
300
|
error "Consumer consuming error: #{error}"
|
|
298
301
|
error details
|
|
@@ -56,32 +56,7 @@ module Karafka
|
|
|
56
56
|
consumer = job.executor.topic.consumer
|
|
57
57
|
topic = job.executor.topic.name
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
when 'Periodic'
|
|
61
|
-
'tick'
|
|
62
|
-
when 'PeriodicNonBlocking'
|
|
63
|
-
'tick'
|
|
64
|
-
when 'Shutdown'
|
|
65
|
-
'shutdown'
|
|
66
|
-
when 'Revoked'
|
|
67
|
-
'revoked'
|
|
68
|
-
when 'RevokedNonBlocking'
|
|
69
|
-
'revoked'
|
|
70
|
-
when 'Idle'
|
|
71
|
-
'idle'
|
|
72
|
-
when 'Eofed'
|
|
73
|
-
'eofed'
|
|
74
|
-
when 'EofedNonBlocking'
|
|
75
|
-
'eofed'
|
|
76
|
-
when 'ConsumeNonBlocking'
|
|
77
|
-
'consume'
|
|
78
|
-
when 'Consume'
|
|
79
|
-
'consume'
|
|
80
|
-
else
|
|
81
|
-
raise Errors::UnsupportedCaseError, job_type
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
current_span.resource = "#{consumer}##{action}"
|
|
59
|
+
current_span.resource = "#{consumer}##{job.class.action}"
|
|
85
60
|
info "[#{job.id}] #{job_type} job for #{consumer} on #{topic} started"
|
|
86
61
|
|
|
87
62
|
pop_tags
|
|
@@ -26,6 +26,8 @@ module Karafka
|
|
|
26
26
|
# @note It needs to be working with a proper consumer that will handle the partition
|
|
27
27
|
# management. This layer of the framework knows nothing about Kafka messages consumption.
|
|
28
28
|
class ConsumeNonBlocking < ::Karafka::Processing::Jobs::Consume
|
|
29
|
+
self.action = :consume
|
|
30
|
+
|
|
29
31
|
# Makes this job non-blocking from the start
|
|
30
32
|
# @param args [Array] any arguments accepted by `::Karafka::Processing::Jobs::Consume`
|
|
31
33
|
def initialize(*args)
|
|
@@ -20,6 +20,8 @@ module Karafka
|
|
|
20
20
|
# to run this job for extended period of time. Under such scenarios, if we would not use
|
|
21
21
|
# a non-blocking one, we would reach max.poll.interval.ms.
|
|
22
22
|
class EofedNonBlocking < ::Karafka::Processing::Jobs::Eofed
|
|
23
|
+
self.action = :eofed
|
|
24
|
+
|
|
23
25
|
# @param args [Array] any arguments accepted by `::Karafka::Processing::Jobs::Eofed`
|
|
24
26
|
def initialize(*args)
|
|
25
27
|
super
|
|
@@ -18,6 +18,8 @@ module Karafka
|
|
|
18
18
|
# Job that represents a "ticking" work. Work that we run periodically for the Periodics
|
|
19
19
|
# enabled topics.
|
|
20
20
|
class Periodic < ::Karafka::Processing::Jobs::Base
|
|
21
|
+
self.action = :tick
|
|
22
|
+
|
|
21
23
|
# @param executor [Karafka::Pro::Processing::Executor] pro executor that is suppose to
|
|
22
24
|
# run a given job
|
|
23
25
|
def initialize(executor)
|
|
@@ -20,6 +20,8 @@ module Karafka
|
|
|
20
20
|
# to run this job for extended period of time. Under such scenarios, if we would not use
|
|
21
21
|
# a non-blocking one, we would reach max.poll.interval.ms.
|
|
22
22
|
class PeriodicNonBlocking < Periodic
|
|
23
|
+
self.action = :tick
|
|
24
|
+
|
|
23
25
|
# @param args [Array] any arguments accepted by `::Karafka::Processing::Jobs::Periodic`
|
|
24
26
|
def initialize(*args)
|
|
25
27
|
super
|
|
@@ -24,6 +24,8 @@ module Karafka
|
|
|
24
24
|
# in scenarios where there are more jobs than threads, without this being async we
|
|
25
25
|
# would potentially stop polling
|
|
26
26
|
class RevokedNonBlocking < ::Karafka::Processing::Jobs::Revoked
|
|
27
|
+
self.action = :revoked
|
|
28
|
+
|
|
27
29
|
# Makes this job non-blocking from the start
|
|
28
30
|
# @param args [Array] any arguments accepted by `::Karafka::Processing::Jobs::Revoked`
|
|
29
31
|
def initialize(*args)
|
|
@@ -69,8 +69,8 @@ module Karafka
|
|
|
69
69
|
coordinator.revoke
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
monitor.instrument('consumer.revoke', caller: self)
|
|
73
|
+
monitor.instrument('consumer.revoked', caller: self) do
|
|
74
74
|
revoked
|
|
75
75
|
end
|
|
76
76
|
ensure
|
|
@@ -54,8 +54,11 @@ module Karafka
|
|
|
54
54
|
# already processed but rather at the next one. This applies to both sync and async
|
|
55
55
|
# versions of this method.
|
|
56
56
|
def mark_as_consumed(message, offset_metadata = @_current_offset_metadata)
|
|
57
|
+
# If we are inside a transaction than we can just mark as consumed within it
|
|
57
58
|
if @_in_transaction
|
|
58
59
|
mark_in_transaction(message, offset_metadata, true)
|
|
60
|
+
elsif @_in_transaction_marked
|
|
61
|
+
mark_in_memory(message)
|
|
59
62
|
else
|
|
60
63
|
# seek offset can be nil only in case `#seek` was invoked with offset reset request
|
|
61
64
|
# In case like this we ignore marking
|
|
@@ -63,7 +66,16 @@ module Karafka
|
|
|
63
66
|
# Ignore earlier offsets than the one we already committed
|
|
64
67
|
return true if coordinator.seek_offset > message.offset
|
|
65
68
|
return false if revoked?
|
|
66
|
-
|
|
69
|
+
|
|
70
|
+
# If we are not inside a transaction but this is a transactional topic, we mark with
|
|
71
|
+
# artificially created transaction
|
|
72
|
+
stored = if producer.transactional?
|
|
73
|
+
mark_with_transaction(message, offset_metadata, true)
|
|
74
|
+
else
|
|
75
|
+
client.mark_as_consumed(message, offset_metadata)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
return revoked? unless stored
|
|
67
79
|
|
|
68
80
|
coordinator.seek_offset = message.offset + 1
|
|
69
81
|
end
|
|
@@ -82,6 +94,8 @@ module Karafka
|
|
|
82
94
|
def mark_as_consumed!(message, offset_metadata = @_current_offset_metadata)
|
|
83
95
|
if @_in_transaction
|
|
84
96
|
mark_in_transaction(message, offset_metadata, false)
|
|
97
|
+
elsif @_in_transaction_marked
|
|
98
|
+
mark_in_memory(message)
|
|
85
99
|
else
|
|
86
100
|
# seek offset can be nil only in case `#seek` was invoked with offset reset request
|
|
87
101
|
# In case like this we ignore marking
|
|
@@ -90,7 +104,15 @@ module Karafka
|
|
|
90
104
|
return true if coordinator.seek_offset > message.offset
|
|
91
105
|
return false if revoked?
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
# If we are not inside a transaction but this is a transactional topic, we mark with
|
|
108
|
+
# artificially created transaction
|
|
109
|
+
stored = if producer.transactional?
|
|
110
|
+
mark_with_transaction(message, offset_metadata, false)
|
|
111
|
+
else
|
|
112
|
+
client.mark_as_consumed!(message, offset_metadata)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
return revoked? unless stored
|
|
94
116
|
|
|
95
117
|
coordinator.seek_offset = message.offset + 1
|
|
96
118
|
end
|
|
@@ -112,7 +134,7 @@ module Karafka
|
|
|
112
134
|
# managing multiple producers. If not provided, default producer taken from `#producer`
|
|
113
135
|
# will be used.
|
|
114
136
|
#
|
|
115
|
-
# @
|
|
137
|
+
# @yield code that we want to run in a transaction
|
|
116
138
|
#
|
|
117
139
|
# @note Please note, that if you provide the producer, it will reassign the producer of
|
|
118
140
|
# the consumer for the transaction time. This means, that in case you would even
|
|
@@ -120,7 +142,7 @@ module Karafka
|
|
|
120
142
|
# reassigned producer and not the initially used/assigned producer. It is done that
|
|
121
143
|
# way, so the message producing aliases operate from within transactions and since the
|
|
122
144
|
# producer in transaction is locked, it will prevent other threads from using it.
|
|
123
|
-
def transaction(active_producer = producer
|
|
145
|
+
def transaction(active_producer = producer)
|
|
124
146
|
default_producer = producer
|
|
125
147
|
self.producer = active_producer
|
|
126
148
|
|
|
@@ -132,8 +154,16 @@ module Karafka
|
|
|
132
154
|
transaction_started = true
|
|
133
155
|
@_transaction_marked = []
|
|
134
156
|
@_in_transaction = true
|
|
157
|
+
@_in_transaction_marked = false
|
|
135
158
|
|
|
136
|
-
producer.transaction
|
|
159
|
+
producer.transaction do
|
|
160
|
+
yield
|
|
161
|
+
|
|
162
|
+
# Ensure this transaction is rolled back if we have lost the ownership of this
|
|
163
|
+
# transaction. We do it only for transactions that contain offset management as for
|
|
164
|
+
# producer only, this is not relevant.
|
|
165
|
+
raise Errors::AssignmentLostError if @_in_transaction_marked && revoked?
|
|
166
|
+
end
|
|
137
167
|
|
|
138
168
|
@_in_transaction = false
|
|
139
169
|
|
|
@@ -143,6 +173,11 @@ module Karafka
|
|
|
143
173
|
# @note We never need to use the blocking `#mark_as_consumed!` here because the offset
|
|
144
174
|
# anyhow was already stored during the transaction
|
|
145
175
|
#
|
|
176
|
+
# @note Since the offset could have been already stored in Kafka (could have because
|
|
177
|
+
# you can have transactions without marking), we use the `@_in_transaction_marked`
|
|
178
|
+
# state to decide if we need to dispatch the offset via client at all
|
|
179
|
+
# (if post transaction, then we do not have to)
|
|
180
|
+
#
|
|
146
181
|
# @note In theory we could only keep reference to the most recent marking and reject
|
|
147
182
|
# others. We however do not do it for two reasons:
|
|
148
183
|
# - User may have non standard flow relying on some alternative order and we want to
|
|
@@ -152,12 +187,15 @@ module Karafka
|
|
|
152
187
|
@_transaction_marked.each do |marking|
|
|
153
188
|
marking.pop ? mark_as_consumed(*marking) : mark_as_consumed!(*marking)
|
|
154
189
|
end
|
|
190
|
+
|
|
191
|
+
true
|
|
155
192
|
ensure
|
|
156
193
|
self.producer = default_producer
|
|
157
194
|
|
|
158
195
|
if transaction_started
|
|
159
196
|
@_transaction_marked.clear
|
|
160
197
|
@_in_transaction = false
|
|
198
|
+
@_in_transaction_marked = false
|
|
161
199
|
end
|
|
162
200
|
end
|
|
163
201
|
|
|
@@ -178,13 +216,60 @@ module Karafka
|
|
|
178
216
|
offset_metadata
|
|
179
217
|
)
|
|
180
218
|
|
|
219
|
+
@_in_transaction_marked = true
|
|
181
220
|
@_transaction_marked ||= []
|
|
182
221
|
@_transaction_marked << [message, offset_metadata, async]
|
|
183
222
|
end
|
|
184
223
|
|
|
224
|
+
# @private
|
|
225
|
+
# @param message [Messages::Message] message we want to commit inside of a transaction
|
|
226
|
+
# @param offset_metadata [String, nil] offset metadata or nil if none
|
|
227
|
+
# @param async [Boolean] should we mark in async or sync way (applicable only to post
|
|
228
|
+
# transaction state synchronization usage as within transaction it is always sync)
|
|
229
|
+
# @return [Boolean] false if marking failed otherwise true
|
|
230
|
+
def mark_with_transaction(message, offset_metadata, async)
|
|
231
|
+
# This flag is used by VPs to differentiate between user initiated transactions and
|
|
232
|
+
# post-execution system transactions.
|
|
233
|
+
@_transaction_internal = true
|
|
234
|
+
|
|
235
|
+
transaction do
|
|
236
|
+
mark_in_transaction(message, offset_metadata, async)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
true
|
|
240
|
+
# We handle both cases here because this is a private API for internal usage and we want
|
|
241
|
+
# the post-user code execution marking with transactional producer to result in a
|
|
242
|
+
# boolean state of marking for further framework flow. This is a normalization to make it
|
|
243
|
+
# behave the same way as it would behave with a non-transactional one
|
|
244
|
+
rescue ::Rdkafka::RdkafkaError, Errors::AssignmentLostError
|
|
245
|
+
false
|
|
246
|
+
ensure
|
|
247
|
+
@_transaction_internal = false
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Marks the current state only in memory as the offset marking has already happened
|
|
251
|
+
# using the producer transaction
|
|
252
|
+
# @param message [Messages::Message] last successfully processed message.
|
|
253
|
+
# @return [Boolean] true if all good, false if we lost assignment and no point in marking
|
|
254
|
+
def mark_in_memory(message)
|
|
255
|
+
# seek offset can be nil only in case `#seek` was invoked with offset reset request
|
|
256
|
+
# In case like this we ignore marking
|
|
257
|
+
return true if coordinator.seek_offset.nil?
|
|
258
|
+
# Ignore earlier offsets than the one we already committed
|
|
259
|
+
return true if coordinator.seek_offset > message.offset
|
|
260
|
+
return false if revoked?
|
|
261
|
+
|
|
262
|
+
# If we have already marked this successfully in a transaction that was running
|
|
263
|
+
# we should not mark it again with the client offset delegation but instead we should
|
|
264
|
+
# just align the in-memory state
|
|
265
|
+
coordinator.seek_offset = message.offset + 1
|
|
266
|
+
|
|
267
|
+
true
|
|
268
|
+
end
|
|
269
|
+
|
|
185
270
|
# No actions needed for the standard flow here
|
|
186
271
|
def handle_before_schedule_consume
|
|
187
|
-
|
|
272
|
+
monitor.instrument('consumer.before_schedule_consume', caller: self)
|
|
188
273
|
|
|
189
274
|
nil
|
|
190
275
|
end
|
|
@@ -203,8 +288,8 @@ module Karafka
|
|
|
203
288
|
# This can happen primarily when an LRJ job gets to the internal worker queue and
|
|
204
289
|
# this partition is revoked prior processing.
|
|
205
290
|
unless revoked?
|
|
206
|
-
|
|
207
|
-
|
|
291
|
+
monitor.instrument('consumer.consume', caller: self)
|
|
292
|
+
monitor.instrument('consumer.consumed', caller: self) do
|
|
208
293
|
consume
|
|
209
294
|
end
|
|
210
295
|
end
|
|
@@ -250,8 +335,8 @@ module Karafka
|
|
|
250
335
|
coordinator.revoke
|
|
251
336
|
end
|
|
252
337
|
|
|
253
|
-
|
|
254
|
-
|
|
338
|
+
monitor.instrument('consumer.revoke', caller: self)
|
|
339
|
+
monitor.instrument('consumer.revoked', caller: self) do
|
|
255
340
|
revoked
|
|
256
341
|
end
|
|
257
342
|
ensure
|
|
@@ -260,15 +345,15 @@ module Karafka
|
|
|
260
345
|
|
|
261
346
|
# No action needed for the tick standard flow
|
|
262
347
|
def handle_before_schedule_tick
|
|
263
|
-
|
|
348
|
+
monitor.instrument('consumer.before_schedule_tick', caller: self)
|
|
264
349
|
|
|
265
350
|
nil
|
|
266
351
|
end
|
|
267
352
|
|
|
268
353
|
# Runs the consumer `#tick` method with reporting
|
|
269
354
|
def handle_tick
|
|
270
|
-
|
|
271
|
-
|
|
355
|
+
monitor.instrument('consumer.tick', caller: self)
|
|
356
|
+
monitor.instrument('consumer.ticked', caller: self) do
|
|
272
357
|
tick
|
|
273
358
|
end
|
|
274
359
|
ensure
|
|
@@ -89,7 +89,7 @@ module Karafka
|
|
|
89
89
|
|
|
90
90
|
throttle_message = filter.cursor
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
monitor.instrument(
|
|
93
93
|
'filtering.seek',
|
|
94
94
|
caller: self,
|
|
95
95
|
message: throttle_message
|
|
@@ -104,7 +104,7 @@ module Karafka
|
|
|
104
104
|
|
|
105
105
|
throttle_message = filter.cursor
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
monitor.instrument(
|
|
108
108
|
'filtering.throttled',
|
|
109
109
|
caller: self,
|
|
110
110
|
message: throttle_message,
|
|
@@ -72,8 +72,8 @@ module Karafka
|
|
|
72
72
|
coordinator.revoke
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
monitor.instrument('consumer.revoke', caller: self)
|
|
76
|
+
monitor.instrument('consumer.revoked', caller: self) do
|
|
77
77
|
revoked
|
|
78
78
|
end
|
|
79
79
|
ensure
|
|
@@ -64,8 +64,8 @@ module Karafka
|
|
|
64
64
|
coordinator.revoke
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
monitor.instrument('consumer.revoke', caller: self)
|
|
68
|
+
monitor.instrument('consumer.revoked', caller: self) do
|
|
69
69
|
revoked
|
|
70
70
|
end
|
|
71
71
|
ensure
|
|
@@ -101,6 +101,9 @@ module Karafka
|
|
|
101
101
|
raise Errors::AssignmentLostError if revoked?
|
|
102
102
|
|
|
103
103
|
return super if collapsed?
|
|
104
|
+
# If this is user post-execution transaction (one initiated by the system) we should
|
|
105
|
+
# delegate to the original implementation that will store the offset via the producer
|
|
106
|
+
return super if @_transaction_internal
|
|
104
107
|
|
|
105
108
|
@_transaction_marked << [message, offset_metadata, async]
|
|
106
109
|
end
|
|
@@ -81,6 +81,13 @@ module Karafka
|
|
|
81
81
|
consumer.on_before_consume
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
# Runs the wrap/around execution context appropriate for a given action
|
|
85
|
+
# @param action [Symbol] action execution wrapped with our block
|
|
86
|
+
# @param block [Proc] execution context
|
|
87
|
+
def wrap(action, &block)
|
|
88
|
+
consumer.on_wrap(action, &block)
|
|
89
|
+
end
|
|
90
|
+
|
|
84
91
|
# Runs consumer data processing against given batch and handles failures and errors.
|
|
85
92
|
def consume
|
|
86
93
|
# We run the consumer client logic...
|
|
@@ -15,6 +15,11 @@ module Karafka
|
|
|
15
15
|
|
|
16
16
|
attr_reader :executor
|
|
17
17
|
|
|
18
|
+
class << self
|
|
19
|
+
# @return [Symbol] Job matching appropriate action
|
|
20
|
+
attr_accessor :action
|
|
21
|
+
end
|
|
22
|
+
|
|
18
23
|
# Creates a new job instance
|
|
19
24
|
def initialize
|
|
20
25
|
# All jobs are blocking by default and they can release the lock when blocking operations
|
|
@@ -23,6 +28,14 @@ module Karafka
|
|
|
23
28
|
@status = :pending
|
|
24
29
|
end
|
|
25
30
|
|
|
31
|
+
# Runs the wrap/around job hook within which the rest of the flow happens
|
|
32
|
+
# @param block [Proc] whole user related processing flow
|
|
33
|
+
# @note We inject the action name so user can decide whether to run custom logic on a
|
|
34
|
+
# given action or not.
|
|
35
|
+
def wrap(&block)
|
|
36
|
+
executor.wrap(self.class.action, &block)
|
|
37
|
+
end
|
|
38
|
+
|
|
26
39
|
# When redefined can run any code prior to the job being scheduled
|
|
27
40
|
# @note This will run in the listener thread and not in the worker
|
|
28
41
|
def before_schedule
|
|
@@ -9,6 +9,8 @@ module Karafka
|
|
|
9
9
|
# @return [Array<Rdkafka::Consumer::Message>] array with messages
|
|
10
10
|
attr_reader :messages
|
|
11
11
|
|
|
12
|
+
self.action = :consume
|
|
13
|
+
|
|
12
14
|
# @param executor [Karafka::Processing::Executor] executor that is suppose to run a given
|
|
13
15
|
# job
|
|
14
16
|
# @param messages [Karafka::Messages::Messages] karafka messages batch
|
|
@@ -5,6 +5,8 @@ module Karafka
|
|
|
5
5
|
module Jobs
|
|
6
6
|
# Job that runs the eofed operation when we receive eof without messages alongside.
|
|
7
7
|
class Eofed < Base
|
|
8
|
+
self.action = :eofed
|
|
9
|
+
|
|
8
10
|
# @param executor [Karafka::Processing::Executor] executor that is suppose to run the job
|
|
9
11
|
# @return [Eofed]
|
|
10
12
|
def initialize(executor)
|
|
@@ -6,6 +6,8 @@ module Karafka
|
|
|
6
6
|
# Type of job that we may use to run some extra handling that happens without the user
|
|
7
7
|
# related lifecycle event like consumption, revocation, etc.
|
|
8
8
|
class Idle < Base
|
|
9
|
+
self.action = :idle
|
|
10
|
+
|
|
9
11
|
# @param executor [Karafka::Processing::Executor] executor that is suppose to run a given
|
|
10
12
|
# job on an active consumer
|
|
11
13
|
# @return [Shutdown]
|
|
@@ -5,6 +5,8 @@ module Karafka
|
|
|
5
5
|
module Jobs
|
|
6
6
|
# Job that runs the revoked operation when we loose a partition on a consumer that lost it.
|
|
7
7
|
class Revoked < Base
|
|
8
|
+
self.action = :revoked
|
|
9
|
+
|
|
8
10
|
# @param executor [Karafka::Processing::Executor] executor that is suppose to run the job
|
|
9
11
|
# @return [Revoked]
|
|
10
12
|
def initialize(executor)
|
|
@@ -5,6 +5,8 @@ module Karafka
|
|
|
5
5
|
module Jobs
|
|
6
6
|
# Job that runs on each active consumer upon process shutdown (one job per consumer).
|
|
7
7
|
class Shutdown < Base
|
|
8
|
+
self.action = :shutdown
|
|
9
|
+
|
|
8
10
|
# @param executor [Karafka::Processing::Executor] executor that is suppose to run a given
|
|
9
11
|
# job on an active consumer
|
|
10
12
|
# @return [Shutdown]
|
|
@@ -24,7 +24,7 @@ module Karafka
|
|
|
24
24
|
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
25
25
|
# No actions needed for the standard flow here
|
|
26
26
|
def handle_before_schedule_#{action}
|
|
27
|
-
|
|
27
|
+
monitor.instrument('consumer.before_schedule_#{action}', caller: self)
|
|
28
28
|
|
|
29
29
|
nil
|
|
30
30
|
end
|
|
@@ -35,8 +35,8 @@ module Karafka
|
|
|
35
35
|
# @note It runs in the listener loop. Should **not** be used for anything heavy or
|
|
36
36
|
# with any potential errors. Mostly for initialization of states, etc.
|
|
37
37
|
def handle_initialized
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
monitor.instrument('consumer.initialize', caller: self)
|
|
39
|
+
monitor.instrument('consumer.initialized', caller: self) do
|
|
40
40
|
initialized
|
|
41
41
|
end
|
|
42
42
|
end
|
|
@@ -115,10 +115,21 @@ module Karafka
|
|
|
115
115
|
coordinator.pause_tracker.increment
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
+
# Runs the wrapping to execute appropriate action wrapped with the wrapper method code
|
|
119
|
+
#
|
|
120
|
+
# @param action [Symbol]
|
|
121
|
+
# @param block [Proc]
|
|
122
|
+
def handle_wrap(action, &block)
|
|
123
|
+
monitor.instrument('consumer.wrap', caller: self)
|
|
124
|
+
monitor.instrument('consumer.wrapped', caller: self) do
|
|
125
|
+
wrap(action, &block)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
118
129
|
# Run the user consumption code
|
|
119
130
|
def handle_consume
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
monitor.instrument('consumer.consume', caller: self)
|
|
132
|
+
monitor.instrument('consumer.consumed', caller: self) do
|
|
122
133
|
consume
|
|
123
134
|
end
|
|
124
135
|
|
|
@@ -164,8 +175,8 @@ module Karafka
|
|
|
164
175
|
|
|
165
176
|
# Runs the consumer `#eofed` method with reporting
|
|
166
177
|
def handle_eofed
|
|
167
|
-
|
|
168
|
-
|
|
178
|
+
monitor.instrument('consumer.eof', caller: self)
|
|
179
|
+
monitor.instrument('consumer.eofed', caller: self) do
|
|
169
180
|
eofed
|
|
170
181
|
end
|
|
171
182
|
ensure
|
|
@@ -180,8 +191,8 @@ module Karafka
|
|
|
180
191
|
|
|
181
192
|
coordinator.revoke
|
|
182
193
|
|
|
183
|
-
|
|
184
|
-
|
|
194
|
+
monitor.instrument('consumer.revoke', caller: self)
|
|
195
|
+
monitor.instrument('consumer.revoked', caller: self) do
|
|
185
196
|
revoked
|
|
186
197
|
end
|
|
187
198
|
ensure
|
|
@@ -190,8 +201,8 @@ module Karafka
|
|
|
190
201
|
|
|
191
202
|
# Runs the shutdown code
|
|
192
203
|
def handle_shutdown
|
|
193
|
-
|
|
194
|
-
|
|
204
|
+
monitor.instrument('consumer.shutting_down', caller: self)
|
|
205
|
+
monitor.instrument('consumer.shutdown', caller: self) do
|
|
195
206
|
shutdown
|
|
196
207
|
end
|
|
197
208
|
ensure
|
|
@@ -19,7 +19,8 @@ module Karafka
|
|
|
19
19
|
class Worker
|
|
20
20
|
include Helpers::Async
|
|
21
21
|
include Helpers::ConfigImporter.new(
|
|
22
|
-
worker_job_call_wrapper: %i[internal processing worker_job_call_wrapper]
|
|
22
|
+
worker_job_call_wrapper: %i[internal processing worker_job_call_wrapper],
|
|
23
|
+
monitor: %i[monitor]
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
# @return [String] id of this worker
|
|
@@ -53,28 +54,30 @@ module Karafka
|
|
|
53
54
|
instrument_details = { caller: self, job: job, jobs_queue: @jobs_queue }
|
|
54
55
|
|
|
55
56
|
if job
|
|
56
|
-
|
|
57
|
+
job.wrap do
|
|
58
|
+
monitor.instrument('worker.process', instrument_details)
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
monitor.instrument('worker.processed', instrument_details) do
|
|
61
|
+
job.before_call
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
# If a job is marked as non blocking, we can run a tick in the job queue and if there
|
|
64
|
+
# are no other blocking factors, the job queue will be unlocked.
|
|
65
|
+
# If this does not run, all the things will be blocking and job queue won't allow to
|
|
66
|
+
# pass it until done.
|
|
67
|
+
@jobs_queue.tick(job.group_id) if job.non_blocking?
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
job.call
|
|
69
|
-
else
|
|
70
|
-
worker_job_call_wrapper.wrap do
|
|
69
|
+
if @non_wrapped_flow
|
|
71
70
|
job.call
|
|
71
|
+
else
|
|
72
|
+
worker_job_call_wrapper.wrap do
|
|
73
|
+
job.call
|
|
74
|
+
end
|
|
72
75
|
end
|
|
73
|
-
end
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
job.after_call
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
true
|
|
80
|
+
end
|
|
78
81
|
end
|
|
79
82
|
else
|
|
80
83
|
false
|
|
@@ -83,7 +86,7 @@ module Karafka
|
|
|
83
86
|
# rubocop:disable Lint/RescueException
|
|
84
87
|
rescue Exception => e
|
|
85
88
|
# rubocop:enable Lint/RescueException
|
|
86
|
-
|
|
89
|
+
monitor.instrument(
|
|
87
90
|
'error.occurred',
|
|
88
91
|
caller: self,
|
|
89
92
|
job: job,
|
|
@@ -99,7 +102,7 @@ module Karafka
|
|
|
99
102
|
end
|
|
100
103
|
|
|
101
104
|
# Always publish info, that we completed all the work despite its result
|
|
102
|
-
|
|
105
|
+
monitor.instrument('worker.completed', instrument_details)
|
|
103
106
|
end
|
|
104
107
|
end
|
|
105
108
|
end
|
|
@@ -67,6 +67,20 @@ module Karafka
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
# Clear out the drawn routes.
|
|
71
|
+
alias array_clear clear
|
|
72
|
+
private :array_clear
|
|
73
|
+
|
|
74
|
+
# Clear routes and draw them again with the given block. Helpful for testing purposes.
|
|
75
|
+
# @param block [Proc] block we will evaluate within the builder context
|
|
76
|
+
def redraw(&block)
|
|
77
|
+
@mutex.synchronize do
|
|
78
|
+
@draws.clear
|
|
79
|
+
array_clear
|
|
80
|
+
end
|
|
81
|
+
draw(&block)
|
|
82
|
+
end
|
|
83
|
+
|
|
70
84
|
# @return [Array<Karafka::Routing::ConsumerGroup>] only active consumer groups that
|
|
71
85
|
# we want to use. Since Karafka supports multi-process setup, we need to be able
|
|
72
86
|
# to pick only those consumer groups that should be active in our given process context
|
|
@@ -79,7 +93,7 @@ module Karafka
|
|
|
79
93
|
@mutex.synchronize do
|
|
80
94
|
@defaults = EMPTY_DEFAULTS
|
|
81
95
|
@draws.clear
|
|
82
|
-
|
|
96
|
+
array_clear
|
|
83
97
|
end
|
|
84
98
|
end
|
|
85
99
|
|
data/lib/karafka/version.rb
CHANGED
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: karafka
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.4.
|
|
4
|
+
version: 2.4.16
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maciej Mensfeld
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain:
|
|
11
10
|
- |
|
|
@@ -35,7 +34,7 @@ cert_chain:
|
|
|
35
34
|
i9zWxov0mr44TWegTVeypcWGd/0nxu1+QHVNHJrpqlPBRvwQsUm7fwmRInGpcaB8
|
|
36
35
|
ap8wNYvryYzrzvzUxIVFBVM5PacgkFqRmolCa8I7tdKQN+R1
|
|
37
36
|
-----END CERTIFICATE-----
|
|
38
|
-
date: 2024-
|
|
37
|
+
date: 2024-12-27 00:00:00.000000000 Z
|
|
39
38
|
dependencies:
|
|
40
39
|
- !ruby/object:Gem::Dependency
|
|
41
40
|
name: base64
|
|
@@ -605,7 +604,6 @@ metadata:
|
|
|
605
604
|
source_code_uri: https://github.com/karafka/karafka
|
|
606
605
|
documentation_uri: https://karafka.io/docs
|
|
607
606
|
rubygems_mfa_required: 'true'
|
|
608
|
-
post_install_message:
|
|
609
607
|
rdoc_options: []
|
|
610
608
|
require_paths:
|
|
611
609
|
- lib
|
|
@@ -620,8 +618,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
620
618
|
- !ruby/object:Gem::Version
|
|
621
619
|
version: '0'
|
|
622
620
|
requirements: []
|
|
623
|
-
rubygems_version: 3.
|
|
624
|
-
signing_key:
|
|
621
|
+
rubygems_version: 3.6.2
|
|
625
622
|
specification_version: 4
|
|
626
623
|
summary: Karafka is Ruby and Rails efficient Kafka processing framework.
|
|
627
624
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|