karafka 2.3.0 → 2.3.2
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/.rspec +2 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +22 -22
- data/README.md +2 -2
- data/bin/integrations +2 -1
- data/bin/rspecs +6 -2
- data/config/locales/errors.yml +30 -8
- data/config/locales/pro_errors.yml +2 -0
- data/docker-compose.yml +1 -1
- data/lib/karafka/app.rb +14 -0
- data/lib/karafka/cli/base.rb +19 -0
- data/lib/karafka/cli/server.rb +62 -76
- data/lib/karafka/cli/swarm.rb +30 -0
- data/lib/karafka/constraints.rb +3 -3
- data/lib/karafka/contracts/config.rb +19 -0
- data/lib/karafka/errors.rb +12 -0
- data/lib/karafka/helpers/async.rb +13 -3
- data/lib/karafka/helpers/config_importer.rb +30 -0
- data/lib/karafka/instrumentation/logger_listener.rb +31 -0
- data/lib/karafka/instrumentation/notifications.rb +9 -0
- data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +2 -0
- data/lib/karafka/instrumentation/vendors/kubernetes/base_listener.rb +72 -0
- data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +11 -40
- data/lib/karafka/instrumentation/vendors/kubernetes/swarm_liveness_listener.rb +54 -0
- data/lib/karafka/pro/active_job/job_options_contract.rb +1 -1
- data/lib/karafka/pro/base_consumer.rb +16 -0
- data/lib/karafka/pro/connection/manager.rb +6 -1
- data/lib/karafka/pro/processing/coordinator.rb +13 -3
- data/lib/karafka/pro/processing/coordinators/errors_tracker.rb +74 -0
- data/lib/karafka/pro/processing/coordinators/filters_applier.rb +107 -0
- data/lib/karafka/pro/processing/coordinators/virtual_offset_manager.rb +180 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom.rb +5 -7
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom_vp.rb +5 -7
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom.rb +8 -10
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom_vp.rb +8 -16
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom.rb +5 -7
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom_vp.rb +5 -7
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom.rb +8 -10
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom_vp.rb +7 -9
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +36 -10
- data/lib/karafka/pro/processing/strategies/dlq/ftr.rb +3 -7
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj.rb +4 -8
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +6 -9
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +5 -15
- data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +4 -8
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +6 -9
- data/lib/karafka/pro/processing/strategies/dlq/mom.rb +10 -20
- data/lib/karafka/pro/processing/strategies/vp/default.rb +7 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/contracts/topic.rb +6 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/topic.rb +39 -0
- data/lib/karafka/pro/swarm/liveness_listener.rb +171 -0
- data/lib/karafka/process.rb +27 -1
- data/lib/karafka/routing/features/dead_letter_queue/config.rb +2 -0
- data/lib/karafka/routing/subscription_group.rb +31 -9
- data/lib/karafka/runner.rb +4 -0
- data/lib/karafka/server.rb +13 -16
- data/lib/karafka/setup/config.rb +41 -2
- data/lib/karafka/status.rb +4 -2
- data/lib/karafka/swarm/liveness_listener.rb +55 -0
- data/lib/karafka/swarm/manager.rb +217 -0
- data/lib/karafka/swarm/node.rb +179 -0
- data/lib/karafka/swarm/pidfd.rb +131 -0
- data/lib/karafka/swarm/supervisor.rb +184 -0
- data/lib/karafka/swarm.rb +27 -0
- data/lib/karafka/templates/karafka.rb.erb +0 -2
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +17 -4
- metadata.gz.sig +0 -0
- data/lib/karafka/pro/processing/filters_applier.rb +0 -105
- data/lib/karafka/pro/processing/virtual_offset_manager.rb +0 -177
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Pro
|
16
|
+
module Processing
|
17
|
+
module Coordinators
|
18
|
+
# Manager that keeps track of our offsets with the virtualization layer that are local
|
19
|
+
# to given partition assignment. It allows for easier offset management for virtual
|
20
|
+
# virtual partition cases as it provides us ability to mark as consumed and move the
|
21
|
+
# real offset behind as expected.
|
22
|
+
#
|
23
|
+
# @note We still use the regular coordinator "real" offset management as we want to have
|
24
|
+
# them as separated as possible because the real seek offset management is also used for
|
25
|
+
# pausing, filtering and others and should not be impacted by the virtual one
|
26
|
+
#
|
27
|
+
# @note This manager is **not** thread-safe by itself. It should operate from coordinator
|
28
|
+
# locked locations.
|
29
|
+
class VirtualOffsetManager
|
30
|
+
attr_reader :groups
|
31
|
+
|
32
|
+
# @param topic [String]
|
33
|
+
# @param partition [Integer]
|
34
|
+
# @param offset_metadata_strategy [Symbol] what metadata should we select. That is,
|
35
|
+
# should we use the most recent or one picked from the offset that is going to be
|
36
|
+
# committed
|
37
|
+
#
|
38
|
+
# @note We need topic and partition because we use a seek message (virtual) for real
|
39
|
+
# offset management. We could keep real message reference but this can be memory
|
40
|
+
# consuming and not worth it.
|
41
|
+
def initialize(topic, partition, offset_metadata_strategy)
|
42
|
+
@topic = topic
|
43
|
+
@partition = partition
|
44
|
+
@groups = []
|
45
|
+
@marked = {}
|
46
|
+
@offsets_metadata = {}
|
47
|
+
@real_offset = -1
|
48
|
+
@offset_metadata_strategy = offset_metadata_strategy
|
49
|
+
@current_offset_metadata = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Clears the manager for a next collective operation
|
53
|
+
def clear
|
54
|
+
@groups.clear
|
55
|
+
@offsets_metadata.clear
|
56
|
+
@current_offset_metadata = nil
|
57
|
+
@marked.clear
|
58
|
+
@real_offset = -1
|
59
|
+
end
|
60
|
+
|
61
|
+
# Registers an offset group coming from one virtual consumer. In order to move the real
|
62
|
+
# underlying offset accordingly, we need to make sure to track the virtual consumers
|
63
|
+
# offsets groups independently and only materialize the end result.
|
64
|
+
#
|
65
|
+
# @param offsets_group [Array<Integer>] offsets from one virtual consumer
|
66
|
+
def register(offsets_group)
|
67
|
+
@groups << offsets_group
|
68
|
+
|
69
|
+
offsets_group.each { |offset| @marked[offset] = false }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Marks given message as marked (virtually consumed).
|
73
|
+
# We mark given message offset and other earlier offsets from the same group as done
|
74
|
+
# and we can refresh our real offset representation based on that as it might have
|
75
|
+
# changed to a newer real offset.
|
76
|
+
# @param message [Karafka::Messages::Message] message coming from VP we want to mark
|
77
|
+
# @param offset_metadata [String, nil] offset metadata. `nil` if none
|
78
|
+
def mark(message, offset_metadata)
|
79
|
+
offset = message.offset
|
80
|
+
|
81
|
+
# Store metadata when we materialize the most stable offset
|
82
|
+
@offsets_metadata[offset] = offset_metadata
|
83
|
+
@current_offset_metadata = offset_metadata
|
84
|
+
|
85
|
+
group = @groups.find { |reg_group| reg_group.include?(offset) }
|
86
|
+
|
87
|
+
# This case can happen when someone uses MoM and wants to mark message from a previous
|
88
|
+
# batch as consumed. We can add it, since the real offset refresh will point to it
|
89
|
+
unless group
|
90
|
+
group = [offset]
|
91
|
+
@groups << group
|
92
|
+
end
|
93
|
+
|
94
|
+
position = group.index(offset)
|
95
|
+
|
96
|
+
# Mark all previous messages from the same group also as virtually consumed
|
97
|
+
group[0..position].each do |markable_offset|
|
98
|
+
# Set previous messages metadata offset as the offset of higher one for overwrites
|
99
|
+
# unless a different metadata were set explicitely
|
100
|
+
@offsets_metadata[markable_offset] ||= offset_metadata
|
101
|
+
@marked[markable_offset] = true
|
102
|
+
end
|
103
|
+
|
104
|
+
# Recompute the real offset representation
|
105
|
+
materialize_real_offset
|
106
|
+
end
|
107
|
+
|
108
|
+
# Mark all from all groups including the `message`.
|
109
|
+
# Useful when operating in a collapsed state for marking
|
110
|
+
# @param message [Karafka::Messages::Message]
|
111
|
+
# @param offset_metadata [String, nil]
|
112
|
+
def mark_until(message, offset_metadata)
|
113
|
+
mark(message, offset_metadata)
|
114
|
+
|
115
|
+
@groups.each do |group|
|
116
|
+
group.each do |offset|
|
117
|
+
next if offset > message.offset
|
118
|
+
|
119
|
+
@offsets_metadata[offset] = offset_metadata
|
120
|
+
@marked[offset] = true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
materialize_real_offset
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [Array<Integer>] Offsets of messages already marked as consumed virtually
|
128
|
+
def marked
|
129
|
+
@marked.select { |_, status| status }.map(&:first).sort
|
130
|
+
end
|
131
|
+
|
132
|
+
# Is there a real offset we can mark as consumed
|
133
|
+
# @return [Boolean]
|
134
|
+
def markable?
|
135
|
+
!@real_offset.negative?
|
136
|
+
end
|
137
|
+
|
138
|
+
# @return [Array<Messages::Seek, String>] markable message for real offset marking and
|
139
|
+
# its associated metadata
|
140
|
+
def markable
|
141
|
+
raise Errors::InvalidRealOffsetUsageError unless markable?
|
142
|
+
|
143
|
+
offset_metadata = case @offset_metadata_strategy
|
144
|
+
when :exact
|
145
|
+
@offsets_metadata.fetch(@real_offset)
|
146
|
+
when :current
|
147
|
+
@current_offset_metadata
|
148
|
+
else
|
149
|
+
raise Errors::UnsupportedCaseError, @offset_metadata_strategy
|
150
|
+
end
|
151
|
+
|
152
|
+
[
|
153
|
+
Messages::Seek.new(
|
154
|
+
@topic,
|
155
|
+
@partition,
|
156
|
+
@real_offset
|
157
|
+
),
|
158
|
+
offset_metadata
|
159
|
+
]
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
# Recomputes the biggest possible real offset we can have.
|
165
|
+
# It picks the the biggest offset that has uninterrupted stream of virtually marked as
|
166
|
+
# consumed because this will be the collective offset.
|
167
|
+
def materialize_real_offset
|
168
|
+
@marked.to_a.sort_by(&:first).each do |offset, marked|
|
169
|
+
break unless marked
|
170
|
+
|
171
|
+
@real_offset = offset
|
172
|
+
end
|
173
|
+
|
174
|
+
@real_offset = (@marked.keys.min - 1) if @real_offset.negative?
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -51,14 +51,12 @@ module Karafka
|
|
51
51
|
else
|
52
52
|
resume
|
53
53
|
end
|
54
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
55
|
-
retry_after_pause
|
56
54
|
else
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
apply_dlq_flow do
|
56
|
+
skippable_message, = find_skippable_message
|
57
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
58
|
+
mark_as_consumed(skippable_message)
|
59
|
+
end
|
62
60
|
end
|
63
61
|
end
|
64
62
|
end
|
@@ -57,14 +57,12 @@ module Karafka
|
|
57
57
|
else
|
58
58
|
resume
|
59
59
|
end
|
60
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
61
|
-
retry_after_pause
|
62
60
|
else
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
apply_dlq_flow do
|
62
|
+
skippable_message, = find_skippable_message
|
63
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
64
|
+
mark_as_consumed(skippable_message)
|
65
|
+
end
|
68
66
|
end
|
69
67
|
end
|
70
68
|
end
|
@@ -44,8 +44,6 @@ module Karafka
|
|
44
44
|
return if coordinator.manual_pause?
|
45
45
|
|
46
46
|
handle_post_filtering
|
47
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
48
|
-
retry_after_pause
|
49
47
|
# If we've reached number of retries that we could, we need to skip the first
|
50
48
|
# message that was not marked as consumed, pause and continue, while also moving
|
51
49
|
# this message to the dead topic.
|
@@ -53,14 +51,14 @@ module Karafka
|
|
53
51
|
# For a Mom setup, this means, that user has to manage the checkpointing by
|
54
52
|
# himself. If no checkpointing is ever done, we end up with an endless loop.
|
55
53
|
else
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
54
|
+
apply_dlq_flow do
|
55
|
+
skippable_message, = find_skippable_message
|
56
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
57
|
+
# We can commit the offset here because we know that we skip it "forever" and
|
58
|
+
# since AJ consumer commits the offset after each job, we also know that the
|
59
|
+
# previous job was successful
|
60
|
+
mark_as_consumed(skippable_message)
|
61
|
+
end
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|
@@ -48,23 +48,15 @@ module Karafka
|
|
48
48
|
mark_as_consumed(last_group_message)
|
49
49
|
|
50
50
|
handle_post_filtering
|
51
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
52
|
-
retry_after_pause
|
53
|
-
# If we've reached number of retries that we could, we need to skip the first
|
54
|
-
# message that was not marked as consumed, pause and continue, while also moving
|
55
|
-
# this message to the dead topic.
|
56
|
-
#
|
57
|
-
# For a Mom setup, this means, that user has to manage the checkpointing by
|
58
|
-
# himself. If no checkpointing is ever done, we end up with an endless loop.
|
59
51
|
else
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
52
|
+
apply_dlq_flow do
|
53
|
+
skippable_message, = find_skippable_message
|
54
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
55
|
+
# We can commit the offset here because we know that we skip it "forever" and
|
56
|
+
# since AJ consumer commits the offset after each job, we also know that the
|
57
|
+
# previous job was successful
|
58
|
+
mark_as_consumed(skippable_message)
|
59
|
+
end
|
68
60
|
end
|
69
61
|
end
|
70
62
|
end
|
@@ -47,14 +47,12 @@ module Karafka
|
|
47
47
|
seek(coordinator.seek_offset, false) unless revoked?
|
48
48
|
|
49
49
|
resume
|
50
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
51
|
-
retry_after_pause
|
52
50
|
else
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
apply_dlq_flow do
|
52
|
+
skippable_message, = find_skippable_message
|
53
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
54
|
+
mark_as_consumed(skippable_message)
|
55
|
+
end
|
58
56
|
end
|
59
57
|
end
|
60
58
|
end
|
@@ -51,14 +51,12 @@ module Karafka
|
|
51
51
|
seek(coordinator.seek_offset, false) unless revoked?
|
52
52
|
|
53
53
|
resume
|
54
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
55
|
-
retry_after_pause
|
56
54
|
else
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
apply_dlq_flow do
|
56
|
+
skippable_message, = find_skippable_message
|
57
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
58
|
+
mark_as_consumed(skippable_message)
|
59
|
+
end
|
62
60
|
end
|
63
61
|
end
|
64
62
|
end
|
@@ -42,17 +42,15 @@ module Karafka
|
|
42
42
|
if coordinator.success?
|
43
43
|
# Do NOT commit offsets, they are comitted after each job in the AJ consumer.
|
44
44
|
coordinator.pause_tracker.reset
|
45
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
46
|
-
retry_after_pause
|
47
45
|
else
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
46
|
+
apply_dlq_flow do
|
47
|
+
skippable_message, = find_skippable_message
|
48
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
49
|
+
# We can commit the offset here because we know that we skip it "forever" and
|
50
|
+
# since AJ consumer commits the offset after each job, we also know that the
|
51
|
+
# previous job was successful
|
52
|
+
mark_as_consumed(skippable_message)
|
53
|
+
end
|
56
54
|
end
|
57
55
|
end
|
58
56
|
end
|
@@ -48,16 +48,14 @@ module Karafka
|
|
48
48
|
return if revoked?
|
49
49
|
|
50
50
|
mark_as_consumed(last_group_message)
|
51
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
52
|
-
retry_after_pause
|
53
51
|
else
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
52
|
+
apply_dlq_flow do
|
53
|
+
# Here we are in a collapsed state, hence we can apply the same logic as
|
54
|
+
# Aj::DlqMom
|
55
|
+
skippable_message, = find_skippable_message
|
56
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
57
|
+
mark_as_consumed(skippable_message)
|
58
|
+
end
|
61
59
|
end
|
62
60
|
end
|
63
61
|
end
|
@@ -76,16 +76,10 @@ module Karafka
|
|
76
76
|
return if coordinator.manual_pause?
|
77
77
|
|
78
78
|
mark_as_consumed(last_group_message)
|
79
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
80
|
-
retry_after_pause
|
81
|
-
# If we've reached number of retries that we could, we need to skip the first
|
82
|
-
# message that was not marked as consumed, pause and continue, while also moving
|
83
|
-
# this message to the dead topic
|
84
79
|
else
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
pause(coordinator.seek_offset, nil, false)
|
80
|
+
apply_dlq_flow do
|
81
|
+
dispatch_if_needed_and_mark_as_consumed
|
82
|
+
end
|
89
83
|
end
|
90
84
|
end
|
91
85
|
end
|
@@ -183,7 +177,10 @@ module Karafka
|
|
183
177
|
# topic is set to false, we will skip the dispatch, effectively ignoring the broken
|
184
178
|
# message without taking any action.
|
185
179
|
def dispatch_to_dlq?
|
186
|
-
topic.dead_letter_queue.topic
|
180
|
+
return false unless topic.dead_letter_queue.topic
|
181
|
+
return false unless @_dispatch_to_dlq
|
182
|
+
|
183
|
+
true
|
187
184
|
end
|
188
185
|
|
189
186
|
# @return [Boolean] should we use a transaction to move the data to the DLQ.
|
@@ -192,6 +189,35 @@ module Karafka
|
|
192
189
|
def dispatch_in_a_transaction?
|
193
190
|
producer.transactional? && topic.dead_letter_queue.transactional?
|
194
191
|
end
|
192
|
+
|
193
|
+
# Runs the DLQ strategy and based on it it performs certain operations
|
194
|
+
#
|
195
|
+
# In case of `:skip` and `:dispatch` will run the exact flow provided in a block
|
196
|
+
# In case of `:retry` always `#retry_after_pause` is applied
|
197
|
+
def apply_dlq_flow
|
198
|
+
flow = topic.dead_letter_queue.strategy.call(errors_tracker, attempt)
|
199
|
+
|
200
|
+
case flow
|
201
|
+
when :retry
|
202
|
+
retry_after_pause
|
203
|
+
|
204
|
+
return
|
205
|
+
when :skip
|
206
|
+
@_dispatch_to_dlq = false
|
207
|
+
when :dispatch
|
208
|
+
@_dispatch_to_dlq = true
|
209
|
+
else
|
210
|
+
raise Karafka::UnsupportedCaseError, flow
|
211
|
+
end
|
212
|
+
|
213
|
+
# We reset the pause to indicate we will now consider it as "ok".
|
214
|
+
coordinator.pause_tracker.reset
|
215
|
+
|
216
|
+
yield
|
217
|
+
|
218
|
+
# Always backoff after DLQ dispatch even on skip to prevent overloads on errors
|
219
|
+
pause(coordinator.seek_offset, nil, false)
|
220
|
+
end
|
195
221
|
end
|
196
222
|
end
|
197
223
|
end
|
@@ -42,14 +42,10 @@ module Karafka
|
|
42
42
|
mark_as_consumed(last_group_message)
|
43
43
|
|
44
44
|
handle_post_filtering
|
45
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
46
|
-
retry_after_pause
|
47
45
|
else
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
pause(coordinator.seek_offset, nil, false)
|
46
|
+
apply_dlq_flow do
|
47
|
+
dispatch_if_needed_and_mark_as_consumed
|
48
|
+
end
|
53
49
|
end
|
54
50
|
end
|
55
51
|
end
|
@@ -53,16 +53,12 @@ module Karafka
|
|
53
53
|
else
|
54
54
|
resume
|
55
55
|
end
|
56
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
57
|
-
retry_after_pause
|
58
56
|
else
|
59
|
-
|
60
|
-
|
61
|
-
return resume if revoked?
|
57
|
+
apply_dlq_flow do
|
58
|
+
return resume if revoked?
|
62
59
|
|
63
|
-
|
64
|
-
|
65
|
-
pause(coordinator.seek_offset, nil, false)
|
60
|
+
dispatch_if_needed_and_mark_as_consumed
|
61
|
+
end
|
66
62
|
end
|
67
63
|
end
|
68
64
|
end
|
@@ -48,18 +48,15 @@ module Karafka
|
|
48
48
|
else
|
49
49
|
resume
|
50
50
|
end
|
51
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
52
|
-
retry_after_pause
|
53
51
|
else
|
54
|
-
|
55
|
-
|
56
|
-
return resume if revoked?
|
52
|
+
apply_dlq_flow do
|
53
|
+
return resume if revoked?
|
57
54
|
|
58
|
-
|
59
|
-
|
55
|
+
skippable_message, _marked = find_skippable_message
|
56
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
60
57
|
|
61
|
-
|
62
|
-
|
58
|
+
coordinator.seek_offset = skippable_message.offset + 1
|
59
|
+
end
|
63
60
|
end
|
64
61
|
end
|
65
62
|
end
|
@@ -41,23 +41,13 @@ module Karafka
|
|
41
41
|
return if coordinator.manual_pause?
|
42
42
|
|
43
43
|
handle_post_filtering
|
44
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
45
|
-
retry_after_pause
|
46
|
-
# If we've reached number of retries that we could, we need to skip the first
|
47
|
-
# message that was not marked as consumed, pause and continue, while also moving
|
48
|
-
# this message to the dead topic.
|
49
|
-
#
|
50
|
-
# For a Mom setup, this means, that user has to manage the checkpointing by
|
51
|
-
# himself. If no checkpointing is ever done, we end up with an endless loop.
|
52
44
|
else
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
skippable_message, _marked = find_skippable_message
|
57
|
-
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
45
|
+
apply_dlq_flow do
|
46
|
+
skippable_message, _marked = find_skippable_message
|
47
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
58
48
|
|
59
|
-
|
60
|
-
|
49
|
+
coordinator.seek_offset = skippable_message.offset + 1
|
50
|
+
end
|
61
51
|
end
|
62
52
|
end
|
63
53
|
end
|
@@ -42,16 +42,12 @@ module Karafka
|
|
42
42
|
seek(coordinator.seek_offset, false) unless revoked? || coordinator.manual_seek?
|
43
43
|
|
44
44
|
resume
|
45
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
46
|
-
retry_after_pause
|
47
45
|
else
|
48
|
-
|
49
|
-
|
50
|
-
return resume if revoked?
|
51
|
-
|
52
|
-
dispatch_if_needed_and_mark_as_consumed
|
46
|
+
apply_dlq_flow do
|
47
|
+
return resume if revoked?
|
53
48
|
|
54
|
-
|
49
|
+
dispatch_if_needed_and_mark_as_consumed
|
50
|
+
end
|
55
51
|
end
|
56
52
|
end
|
57
53
|
end
|
@@ -42,18 +42,15 @@ module Karafka
|
|
42
42
|
end
|
43
43
|
|
44
44
|
resume
|
45
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
46
|
-
retry_after_pause
|
47
45
|
else
|
48
|
-
|
49
|
-
|
50
|
-
return resume if revoked?
|
46
|
+
apply_dlq_flow do
|
47
|
+
return resume if revoked?
|
51
48
|
|
52
|
-
|
53
|
-
|
49
|
+
skippable_message, _marked = find_skippable_message
|
50
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
54
51
|
|
55
|
-
|
56
|
-
|
52
|
+
coordinator.seek_offset = skippable_message.offset + 1
|
53
|
+
end
|
57
54
|
end
|
58
55
|
end
|
59
56
|
end
|
@@ -35,28 +35,18 @@ module Karafka
|
|
35
35
|
|
36
36
|
if coordinator.success?
|
37
37
|
coordinator.pause_tracker.reset
|
38
|
-
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
39
|
-
retry_after_pause
|
40
|
-
# If we've reached number of retries that we could, we need to skip the first
|
41
|
-
# message that was not marked as consumed, pause and continue, while also moving
|
42
|
-
# this message to the dead topic.
|
43
|
-
#
|
44
|
-
# For a Mom setup, this means, that user has to manage the checkpointing by
|
45
|
-
# himself. If no checkpointing is ever done, we end up with an endless loop.
|
46
38
|
else
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
skippable_message, = find_skippable_message
|
51
|
-
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
39
|
+
apply_dlq_flow do
|
40
|
+
skippable_message, = find_skippable_message
|
41
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
52
42
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
43
|
+
# Save the next offset we want to go with after moving given message to DLQ
|
44
|
+
# Without this, we would not be able to move forward and we would end up
|
45
|
+
# in an infinite loop trying to un-pause from the message we've already
|
46
|
+
# processed. Of course, since it's a MoM a rebalance or kill, will move it back
|
47
|
+
# as no offsets are being committed
|
48
|
+
coordinator.seek_offset = skippable_message.offset + 1
|
49
|
+
end
|
60
50
|
end
|
61
51
|
end
|
62
52
|
end
|
@@ -155,6 +155,13 @@ module Karafka
|
|
155
155
|
def handle_before_schedule_consume
|
156
156
|
super
|
157
157
|
|
158
|
+
# We should not register offsets in virtual manager when in collapse as virtual
|
159
|
+
# manager is not used then for offsets materialization.
|
160
|
+
#
|
161
|
+
# If we would do so, it would cause increased storage in cases of endless errors
|
162
|
+
# that are being retried in collapse without a DLQ.
|
163
|
+
return if collapsed?
|
164
|
+
|
158
165
|
coordinator.virtual_offset_manager.register(
|
159
166
|
messages.map(&:offset)
|
160
167
|
)
|