karafka 2.0.27 → 2.0.28
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/CHANGELOG.md +27 -795
- data/Gemfile.lock +6 -6
- data/config/locales/pro_errors.yml +1 -1
- data/lib/karafka/connection/listener.rb +1 -1
- data/lib/karafka/pro/active_job/consumer.rb +3 -2
- data/lib/karafka/pro/processing/collapser.rb +62 -0
- data/lib/karafka/pro/processing/coordinator.rb +17 -0
- data/lib/karafka/pro/processing/partitioner.rb +19 -5
- data/lib/karafka/pro/processing/strategies/aj_dlq_lrj_mom_vp.rb +70 -0
- data/lib/karafka/pro/processing/strategies/aj_dlq_mom_vp.rb +68 -0
- data/lib/karafka/pro/processing/strategies/aj_lrj_mom_vp.rb +1 -0
- data/lib/karafka/pro/processing/strategies/aj_mom_vp.rb +4 -0
- data/lib/karafka/pro/processing/strategies/default.rb +2 -2
- data/lib/karafka/pro/processing/strategies/dlq.rb +31 -7
- data/lib/karafka/pro/processing/strategies/dlq_lrj_vp.rb +36 -0
- data/lib/karafka/pro/processing/strategies/dlq_vp.rb +37 -0
- data/lib/karafka/pro/processing/strategies/lrj_vp.rb +1 -0
- data/lib/karafka/pro/processing/strategies/vp.rb +5 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +6 -5
- data/lib/karafka/processing/coordinator.rb +27 -8
- data/lib/karafka/processing/partitioner.rb +3 -1
- data/lib/karafka/processing/strategies/default.rb +2 -3
- data/lib/karafka/setup/config.rb +3 -1
- data/lib/karafka/templates/karafka.rb.erb +1 -1
- data/lib/karafka/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +7 -2
- metadata.gz.sig +0 -0
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
karafka (2.0.
|
4
|
+
karafka (2.0.28)
|
5
5
|
karafka-core (>= 2.0.9, < 3.0.0)
|
6
6
|
thor (>= 0.20)
|
7
7
|
waterdrop (>= 2.4.9, < 3.0.0)
|
@@ -10,10 +10,10 @@ PATH
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
activejob (7.0.4)
|
14
|
-
activesupport (= 7.0.4)
|
13
|
+
activejob (7.0.4.1)
|
14
|
+
activesupport (= 7.0.4.1)
|
15
15
|
globalid (>= 0.3.6)
|
16
|
-
activesupport (7.0.4)
|
16
|
+
activesupport (7.0.4.1)
|
17
17
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
18
|
i18n (>= 1.6, < 2)
|
19
19
|
minitest (>= 5.1)
|
@@ -25,7 +25,7 @@ GEM
|
|
25
25
|
factory_bot (6.2.1)
|
26
26
|
activesupport (>= 5.0.0)
|
27
27
|
ffi (1.15.5)
|
28
|
-
globalid (1.0.
|
28
|
+
globalid (1.0.1)
|
29
29
|
activesupport (>= 5.0)
|
30
30
|
i18n (1.12.0)
|
31
31
|
concurrent-ruby (~> 1.0)
|
@@ -48,7 +48,7 @@ GEM
|
|
48
48
|
rspec-expectations (3.12.2)
|
49
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
50
50
|
rspec-support (~> 3.12.0)
|
51
|
-
rspec-mocks (3.12.
|
51
|
+
rspec-mocks (3.12.3)
|
52
52
|
diff-lcs (>= 1.2.0, < 2.0)
|
53
53
|
rspec-support (~> 3.12.0)
|
54
54
|
rspec-support (3.12.0)
|
@@ -5,7 +5,7 @@ en:
|
|
5
5
|
virtual_partitions.max_partitions_format: needs to be equal or more than 1
|
6
6
|
manual_offset_management_not_with_virtual_partitions: cannot be used together with Virtual Partitions
|
7
7
|
long_running_job.active_format: needs to be either true or false
|
8
|
-
|
8
|
+
dead_letter_queue_with_virtual_partitions: when using Dead Letter Queue with Virtual Partitions, at least one retry is required.
|
9
9
|
|
10
10
|
config:
|
11
11
|
encryption.active_format: 'needs to be either true or false'
|
@@ -267,7 +267,7 @@ module Karafka
|
|
267
267
|
# Start work coordination for this topic partition
|
268
268
|
coordinator.start(messages)
|
269
269
|
|
270
|
-
@partitioner.call(topic, messages) do |group_id, partition_messages|
|
270
|
+
@partitioner.call(topic, messages, coordinator) do |group_id, partition_messages|
|
271
271
|
# Count the job we're going to create here
|
272
272
|
coordinator.increment
|
273
273
|
executor = @executors.find_or_create(topic, partition, group_id)
|
@@ -36,8 +36,9 @@ module Karafka
|
|
36
36
|
)
|
37
37
|
|
38
38
|
# We cannot mark jobs as done after each if there are virtual partitions. Otherwise
|
39
|
-
# this could create random markings
|
40
|
-
|
39
|
+
# this could create random markings.
|
40
|
+
# The exception here is the collapsed state where we can move one after another
|
41
|
+
next if topic.virtual_partitions? && !collapsed?
|
41
42
|
|
42
43
|
mark_as_consumed(message)
|
43
44
|
end
|
@@ -0,0 +1,62 @@
|
|
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
|
+
# Manages the collapse of virtual partitions
|
18
|
+
# Since any non-virtual partition is actually a virtual partition of size one, we can use
|
19
|
+
# it in a generic manner without having to distinguish between those cases.
|
20
|
+
#
|
21
|
+
# We need to have notion of the offset until we want to collapse because upon pause and retry
|
22
|
+
# rdkafka may purge the buffer. This means, that we may end up with smaller or bigger
|
23
|
+
# (different) dataset and without tracking the end of collapse, there would be a chance for
|
24
|
+
# things to flicker. Tracking allows us to ensure, that collapse is happening until all the
|
25
|
+
# messages from the corrupted batch are processed.
|
26
|
+
class Collapser
|
27
|
+
# When initialized, nothing is collapsed
|
28
|
+
def initialize
|
29
|
+
@collapsed = false
|
30
|
+
@until_offset = -1
|
31
|
+
@mutex = Mutex.new
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean] Should we collapse into a single consumer
|
35
|
+
def collapsed?
|
36
|
+
@collapsed
|
37
|
+
end
|
38
|
+
|
39
|
+
# Collapse until given offset. Until given offset is encountered or offset bigger than that
|
40
|
+
# we keep collapsing.
|
41
|
+
# @param offset [Integer] offset until which we keep the collapse
|
42
|
+
def collapse_until!(offset)
|
43
|
+
@mutex.synchronize do
|
44
|
+
# We check it here in case after a pause and re-fetch we would get less messages and
|
45
|
+
# one of them would cause an error. We do not want to overwrite the offset here unless
|
46
|
+
# it is bigger.
|
47
|
+
@until_offset = offset if offset > @until_offset
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets the collapse state based on the first collective offset that we are going to process
|
52
|
+
# and makes the decision whether or not we need to still keep the collapse.
|
53
|
+
# @param first_offset [Integer] first offset from a collective batch
|
54
|
+
def refresh!(first_offset)
|
55
|
+
@mutex.synchronize do
|
56
|
+
@collapsed = first_offset < @until_offset
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -23,6 +23,7 @@ module Karafka
|
|
23
23
|
|
24
24
|
@executed = []
|
25
25
|
@flow_lock = Mutex.new
|
26
|
+
@collapser = Collapser.new
|
26
27
|
end
|
27
28
|
|
28
29
|
# Starts the coordination process
|
@@ -31,12 +32,28 @@ module Karafka
|
|
31
32
|
def start(messages)
|
32
33
|
super
|
33
34
|
|
35
|
+
@collapser.refresh!(messages.first.offset)
|
36
|
+
|
34
37
|
@mutex.synchronize do
|
35
38
|
@executed.clear
|
36
39
|
@last_message = messages.last
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
43
|
+
# Sets the consumer failure status and additionally starts the collapse until
|
44
|
+
#
|
45
|
+
# @param consumer [Karafka::BaseConsumer] consumer that failed
|
46
|
+
# @param error [StandardError] error from the failure
|
47
|
+
def failure!(consumer, error)
|
48
|
+
super
|
49
|
+
@collapser.collapse_until!(@last_message.offset + 1)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Boolean] are we in a collapsed state at the moment
|
53
|
+
def collapsed?
|
54
|
+
@collapser.collapsed?
|
55
|
+
end
|
56
|
+
|
40
57
|
# @return [Boolean] is the coordinated work finished or not
|
41
58
|
def finished?
|
42
59
|
@running_jobs.zero?
|
@@ -18,15 +18,29 @@ module Karafka
|
|
18
18
|
class Partitioner < ::Karafka::Processing::Partitioner
|
19
19
|
# @param topic [String] topic name
|
20
20
|
# @param messages [Array<Karafka::Messages::Message>] karafka messages
|
21
|
+
# @param coordinator [Karafka::Pro::Processing::Coordinator] processing coordinator that
|
22
|
+
# will be used with those messages
|
21
23
|
# @yieldparam [Integer] group id
|
22
24
|
# @yieldparam [Array<Karafka::Messages::Message>] karafka messages
|
23
|
-
def call(topic, messages)
|
25
|
+
def call(topic, messages, coordinator)
|
24
26
|
ktopic = @subscription_group.topics.find(topic)
|
25
27
|
|
26
|
-
# We only partition work if we have
|
27
|
-
#
|
28
|
-
#
|
29
|
-
|
28
|
+
# We only partition work if we have:
|
29
|
+
# - a virtual partitioner
|
30
|
+
# - more than one thread to process the data
|
31
|
+
# - collective is not collapsed via coordinator
|
32
|
+
#
|
33
|
+
# With one thread it is not worth partitioning the work as the work itself will be
|
34
|
+
# assigned to one thread (pointless work)
|
35
|
+
#
|
36
|
+
# We collapse the partitioning on errors because we "regain" full ordering on a batch
|
37
|
+
# that potentially contains the data that caused the error.
|
38
|
+
#
|
39
|
+
# This is great because it allows us to run things without the parallelization that adds
|
40
|
+
# a bit of uncertainty and allows us to use DLQ and safely skip messages if needed.
|
41
|
+
if ktopic.virtual_partitions? &&
|
42
|
+
ktopic.virtual_partitions.max_partitions > 1 &&
|
43
|
+
!coordinator.collapsed?
|
30
44
|
# We need to reduce it to the max concurrency, so the group_id is not a direct effect
|
31
45
|
# of the end user action. Otherwise the persistence layer for consumers would cache
|
32
46
|
# it forever and it would cause memory leaks
|
@@ -0,0 +1,70 @@
|
|
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 Strategies
|
18
|
+
# ActiveJob enabled
|
19
|
+
# DLQ enabled
|
20
|
+
# Long-Running Job enabled
|
21
|
+
# Manual offset management enabled
|
22
|
+
# Virtual Partitions enabled
|
23
|
+
#
|
24
|
+
# This case is a bit of special. Please see the `AjDlqMom` for explanation on how the
|
25
|
+
# offset management works in this case.
|
26
|
+
module AjDlqLrjMomVp
|
27
|
+
include AjDlqMomVp
|
28
|
+
include AjLrjMom
|
29
|
+
|
30
|
+
# Features for this strategy
|
31
|
+
FEATURES = %i[
|
32
|
+
active_job
|
33
|
+
long_running_job
|
34
|
+
manual_offset_management
|
35
|
+
dead_letter_queue
|
36
|
+
virtual_partitions
|
37
|
+
].freeze
|
38
|
+
|
39
|
+
# This strategy is pretty much as non VP one because of the collapse
|
40
|
+
def handle_after_consume
|
41
|
+
coordinator.on_finished do |last_group_message|
|
42
|
+
if coordinator.success?
|
43
|
+
coordinator.pause_tracker.reset
|
44
|
+
|
45
|
+
return if revoked?
|
46
|
+
return if Karafka::App.stopping?
|
47
|
+
|
48
|
+
# Since we have VP here we do not commit intermediate offsets and need to commit
|
49
|
+
# them here. We do commit in collapsed mode but this is generalized.
|
50
|
+
mark_as_consumed(last_group_message)
|
51
|
+
|
52
|
+
seek(coordinator.seek_offset) unless revoked?
|
53
|
+
|
54
|
+
resume
|
55
|
+
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
56
|
+
retry_after_pause
|
57
|
+
else
|
58
|
+
coordinator.pause_tracker.reset
|
59
|
+
skippable_message = find_skippable_message
|
60
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
61
|
+
mark_as_consumed(skippable_message)
|
62
|
+
pause(coordinator.seek_offset, nil, false)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,68 @@
|
|
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 Strategies
|
18
|
+
# ActiveJob enabled
|
19
|
+
# Manual offset management enabled
|
20
|
+
# Virtual Partitions enabled
|
21
|
+
module AjDlqMomVp
|
22
|
+
include Dlq
|
23
|
+
include Vp
|
24
|
+
include Default
|
25
|
+
|
26
|
+
# Features for this strategy
|
27
|
+
FEATURES = %i[
|
28
|
+
active_job
|
29
|
+
dead_letter_queue
|
30
|
+
manual_offset_management
|
31
|
+
virtual_partitions
|
32
|
+
].freeze
|
33
|
+
|
34
|
+
# Flow including moving to DLQ in the collapsed mode
|
35
|
+
def handle_after_consume
|
36
|
+
coordinator.on_finished do |last_group_message|
|
37
|
+
if coordinator.success?
|
38
|
+
coordinator.pause_tracker.reset
|
39
|
+
|
40
|
+
# When this is an ActiveJob running via Pro with virtual partitions, we cannot mark
|
41
|
+
# intermediate jobs as processed not to mess up with the ordering.
|
42
|
+
# Only when all the jobs are processed and we did not loose the partition
|
43
|
+
# assignment and we are not stopping (Pro ActiveJob has an early break) we can
|
44
|
+
# commit offsets on this as only then we can be sure, that all the jobs were
|
45
|
+
# processed.
|
46
|
+
# For a non virtual partitions case, the flow is regular and state is marked after
|
47
|
+
# each successfully processed job
|
48
|
+
return if revoked?
|
49
|
+
return if Karafka::App.stopping?
|
50
|
+
|
51
|
+
mark_as_consumed(last_group_message)
|
52
|
+
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
53
|
+
retry_after_pause
|
54
|
+
else
|
55
|
+
# Here we are in a collapsed state, hence we can apply the same logic as AjDlqMom
|
56
|
+
coordinator.pause_tracker.reset
|
57
|
+
skippable_message = find_skippable_message
|
58
|
+
dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
|
59
|
+
mark_as_consumed(skippable_message)
|
60
|
+
pause(coordinator.seek_offset, nil, false)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -19,6 +19,7 @@ module Karafka
|
|
19
19
|
# Manual offset management enabled
|
20
20
|
# Virtual Partitions enabled
|
21
21
|
module AjMomVp
|
22
|
+
include Vp
|
22
23
|
include Default
|
23
24
|
|
24
25
|
# Features for this strategy
|
@@ -42,6 +43,9 @@ module Karafka
|
|
42
43
|
# processed.
|
43
44
|
# For a non virtual partitions case, the flow is regular and state is marked after
|
44
45
|
# each successfully processed job
|
46
|
+
#
|
47
|
+
# We can mark and we do mark intermediate jobs in the collapsed mode when running
|
48
|
+
# VPs
|
45
49
|
return if revoked?
|
46
50
|
return if Karafka::App.stopping?
|
47
51
|
|
@@ -53,10 +53,10 @@ module Karafka
|
|
53
53
|
end
|
54
54
|
|
55
55
|
# Mark job as successful
|
56
|
-
coordinator.
|
56
|
+
coordinator.success!(self)
|
57
57
|
rescue StandardError => e
|
58
58
|
# If failed, mark as failed
|
59
|
-
coordinator.
|
59
|
+
coordinator.failure!(self, e)
|
60
60
|
|
61
61
|
# Re-raise so reported in the consumer
|
62
62
|
raise e
|
@@ -65,13 +65,8 @@ module Karafka
|
|
65
65
|
# dispatch to DLQ
|
66
66
|
def dispatch_to_dlq(skippable_message)
|
67
67
|
producer.produce_async(
|
68
|
-
|
69
|
-
|
70
|
-
key: skippable_message.partition.to_s,
|
71
|
-
headers: skippable_message.headers.merge(
|
72
|
-
'original_topic' => topic.name,
|
73
|
-
'original_partition' => skippable_message.partition.to_s,
|
74
|
-
'original_offset' => skippable_message.offset.to_s
|
68
|
+
build_dlq_message(
|
69
|
+
skippable_message
|
75
70
|
)
|
76
71
|
)
|
77
72
|
|
@@ -83,6 +78,35 @@ module Karafka
|
|
83
78
|
)
|
84
79
|
end
|
85
80
|
|
81
|
+
# @param skippable_message [Array<Karafka::Messages::Message>]
|
82
|
+
# @return [Hash] dispatch DLQ message
|
83
|
+
def build_dlq_message(skippable_message)
|
84
|
+
original_partition = skippable_message.partition.to_s
|
85
|
+
|
86
|
+
dlq_message = {
|
87
|
+
topic: topic.dead_letter_queue.topic,
|
88
|
+
key: original_partition,
|
89
|
+
payload: skippable_message.raw_payload,
|
90
|
+
headers: skippable_message.headers.merge(
|
91
|
+
'original_topic' => topic.name,
|
92
|
+
'original_partition' => original_partition,
|
93
|
+
'original_offset' => skippable_message.offset.to_s,
|
94
|
+
'original_consumer_group' => topic.consumer_group.id
|
95
|
+
)
|
96
|
+
}
|
97
|
+
|
98
|
+
# Optional method user can define in consumer to enhance the dlq message hash with
|
99
|
+
# some extra details if needed or to replace payload, etc
|
100
|
+
if respond_to?(:enhance_dlq_message, true)
|
101
|
+
enhance_dlq_message(
|
102
|
+
dlq_message,
|
103
|
+
skippable_message
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
dlq_message
|
108
|
+
end
|
109
|
+
|
86
110
|
# @return [Boolean] should we dispatch the message to DLQ or not. When the dispatch topic
|
87
111
|
# is set to false, we will skip the dispatch, effectively ignoring the broken message
|
88
112
|
# without taking any action.
|
@@ -0,0 +1,36 @@
|
|
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 Strategies
|
18
|
+
# Dead-Letter Queue enabled
|
19
|
+
# Long-Running Job enabled
|
20
|
+
# Virtual Partitions enabled
|
21
|
+
module DlqLrjVp
|
22
|
+
# Same flow as the Dlq Lrj because VP collapses on errors, so DlqLrj can kick in
|
23
|
+
include Vp
|
24
|
+
include DlqLrj
|
25
|
+
|
26
|
+
# Features for this strategy
|
27
|
+
FEATURES = %i[
|
28
|
+
dead_letter_queue
|
29
|
+
long_running_job
|
30
|
+
virtual_partitions
|
31
|
+
].freeze
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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 Strategies
|
18
|
+
# Dead Letter Queue enabled
|
19
|
+
# Virtual Partitions enabled
|
20
|
+
#
|
21
|
+
# In general because we collapse processing in virtual partitions to one on errors, there
|
22
|
+
# is no special action that needs to be taken because we warranty that even with VPs
|
23
|
+
# on errors a retry collapses into a single state.
|
24
|
+
module DlqVp
|
25
|
+
# Features for this strategy
|
26
|
+
FEATURES = %i[
|
27
|
+
dead_letter_queue
|
28
|
+
virtual_partitions
|
29
|
+
].freeze
|
30
|
+
|
31
|
+
include Dlq
|
32
|
+
include Vp
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -26,10 +26,10 @@ module Karafka
|
|
26
26
|
).fetch('en').fetch('validations').fetch('topic')
|
27
27
|
end
|
28
28
|
|
29
|
-
# Make sure that we
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
29
|
+
# Make sure that when we use virtual partitions with DLQ, at least one retry is set
|
30
|
+
# We cannot use VP with DLQ without retries as we in order to provide ordering
|
31
|
+
# warranties on errors with VP, we need to collapse the VPs concurrency and retry
|
32
|
+
# without any indeterministic work
|
33
33
|
virtual do |data, errors|
|
34
34
|
next unless errors.empty?
|
35
35
|
|
@@ -38,8 +38,9 @@ module Karafka
|
|
38
38
|
|
39
39
|
next unless dead_letter_queue[:active]
|
40
40
|
next unless virtual_partitions[:active]
|
41
|
+
next if dead_letter_queue[:max_retries].positive?
|
41
42
|
|
42
|
-
[[%i[dead_letter_queue], :
|
43
|
+
[[%i[dead_letter_queue], :with_virtual_partitions]]
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
@@ -71,18 +71,28 @@ module Karafka
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
#
|
75
|
-
|
76
|
-
# consumption processing state.
|
77
|
-
def consumption(consumer)
|
74
|
+
# Is all the consumption done and finished successfully for this coordinator
|
75
|
+
def success?
|
78
76
|
@mutex.synchronize do
|
79
|
-
@
|
77
|
+
@running_jobs.zero? && @consumptions.values.all?(&:success?)
|
80
78
|
end
|
81
79
|
end
|
82
80
|
|
83
|
-
#
|
84
|
-
|
85
|
-
|
81
|
+
# Mark given consumption on consumer as successful
|
82
|
+
# @param consumer [Karafka::BaseConsumer] consumer that finished successfully
|
83
|
+
def success!(consumer)
|
84
|
+
@mutex.synchronize do
|
85
|
+
consumption(consumer).success!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Mark given consumption on consumer as failed
|
90
|
+
# @param consumer [Karafka::BaseConsumer] consumer that failed
|
91
|
+
# @param error [StandardError] error that occurred
|
92
|
+
def failure!(consumer, error)
|
93
|
+
@mutex.synchronize do
|
94
|
+
consumption(consumer).failure!(error)
|
95
|
+
end
|
86
96
|
end
|
87
97
|
|
88
98
|
# Marks given coordinator for processing group as revoked
|
@@ -113,6 +123,15 @@ module Karafka
|
|
113
123
|
def manual_pause?
|
114
124
|
@pause_tracker.paused? && @manual_pause
|
115
125
|
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# @param consumer [Object] karafka consumer (normal or pro)
|
130
|
+
# @return [Karafka::Processing::Result] result object which we can use to indicate
|
131
|
+
# consumption processing state.
|
132
|
+
def consumption(consumer)
|
133
|
+
@consumptions[consumer] ||= Processing::Result.new
|
134
|
+
end
|
116
135
|
end
|
117
136
|
end
|
118
137
|
end
|
@@ -12,9 +12,11 @@ module Karafka
|
|
12
12
|
|
13
13
|
# @param _topic [String] topic name
|
14
14
|
# @param messages [Array<Karafka::Messages::Message>] karafka messages
|
15
|
+
# @param _coordinator [Karafka::Processing::Coordinator] processing coordinator that will
|
16
|
+
# be used with those messages
|
15
17
|
# @yieldparam [Integer] group id
|
16
18
|
# @yieldparam [Array<Karafka::Messages::Message>] karafka messages
|
17
|
-
def call(_topic, messages)
|
19
|
+
def call(_topic, messages, _coordinator)
|
18
20
|
yield(0, messages)
|
19
21
|
end
|
20
22
|
end
|
@@ -31,10 +31,9 @@ module Karafka
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# Mark job as successful
|
34
|
-
coordinator.
|
34
|
+
coordinator.success!(self)
|
35
35
|
rescue StandardError => e
|
36
|
-
|
37
|
-
coordinator.consumption(self).failure!(e)
|
36
|
+
coordinator.failure!(self, e)
|
38
37
|
|
39
38
|
# Re-raise so reported in the consumer
|
40
39
|
raise e
|
data/lib/karafka/setup/config.rb
CHANGED
@@ -16,7 +16,6 @@ module Karafka
|
|
16
16
|
|
17
17
|
# Defaults for kafka settings, that will be overwritten only if not present already
|
18
18
|
KAFKA_DEFAULTS = {
|
19
|
-
'client.id': 'karafka',
|
20
19
|
# We emit the statistics by default, so all the instrumentation and web-ui work out of
|
21
20
|
# the box, without requiring users to take any extra actions aside from enabling.
|
22
21
|
'statistics.interval.ms': 5_000
|
@@ -193,6 +192,9 @@ module Karafka
|
|
193
192
|
config.kafka[key] = value
|
194
193
|
end
|
195
194
|
|
195
|
+
# Use Karafka client_id as kafka client id if not set
|
196
|
+
config.kafka[:'client.id'] ||= config.client_id
|
197
|
+
|
196
198
|
return if Karafka::App.env.production?
|
197
199
|
|
198
200
|
KAFKA_DEV_DEFAULTS.each do |key, value|
|