karafka 2.0.0 → 2.1.0
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/FUNDING.yml +1 -0
- data/.github/workflows/ci.yml +40 -16
- data/.rspec +4 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +459 -796
- data/Gemfile +3 -2
- data/Gemfile.lock +44 -32
- data/LICENSE +1 -1
- data/README.md +22 -17
- data/bin/integrations +86 -26
- data/bin/karafka +1 -13
- data/bin/rspecs +6 -0
- data/bin/verify_license_integrity +37 -0
- data/certs/cert_chain.pem +26 -0
- data/config/{errors.yml → locales/errors.yml} +34 -6
- data/config/locales/pro_errors.yml +34 -0
- data/docker-compose.yml +4 -30
- data/karafka.gemspec +17 -10
- data/lib/active_job/karafka.rb +3 -7
- data/lib/active_job/queue_adapters/karafka_adapter.rb +7 -1
- data/lib/karafka/active_job/consumer.rb +29 -6
- data/lib/karafka/active_job/current_attributes/loading.rb +36 -0
- data/lib/karafka/active_job/current_attributes/persistence.rb +28 -0
- data/lib/karafka/active_job/current_attributes.rb +42 -0
- data/lib/karafka/active_job/dispatcher.rb +34 -3
- data/lib/karafka/active_job/job_options_contract.rb +13 -2
- data/lib/karafka/admin.rb +246 -0
- data/lib/karafka/app.rb +30 -7
- data/lib/karafka/base_consumer.rb +124 -82
- data/lib/karafka/cli/base.rb +20 -0
- data/lib/karafka/cli/console.rb +13 -8
- data/lib/karafka/cli/info.rb +3 -2
- data/lib/karafka/cli/server.rb +55 -8
- data/lib/karafka/cli/topics.rb +146 -0
- data/lib/karafka/connection/client.rb +118 -61
- data/lib/karafka/connection/consumer_group_coordinator.rb +48 -0
- data/lib/karafka/connection/listener.rb +78 -30
- data/lib/karafka/connection/listeners_batch.rb +18 -2
- data/lib/karafka/connection/rebalance_manager.rb +3 -0
- data/lib/karafka/contracts/config.rb +1 -1
- data/lib/karafka/contracts/consumer_group.rb +22 -9
- data/lib/karafka/contracts/server_cli_options.rb +71 -11
- data/lib/karafka/contracts/topic.rb +65 -0
- data/lib/karafka/contracts.rb +1 -1
- data/lib/karafka/embedded.rb +36 -0
- data/lib/karafka/errors.rb +6 -4
- data/lib/karafka/helpers/colorize.rb +6 -0
- data/lib/karafka/instrumentation/callbacks/error.rb +3 -4
- data/lib/karafka/instrumentation/callbacks/statistics.rb +14 -4
- data/lib/karafka/instrumentation/logger_listener.rb +151 -22
- data/lib/karafka/instrumentation/notifications.rb +26 -7
- data/lib/karafka/instrumentation/proctitle_listener.rb +7 -16
- data/lib/karafka/instrumentation/vendors/datadog/dashboard.json +1 -1
- data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +153 -0
- data/lib/karafka/instrumentation/vendors/datadog/{listener.rb → metrics_listener.rb} +62 -35
- data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +166 -0
- data/lib/karafka/licenser.rb +53 -50
- data/lib/karafka/messages/builders/batch_metadata.rb +8 -8
- data/lib/karafka/messages/builders/messages.rb +6 -2
- data/lib/karafka/messages/message.rb +14 -2
- data/lib/karafka/messages/messages.rb +5 -0
- data/lib/karafka/messages/parser.rb +14 -0
- data/lib/karafka/patches/rdkafka/bindings.rb +132 -0
- data/lib/karafka/pro/active_job/consumer.rb +10 -9
- data/lib/karafka/pro/active_job/dispatcher.rb +28 -3
- data/lib/karafka/pro/active_job/job_options_contract.rb +16 -3
- data/lib/karafka/pro/encryption/cipher.rb +58 -0
- data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
- data/lib/karafka/pro/encryption/errors.rb +24 -0
- data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
- data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
- data/lib/karafka/pro/encryption/setup/config.rb +48 -0
- data/lib/karafka/pro/encryption.rb +47 -0
- data/lib/karafka/pro/iterator.rb +253 -0
- data/lib/karafka/pro/loader.rb +52 -26
- data/lib/karafka/pro/performance_tracker.rb +10 -6
- data/lib/karafka/pro/processing/collapser.rb +62 -0
- data/lib/karafka/pro/processing/coordinator.rb +92 -16
- data/lib/karafka/pro/processing/filters/base.rb +61 -0
- data/lib/karafka/pro/processing/filters/delayer.rb +70 -0
- data/lib/karafka/pro/processing/filters/expirer.rb +51 -0
- data/lib/karafka/pro/processing/filters/throttler.rb +84 -0
- data/lib/karafka/pro/processing/filters/virtual_limiter.rb +52 -0
- data/lib/karafka/pro/processing/filters_applier.rb +104 -0
- data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +7 -5
- data/lib/karafka/pro/processing/jobs/revoked_non_blocking.rb +37 -0
- data/lib/karafka/pro/processing/jobs_builder.rb +22 -4
- data/lib/karafka/pro/processing/partitioner.rb +33 -24
- data/lib/karafka/pro/processing/scheduler.rb +27 -8
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom.rb +68 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom_vp.rb +74 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom.rb +72 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom_vp.rb +76 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom_vp.rb +68 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom_vp.rb +69 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom.rb +38 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom_vp.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_mom.rb +38 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_mom_vp.rb +58 -0
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom.rb +37 -0
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +79 -0
- data/lib/karafka/pro/processing/strategies/aj/mom.rb +36 -0
- data/lib/karafka/pro/processing/strategies/aj/mom_vp.rb +52 -0
- data/lib/karafka/pro/processing/strategies/base.rb +26 -0
- data/lib/karafka/pro/processing/strategies/default.rb +105 -0
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +131 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr.rb +61 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj.rb +75 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +71 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom_vp.rb +43 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_vp.rb +41 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +69 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom_vp.rb +41 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +63 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +63 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom_vp.rb +36 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_vp.rb +39 -0
- data/lib/karafka/pro/processing/strategies/dlq/mom.rb +68 -0
- data/lib/karafka/pro/processing/strategies/dlq/mom_vp.rb +37 -0
- data/lib/karafka/pro/processing/strategies/dlq/vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/ftr/default.rb +104 -0
- data/lib/karafka/pro/processing/strategies/ftr/vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/lrj/default.rb +87 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr.rb +69 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_mom.rb +67 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_mom_vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_vp.rb +39 -0
- data/lib/karafka/pro/processing/strategies/lrj/mom.rb +80 -0
- data/lib/karafka/pro/processing/strategies/lrj/mom_vp.rb +38 -0
- data/lib/karafka/pro/processing/strategies/lrj/vp.rb +36 -0
- data/lib/karafka/pro/processing/strategies/mom/default.rb +46 -0
- data/lib/karafka/pro/processing/strategies/mom/ftr.rb +53 -0
- data/lib/karafka/pro/processing/strategies/mom/ftr_vp.rb +37 -0
- data/lib/karafka/pro/processing/strategies/mom/vp.rb +35 -0
- data/lib/karafka/pro/processing/strategies/vp/default.rb +104 -0
- data/lib/karafka/pro/{contracts/base.rb → processing/strategies.rb} +6 -5
- data/lib/karafka/pro/processing/strategy_selector.rb +84 -0
- data/lib/karafka/pro/processing/virtual_offset_manager.rb +147 -0
- data/lib/karafka/pro/routing/features/base.rb +24 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +50 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue.rb +27 -0
- data/lib/karafka/pro/routing/{builder_extensions.rb → features/delaying/config.rb} +8 -11
- data/lib/karafka/pro/routing/features/delaying/contract.rb +38 -0
- data/lib/karafka/pro/routing/features/delaying/topic.rb +59 -0
- data/lib/karafka/pro/routing/features/delaying.rb +29 -0
- data/lib/karafka/pro/routing/features/expiring/config.rb +27 -0
- data/lib/karafka/pro/routing/features/expiring/contract.rb +38 -0
- data/lib/karafka/pro/routing/features/expiring/topic.rb +59 -0
- data/lib/karafka/pro/routing/features/expiring.rb +27 -0
- data/lib/karafka/pro/routing/features/filtering/config.rb +40 -0
- data/lib/karafka/pro/routing/features/filtering/contract.rb +41 -0
- data/lib/karafka/pro/routing/features/filtering/topic.rb +51 -0
- data/lib/karafka/pro/routing/features/filtering.rb +27 -0
- data/lib/karafka/pro/routing/features/long_running_job/config.rb +28 -0
- data/lib/karafka/pro/routing/features/long_running_job/contract.rb +37 -0
- data/lib/karafka/pro/routing/features/long_running_job/topic.rb +42 -0
- data/lib/karafka/pro/routing/features/long_running_job.rb +28 -0
- data/lib/karafka/pro/routing/features/throttling/config.rb +32 -0
- data/lib/karafka/pro/routing/features/throttling/contract.rb +41 -0
- data/lib/karafka/pro/routing/features/throttling/topic.rb +69 -0
- data/lib/karafka/pro/routing/features/throttling.rb +30 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/config.rb +30 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +52 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/topic.rb +56 -0
- data/lib/karafka/pro/routing/features/virtual_partitions.rb +27 -0
- data/lib/karafka/process.rb +23 -8
- data/lib/karafka/processing/coordinator.rb +102 -23
- data/lib/karafka/processing/coordinators_buffer.rb +5 -1
- data/lib/karafka/processing/executor.rb +51 -21
- data/lib/karafka/processing/executors_buffer.rb +10 -26
- data/lib/karafka/processing/jobs/base.rb +6 -2
- data/lib/karafka/processing/jobs/consume.rb +8 -5
- data/lib/karafka/processing/jobs/idle.rb +24 -0
- data/lib/karafka/processing/jobs_builder.rb +2 -3
- data/lib/karafka/processing/jobs_queue.rb +14 -8
- data/lib/karafka/processing/partitioner.rb +3 -1
- data/lib/karafka/processing/result.rb +14 -1
- data/lib/karafka/processing/strategies/aj_dlq_mom.rb +44 -0
- data/lib/karafka/processing/strategies/aj_mom.rb +21 -0
- data/lib/karafka/processing/strategies/base.rb +52 -0
- data/lib/karafka/processing/strategies/default.rb +142 -0
- data/lib/karafka/processing/strategies/dlq.rb +88 -0
- data/lib/karafka/processing/strategies/dlq_mom.rb +49 -0
- data/lib/karafka/processing/strategies/mom.rb +29 -0
- data/lib/karafka/processing/strategy_selector.rb +47 -0
- data/lib/karafka/processing/worker.rb +8 -3
- data/lib/karafka/railtie.rb +24 -12
- data/lib/karafka/routing/activity_manager.rb +84 -0
- data/lib/karafka/routing/builder.rb +20 -0
- data/lib/karafka/routing/consumer_group.rb +38 -4
- data/lib/karafka/routing/consumer_mapper.rb +0 -10
- data/lib/karafka/routing/features/active_job/builder.rb +33 -0
- data/lib/karafka/routing/features/active_job/config.rb +15 -0
- data/lib/karafka/routing/features/active_job/contract.rb +41 -0
- data/lib/karafka/routing/features/active_job/topic.rb +33 -0
- data/lib/karafka/routing/features/active_job.rb +13 -0
- data/lib/karafka/routing/features/base/expander.rb +53 -0
- data/lib/karafka/routing/features/base.rb +34 -0
- data/lib/karafka/routing/features/dead_letter_queue/config.rb +19 -0
- data/lib/karafka/routing/features/dead_letter_queue/contract.rb +42 -0
- data/lib/karafka/routing/features/dead_letter_queue/topic.rb +41 -0
- data/lib/karafka/routing/features/dead_letter_queue.rb +16 -0
- data/lib/karafka/routing/features/declaratives/config.rb +18 -0
- data/lib/karafka/routing/features/declaratives/contract.rb +30 -0
- data/lib/karafka/routing/features/declaratives/topic.rb +44 -0
- data/lib/karafka/routing/features/declaratives.rb +14 -0
- data/lib/karafka/routing/features/manual_offset_management/config.rb +15 -0
- data/lib/karafka/routing/features/manual_offset_management/contract.rb +24 -0
- data/lib/karafka/routing/features/manual_offset_management/topic.rb +35 -0
- data/lib/karafka/routing/features/manual_offset_management.rb +18 -0
- data/lib/karafka/routing/proxy.rb +18 -20
- data/lib/karafka/routing/router.rb +27 -2
- data/lib/karafka/routing/subscription_group.rb +43 -5
- data/lib/karafka/routing/subscription_groups_builder.rb +6 -1
- data/lib/karafka/routing/topic.rb +17 -5
- data/lib/karafka/routing/topics.rb +8 -0
- data/lib/karafka/runner.rb +1 -0
- data/lib/karafka/serialization/json/deserializer.rb +1 -0
- data/lib/karafka/server.rb +58 -28
- data/lib/karafka/setup/attributes_map.rb +344 -0
- data/lib/karafka/setup/config.rb +48 -19
- data/lib/karafka/status.rb +35 -7
- data/lib/karafka/templates/karafka.rb.erb +14 -6
- data/lib/karafka/time_trackers/base.rb +1 -6
- data/lib/karafka/time_trackers/pause.rb +49 -19
- data/lib/karafka/time_trackers/poll.rb +7 -3
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +56 -3
- data/renovate.json +6 -0
- data.tar.gz.sig +0 -0
- metadata +189 -61
- metadata.gz.sig +0 -0
- data/bin/wait_for_kafka +0 -20
- data/certs/mensfeld.pem +0 -25
- data/lib/karafka/active_job/routing/extensions.rb +0 -31
- data/lib/karafka/contracts/consumer_group_topic.rb +0 -42
- data/lib/karafka/instrumentation.rb +0 -21
- data/lib/karafka/pro/base_consumer.rb +0 -82
- data/lib/karafka/pro/contracts/consumer_group.rb +0 -34
- data/lib/karafka/pro/contracts/consumer_group_topic.rb +0 -33
- data/lib/karafka/pro/routing/topic_extensions.rb +0 -38
|
@@ -10,60 +10,105 @@ module Karafka
|
|
|
10
10
|
# listener thread, but we go with thread-safe by default for all not to worry about potential
|
|
11
11
|
# future mistakes.
|
|
12
12
|
class Coordinator
|
|
13
|
-
|
|
14
|
-
attr_reader :pause_tracker
|
|
13
|
+
attr_reader :pause_tracker, :seek_offset, :topic, :partition
|
|
15
14
|
|
|
15
|
+
# @param topic [Karafka::Routing::Topic]
|
|
16
|
+
# @param partition [Integer]
|
|
16
17
|
# @param pause_tracker [Karafka::TimeTrackers::Pause] pause tracker for given topic partition
|
|
17
|
-
def initialize(pause_tracker)
|
|
18
|
+
def initialize(topic, partition, pause_tracker)
|
|
19
|
+
@topic = topic
|
|
20
|
+
@partition = partition
|
|
18
21
|
@pause_tracker = pause_tracker
|
|
19
22
|
@revoked = false
|
|
20
23
|
@consumptions = {}
|
|
21
24
|
@running_jobs = 0
|
|
25
|
+
@manual_pause = false
|
|
22
26
|
@mutex = Mutex.new
|
|
27
|
+
@marked = false
|
|
28
|
+
@failure = false
|
|
23
29
|
end
|
|
24
30
|
|
|
25
31
|
# Starts the coordinator for given consumption jobs
|
|
26
|
-
# @param
|
|
32
|
+
# @param messages [Array<Karafka::Messages::Message>] batch of message for which we are
|
|
27
33
|
# going to coordinate work. Not used with regular coordinator.
|
|
28
|
-
def start(
|
|
29
|
-
@
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
def start(messages)
|
|
35
|
+
@failure = false
|
|
36
|
+
@running_jobs = 0
|
|
37
|
+
# We need to clear the consumption results hash here, otherwise we could end up storing
|
|
38
|
+
# consumption results of consumer instances we no longer control
|
|
39
|
+
@consumptions.clear
|
|
40
|
+
|
|
41
|
+
# When starting to run, no pause is expected and no manual pause as well
|
|
42
|
+
@manual_pause = false
|
|
43
|
+
|
|
44
|
+
# We set it on the first encounter and never again, because then the offset setting
|
|
45
|
+
# should be up to the consumers logic (our or the end user)
|
|
46
|
+
# Seek offset needs to be always initialized as for case where manual offset management
|
|
47
|
+
# is turned on, we need to have reference to the first offset even in case of running
|
|
48
|
+
# multiple batches without marking any messages as consumed. Rollback needs to happen to
|
|
49
|
+
# the last place we know of or the last message + 1 that was marked
|
|
50
|
+
#
|
|
51
|
+
# It is however worth keeping in mind, that this may need to be used with `#marked?` to
|
|
52
|
+
# make sure that the first offset is an offset that has been marked.
|
|
53
|
+
@seek_offset ||= messages.first.offset
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @param offset [Integer] message offset
|
|
57
|
+
def seek_offset=(offset)
|
|
58
|
+
synchronize do
|
|
59
|
+
@marked = true
|
|
60
|
+
@seek_offset = offset
|
|
34
61
|
end
|
|
35
62
|
end
|
|
36
63
|
|
|
37
64
|
# Increases number of jobs that we handle with this coordinator
|
|
38
65
|
def increment
|
|
39
|
-
|
|
66
|
+
synchronize { @running_jobs += 1 }
|
|
40
67
|
end
|
|
41
68
|
|
|
42
69
|
# Decrements number of jobs we handle at the moment
|
|
43
70
|
def decrement
|
|
44
|
-
|
|
71
|
+
synchronize do
|
|
45
72
|
@running_jobs -= 1
|
|
46
73
|
|
|
47
74
|
return @running_jobs unless @running_jobs.negative?
|
|
48
75
|
|
|
49
76
|
# This should never happen. If it does, something is heavily out of sync. Please reach
|
|
50
77
|
# out to us if you encounter this
|
|
51
|
-
raise Karafka::Errors::
|
|
78
|
+
raise Karafka::Errors::InvalidCoordinatorStateError, 'Was zero before decrementation'
|
|
52
79
|
end
|
|
53
80
|
end
|
|
54
81
|
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
@
|
|
82
|
+
# Is all the consumption done and finished successfully for this coordinator
|
|
83
|
+
# We do not say we're successful until all work is done, because running work may still
|
|
84
|
+
# crash.
|
|
85
|
+
def success?
|
|
86
|
+
synchronize do
|
|
87
|
+
@running_jobs.zero? && @consumptions.values.all?(&:success?)
|
|
61
88
|
end
|
|
62
89
|
end
|
|
63
90
|
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
91
|
+
# Mark given consumption on consumer as successful
|
|
92
|
+
# @param consumer [Karafka::BaseConsumer] consumer that finished successfully
|
|
93
|
+
def success!(consumer)
|
|
94
|
+
synchronize do
|
|
95
|
+
consumption(consumer).success!
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Mark given consumption on consumer as failed
|
|
100
|
+
# @param consumer [Karafka::BaseConsumer] consumer that failed
|
|
101
|
+
# @param error [StandardError] error that occurred
|
|
102
|
+
def failure!(consumer, error)
|
|
103
|
+
synchronize do
|
|
104
|
+
@failure = true
|
|
105
|
+
consumption(consumer).failure!(error)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @return [Boolean] true if any of work we were running failed
|
|
110
|
+
def failure?
|
|
111
|
+
@failure
|
|
67
112
|
end
|
|
68
113
|
|
|
69
114
|
# Marks given coordinator for processing group as revoked
|
|
@@ -76,13 +121,47 @@ module Karafka
|
|
|
76
121
|
# listener loop dispatching the revocation job. It is ok, as effectively nothing will be
|
|
77
122
|
# processed until revocation jobs are done.
|
|
78
123
|
def revoke
|
|
79
|
-
|
|
124
|
+
synchronize { @revoked = true }
|
|
80
125
|
end
|
|
81
126
|
|
|
82
127
|
# @return [Boolean] is the partition we are processing revoked or not
|
|
83
128
|
def revoked?
|
|
84
129
|
@revoked
|
|
85
130
|
end
|
|
131
|
+
|
|
132
|
+
# @return [Boolean] was the new seek offset assigned at least once. This is needed because
|
|
133
|
+
# by default we assign seek offset of a first message ever, however this is insufficient
|
|
134
|
+
# for DLQ in a scenario where the first message would be broken. We would never move
|
|
135
|
+
# out of it and would end up in an endless loop.
|
|
136
|
+
def marked?
|
|
137
|
+
@marked
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Store in the coordinator info, that this pause was done manually by the end user and not
|
|
141
|
+
# by the system itself
|
|
142
|
+
def manual_pause
|
|
143
|
+
@manual_pause = true
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @return [Boolean] are we in a pause that was initiated by the user
|
|
147
|
+
def manual_pause?
|
|
148
|
+
@pause_tracker.paused? && @manual_pause
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Allows to run synchronized (locked) code that can operate in between virtual partitions
|
|
152
|
+
# @param block [Proc] code we want to run in the synchronized mode
|
|
153
|
+
def synchronize(&block)
|
|
154
|
+
@mutex.synchronize(&block)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
# @param consumer [Object] karafka consumer (normal or pro)
|
|
160
|
+
# @return [Karafka::Processing::Result] result object which we can use to indicate
|
|
161
|
+
# consumption processing state.
|
|
162
|
+
def consumption(consumer)
|
|
163
|
+
@consumptions[consumer] ||= Processing::Result.new
|
|
164
|
+
end
|
|
86
165
|
end
|
|
87
166
|
end
|
|
88
167
|
end
|
|
@@ -9,16 +9,20 @@ module Karafka
|
|
|
9
9
|
# @note This buffer operates only from the listener loop, thus we do not have to make it
|
|
10
10
|
# thread-safe.
|
|
11
11
|
class CoordinatorsBuffer
|
|
12
|
-
|
|
12
|
+
# @param topics [Karafka::Routing::Topics]
|
|
13
|
+
def initialize(topics)
|
|
13
14
|
@pauses_manager = Connection::PausesManager.new
|
|
14
15
|
@coordinator_class = ::Karafka::App.config.internal.processing.coordinator_class
|
|
15
16
|
@coordinators = Hash.new { |h, k| h[k] = {} }
|
|
17
|
+
@topics = topics
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
# @param topic [String] topic name
|
|
19
21
|
# @param partition [Integer] partition number
|
|
20
22
|
def find_or_create(topic, partition)
|
|
21
23
|
@coordinators[topic][partition] ||= @coordinator_class.new(
|
|
24
|
+
@topics.find(topic),
|
|
25
|
+
partition,
|
|
22
26
|
@pauses_manager.fetch(topic, partition)
|
|
23
27
|
)
|
|
24
28
|
end
|
|
@@ -12,6 +12,10 @@ module Karafka
|
|
|
12
12
|
# @note Executors are not removed after partition is revoked. They are not that big and will
|
|
13
13
|
# be re-used in case of a re-claim
|
|
14
14
|
class Executor
|
|
15
|
+
extend Forwardable
|
|
16
|
+
|
|
17
|
+
def_delegators :@coordinator, :topic, :partition
|
|
18
|
+
|
|
15
19
|
# @return [String] unique id that we use to ensure, that we use for state tracking
|
|
16
20
|
attr_reader :id
|
|
17
21
|
|
|
@@ -21,45 +25,46 @@ module Karafka
|
|
|
21
25
|
# @return [Karafka::Messages::Messages] messages batch
|
|
22
26
|
attr_reader :messages
|
|
23
27
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
# @return [Karafka::Routing::Topic] topic of this executor
|
|
28
|
-
attr_reader :topic
|
|
28
|
+
# @return [Karafka::Processing::Coordinator] coordinator for this executor
|
|
29
|
+
attr_reader :coordinator
|
|
29
30
|
|
|
30
31
|
# @param group_id [String] id of the subscription group to which the executor belongs
|
|
31
32
|
# @param client [Karafka::Connection::Client] kafka client
|
|
32
|
-
# @param
|
|
33
|
-
def initialize(group_id, client,
|
|
34
|
-
@id = SecureRandom.
|
|
33
|
+
# @param coordinator [Karafka::Processing::Coordinator]
|
|
34
|
+
def initialize(group_id, client, coordinator)
|
|
35
|
+
@id = SecureRandom.hex(6)
|
|
35
36
|
@group_id = group_id
|
|
36
37
|
@client = client
|
|
37
|
-
@
|
|
38
|
+
@coordinator = coordinator
|
|
38
39
|
end
|
|
39
40
|
|
|
40
|
-
#
|
|
41
|
-
#
|
|
41
|
+
# Allows us to prepare the consumer in the listener thread prior to the job being send to
|
|
42
|
+
# the queue. It also allows to run some code that is time sensitive and cannot wait in the
|
|
43
|
+
# queue as it could cause starvation.
|
|
42
44
|
#
|
|
43
45
|
# @param messages [Array<Karafka::Messages::Message>]
|
|
44
|
-
|
|
45
|
-
# enqueued it, but good enough
|
|
46
|
-
# @param coordinator [Karafka::Processing::Coordinator] coordinator for processing management
|
|
47
|
-
def before_consume(messages, received_at, coordinator)
|
|
46
|
+
def before_enqueue(messages)
|
|
48
47
|
# Recreate consumer with each batch if persistence is not enabled
|
|
49
48
|
# We reload the consumers with each batch instead of relying on some external signals
|
|
50
49
|
# when needed for consistency. That way devs may have it on or off and not in this
|
|
51
50
|
# middle state, where re-creation of a consumer instance would occur only sometimes
|
|
52
51
|
@consumer = nil unless ::Karafka::App.config.consumer_persistence
|
|
53
52
|
|
|
54
|
-
consumer.coordinator = coordinator
|
|
55
|
-
|
|
56
53
|
# First we build messages batch...
|
|
57
54
|
consumer.messages = Messages::Builders::Messages.call(
|
|
58
55
|
messages,
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
topic,
|
|
57
|
+
partition,
|
|
58
|
+
# the moment we've received the batch or actually the moment we've enqueued it,
|
|
59
|
+
# but good enough
|
|
60
|
+
Time.now
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
+
consumer.on_before_enqueue
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Runs setup and warm-up code in the worker prior to running the consumption
|
|
67
|
+
def before_consume
|
|
63
68
|
consumer.on_before_consume
|
|
64
69
|
end
|
|
65
70
|
|
|
@@ -74,6 +79,23 @@ module Karafka
|
|
|
74
79
|
consumer.on_after_consume
|
|
75
80
|
end
|
|
76
81
|
|
|
82
|
+
# Runs consumer idle operations
|
|
83
|
+
# This may include house-keeping or other state management changes that can occur but that
|
|
84
|
+
# not mean there are any new messages available for the end user to process
|
|
85
|
+
def idle
|
|
86
|
+
# Initializes the messages set in case idle operation would happen before any processing
|
|
87
|
+
# This prevents us from having no messages object at all as the messages object and
|
|
88
|
+
# its metadata may be used for statistics
|
|
89
|
+
consumer.messages ||= Messages::Builders::Messages.call(
|
|
90
|
+
[],
|
|
91
|
+
topic,
|
|
92
|
+
partition,
|
|
93
|
+
Time.now
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
consumer.on_idle
|
|
97
|
+
end
|
|
98
|
+
|
|
77
99
|
# Runs the controller `#revoked` method that should be triggered when a given consumer is
|
|
78
100
|
# no longer needed due to partitions reassignment.
|
|
79
101
|
#
|
|
@@ -106,10 +128,18 @@ module Karafka
|
|
|
106
128
|
# @return [Object] cached consumer instance
|
|
107
129
|
def consumer
|
|
108
130
|
@consumer ||= begin
|
|
109
|
-
|
|
110
|
-
|
|
131
|
+
topic = @coordinator.topic
|
|
132
|
+
|
|
133
|
+
strategy = ::Karafka::App.config.internal.processing.strategy_selector.find(topic)
|
|
134
|
+
|
|
135
|
+
consumer = topic.consumer_class.new
|
|
136
|
+
# We use singleton class as the same consumer class may be used to process different
|
|
137
|
+
# topics with different settings
|
|
138
|
+
consumer.singleton_class.include(strategy)
|
|
111
139
|
consumer.client = @client
|
|
112
140
|
consumer.producer = ::Karafka::App.producer
|
|
141
|
+
consumer.coordinator = @coordinator
|
|
142
|
+
|
|
113
143
|
consumer
|
|
114
144
|
end
|
|
115
145
|
end
|
|
@@ -20,14 +20,13 @@ module Karafka
|
|
|
20
20
|
# @param topic [String] topic name
|
|
21
21
|
# @param partition [Integer] partition number
|
|
22
22
|
# @param parallel_key [String] parallel group key
|
|
23
|
+
# @param coordinator [Karafka::Processing::Coordinator]
|
|
23
24
|
# @return [Executor] consumer executor
|
|
24
|
-
def find_or_create(topic, partition, parallel_key)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@buffer[ktopic][partition][parallel_key] ||= Executor.new(
|
|
25
|
+
def find_or_create(topic, partition, parallel_key, coordinator)
|
|
26
|
+
@buffer[topic][partition][parallel_key] ||= Executor.new(
|
|
28
27
|
@subscription_group.id,
|
|
29
28
|
@client,
|
|
30
|
-
|
|
29
|
+
coordinator
|
|
31
30
|
)
|
|
32
31
|
end
|
|
33
32
|
|
|
@@ -37,9 +36,7 @@ module Karafka
|
|
|
37
36
|
# @param topic [String] topic name
|
|
38
37
|
# @param partition [Integer] partition number
|
|
39
38
|
def revoke(topic, partition)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@buffer[ktopic][partition].clear
|
|
39
|
+
@buffer[topic][partition].clear
|
|
43
40
|
end
|
|
44
41
|
|
|
45
42
|
# Finds all the executors available for a given topic partition
|
|
@@ -48,9 +45,7 @@ module Karafka
|
|
|
48
45
|
# @param partition [Integer] partition number
|
|
49
46
|
# @return [Array<Executor>] executors in use for this topic + partition
|
|
50
47
|
def find_all(topic, partition)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@buffer[ktopic][partition].values
|
|
48
|
+
@buffer[topic][partition].values
|
|
54
49
|
end
|
|
55
50
|
|
|
56
51
|
# Iterates over all available executors and yields them together with topic and partition
|
|
@@ -59,11 +54,10 @@ module Karafka
|
|
|
59
54
|
# @yieldparam [Integer] partition number
|
|
60
55
|
# @yieldparam [Executor] given executor
|
|
61
56
|
def each
|
|
62
|
-
@buffer.each do |
|
|
63
|
-
partitions.each do |
|
|
64
|
-
executors.each do |
|
|
65
|
-
|
|
66
|
-
yield(ktopic, partition, executor)
|
|
57
|
+
@buffer.each do |_, partitions|
|
|
58
|
+
partitions.each do |_, executors|
|
|
59
|
+
executors.each do |_, executor|
|
|
60
|
+
yield(executor)
|
|
67
61
|
end
|
|
68
62
|
end
|
|
69
63
|
end
|
|
@@ -73,16 +67,6 @@ module Karafka
|
|
|
73
67
|
def clear
|
|
74
68
|
@buffer.clear
|
|
75
69
|
end
|
|
76
|
-
|
|
77
|
-
private
|
|
78
|
-
|
|
79
|
-
# Finds topic based on its name
|
|
80
|
-
#
|
|
81
|
-
# @param topic [String] topic we're looking for
|
|
82
|
-
# @return [Karafka::Routing::Topic] topic we're interested in
|
|
83
|
-
def find_topic(topic)
|
|
84
|
-
@subscription_group.topics.find(topic) || raise(Errors::TopicNotFoundError, topic)
|
|
85
|
-
end
|
|
86
70
|
end
|
|
87
71
|
end
|
|
88
72
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Karafka
|
|
4
4
|
module Processing
|
|
5
|
-
# Namespace for all the jobs that are
|
|
5
|
+
# Namespace for all the jobs that are supposed to run in workers.
|
|
6
6
|
module Jobs
|
|
7
7
|
# Base class for all the jobs types that are suppose to run in workers threads.
|
|
8
8
|
# Each job can have 3 main entry-points: `#before_call`, `#call` and `#after_call`
|
|
@@ -10,7 +10,7 @@ module Karafka
|
|
|
10
10
|
class Base
|
|
11
11
|
extend Forwardable
|
|
12
12
|
|
|
13
|
-
# @note Since one job has always one
|
|
13
|
+
# @note Since one job has always one executor, we use the jobs id and group id as reference
|
|
14
14
|
def_delegators :executor, :id, :group_id
|
|
15
15
|
|
|
16
16
|
attr_reader :executor
|
|
@@ -22,6 +22,10 @@ module Karafka
|
|
|
22
22
|
@non_blocking = false
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# When redefined can run any code prior to the job being enqueued
|
|
26
|
+
# @note This will run in the listener thread and not in the worker
|
|
27
|
+
def before_enqueue; end
|
|
28
|
+
|
|
25
29
|
# When redefined can run any code that should run before executing the proper code
|
|
26
30
|
def before_call; end
|
|
27
31
|
|
|
@@ -12,19 +12,22 @@ module Karafka
|
|
|
12
12
|
# @param executor [Karafka::Processing::Executor] executor that is suppose to run a given
|
|
13
13
|
# job
|
|
14
14
|
# @param messages [Karafka::Messages::Messages] karafka messages batch
|
|
15
|
-
# @param coordinator [Karafka::Processing::Coordinator] processing coordinator
|
|
16
15
|
# @return [Consume]
|
|
17
|
-
def initialize(executor, messages
|
|
16
|
+
def initialize(executor, messages)
|
|
18
17
|
@executor = executor
|
|
19
18
|
@messages = messages
|
|
20
|
-
@coordinator = coordinator
|
|
21
|
-
@created_at = Time.now
|
|
22
19
|
super()
|
|
23
20
|
end
|
|
24
21
|
|
|
22
|
+
# Runs all the preparation code on the executor that needs to happen before the job is
|
|
23
|
+
# enqueued.
|
|
24
|
+
def before_enqueue
|
|
25
|
+
executor.before_enqueue(@messages)
|
|
26
|
+
end
|
|
27
|
+
|
|
25
28
|
# Runs the before consumption preparations on the executor
|
|
26
29
|
def before_call
|
|
27
|
-
executor.before_consume
|
|
30
|
+
executor.before_consume
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
# Runs the given executor
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Processing
|
|
5
|
+
module Jobs
|
|
6
|
+
# Type of job that we may use to run some extra handling that happens without the user
|
|
7
|
+
# related lifecycle event like consumption, revocation, etc.
|
|
8
|
+
class Idle < Base
|
|
9
|
+
# @param executor [Karafka::Processing::Executor] executor that is suppose to run a given
|
|
10
|
+
# job on an active consumer
|
|
11
|
+
# @return [Shutdown]
|
|
12
|
+
def initialize(executor)
|
|
13
|
+
@executor = executor
|
|
14
|
+
super()
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Run the idle work via the executor
|
|
18
|
+
def call
|
|
19
|
+
executor.idle
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -7,10 +7,9 @@ module Karafka
|
|
|
7
7
|
class JobsBuilder
|
|
8
8
|
# @param executor [Karafka::Processing::Executor]
|
|
9
9
|
# @param messages [Karafka::Messages::Messages] messages batch to be consumed
|
|
10
|
-
# @param coordinator [Karafka::Processing::Coordinator]
|
|
11
10
|
# @return [Karafka::Processing::Jobs::Consume] consumption job
|
|
12
|
-
def consume(executor, messages
|
|
13
|
-
Jobs::Consume.new(executor, messages
|
|
11
|
+
def consume(executor, messages)
|
|
12
|
+
Jobs::Consume.new(executor, messages)
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
# @param executor [Karafka::Processing::Executor]
|
|
@@ -13,15 +13,19 @@ module Karafka
|
|
|
13
13
|
# @return [Karafka::Processing::JobsQueue]
|
|
14
14
|
def initialize
|
|
15
15
|
@queue = Queue.new
|
|
16
|
-
# Those queues will act as
|
|
16
|
+
# Those queues will act as semaphores internally. Since we need an indicator for waiting
|
|
17
17
|
# we could use Thread.pass but this is expensive. Instead we can just lock until any
|
|
18
18
|
# of the workers finishes their work and we can re-check. This means that in the worse
|
|
19
19
|
# scenario, we will context switch 10 times per poll instead of getting this thread
|
|
20
20
|
# scheduled by Ruby hundreds of thousands of times per group.
|
|
21
21
|
# We cannot use a single semaphore as it could potentially block in listeners that should
|
|
22
22
|
# process with their data and also could unlock when a given group needs to remain locked
|
|
23
|
-
@semaphores =
|
|
23
|
+
@semaphores = Concurrent::Map.new do |h, k|
|
|
24
|
+
h.compute_if_absent(k) { Queue.new }
|
|
25
|
+
end
|
|
26
|
+
|
|
24
27
|
@in_processing = Hash.new { |h, k| h[k] = [] }
|
|
28
|
+
|
|
25
29
|
@mutex = Mutex.new
|
|
26
30
|
end
|
|
27
31
|
|
|
@@ -47,9 +51,9 @@ module Karafka
|
|
|
47
51
|
raise(Errors::JobsQueueSynchronizationError, job.group_id) if group.include?(job)
|
|
48
52
|
|
|
49
53
|
group << job
|
|
50
|
-
end
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
@queue << job
|
|
56
|
+
end
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
# @return [Jobs::Base, nil] waits for a job from the main queue and returns it once available
|
|
@@ -96,7 +100,7 @@ module Karafka
|
|
|
96
100
|
return if @queue.closed?
|
|
97
101
|
|
|
98
102
|
@queue.close
|
|
99
|
-
@semaphores.
|
|
103
|
+
@semaphores.each_value(&:close)
|
|
100
104
|
end
|
|
101
105
|
end
|
|
102
106
|
|
|
@@ -105,7 +109,9 @@ module Karafka
|
|
|
105
109
|
# @return [Boolean] tell us if we have anything in the processing (or for processing) from
|
|
106
110
|
# a given group.
|
|
107
111
|
def empty?(group_id)
|
|
108
|
-
@
|
|
112
|
+
@mutex.synchronize do
|
|
113
|
+
@in_processing[group_id].empty?
|
|
114
|
+
end
|
|
109
115
|
end
|
|
110
116
|
|
|
111
117
|
# Blocks when there are things in the queue in a given group and waits until all the blocking
|
|
@@ -119,13 +125,13 @@ module Karafka
|
|
|
119
125
|
@semaphores[group_id].pop while wait?(group_id)
|
|
120
126
|
end
|
|
121
127
|
|
|
122
|
-
# - `
|
|
128
|
+
# - `busy` - number of jobs that are currently being processed (active work)
|
|
123
129
|
# - `enqueued` - number of jobs in the queue that are waiting to be picked up by a worker
|
|
124
130
|
#
|
|
125
131
|
# @return [Hash] hash with basic usage statistics of this queue.
|
|
126
132
|
def statistics
|
|
127
133
|
{
|
|
128
|
-
|
|
134
|
+
busy: size - @queue.size,
|
|
129
135
|
enqueued: @queue.size
|
|
130
136
|
}.freeze
|
|
131
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
|
|
@@ -6,8 +6,11 @@ module Karafka
|
|
|
6
6
|
# It allows to indicate if given thing moved from success to a failure or the other way around
|
|
7
7
|
# Useful for tracking consumption state
|
|
8
8
|
class Result
|
|
9
|
+
attr_reader :cause
|
|
10
|
+
|
|
9
11
|
def initialize
|
|
10
12
|
@success = true
|
|
13
|
+
@cause = false
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
# @return [Boolean]
|
|
@@ -18,11 +21,21 @@ module Karafka
|
|
|
18
21
|
# Marks state as successful
|
|
19
22
|
def success!
|
|
20
23
|
@success = true
|
|
24
|
+
# We set cause to false so the previous error that occurred does not leak when error is
|
|
25
|
+
# no longer present
|
|
26
|
+
@cause = false
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
# Marks state as failure
|
|
24
|
-
|
|
30
|
+
# @param cause [StandardError] error that occurred and caused failure
|
|
31
|
+
def failure!(cause)
|
|
25
32
|
@success = false
|
|
33
|
+
@cause = cause
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Boolean] true if processing failed
|
|
37
|
+
def failure?
|
|
38
|
+
!@success
|
|
26
39
|
end
|
|
27
40
|
end
|
|
28
41
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Processing
|
|
5
|
+
module Strategies
|
|
6
|
+
# ActiveJob strategy to cooperate with the DLQ.
|
|
7
|
+
#
|
|
8
|
+
# While AJ is uses MOM by default because it delegates the offset management to the AJ
|
|
9
|
+
# consumer. With DLQ however there is an extra case for skipping broken jobs with offset
|
|
10
|
+
# marking due to ordered processing.
|
|
11
|
+
module AjDlqMom
|
|
12
|
+
include DlqMom
|
|
13
|
+
|
|
14
|
+
# Apply strategy when only when using AJ with MOM and DLQ
|
|
15
|
+
FEATURES = %i[
|
|
16
|
+
active_job
|
|
17
|
+
dead_letter_queue
|
|
18
|
+
manual_offset_management
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
# How should we post-finalize consumption.
|
|
22
|
+
def handle_after_consume
|
|
23
|
+
return if revoked?
|
|
24
|
+
|
|
25
|
+
if coordinator.success?
|
|
26
|
+
# Do NOT commit offsets, they are comitted after each job in the AJ consumer.
|
|
27
|
+
coordinator.pause_tracker.reset
|
|
28
|
+
elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
|
|
29
|
+
retry_after_pause
|
|
30
|
+
else
|
|
31
|
+
coordinator.pause_tracker.reset
|
|
32
|
+
skippable_message, = find_skippable_message
|
|
33
|
+
dispatch_to_dlq(skippable_message)
|
|
34
|
+
# We can commit the offset here because we know that we skip it "forever" and
|
|
35
|
+
# since AJ consumer commits the offset after each job, we also know that the
|
|
36
|
+
# previous job was successful
|
|
37
|
+
mark_as_consumed(skippable_message)
|
|
38
|
+
pause(coordinator.seek_offset, nil, false)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Processing
|
|
5
|
+
module Strategies
|
|
6
|
+
# ActiveJob enabled
|
|
7
|
+
# Manual offset management enabled
|
|
8
|
+
#
|
|
9
|
+
# This is the default AJ strategy since AJ cannot be used without MOM
|
|
10
|
+
module AjMom
|
|
11
|
+
include Mom
|
|
12
|
+
|
|
13
|
+
# Apply strategy when only when using AJ with MOM
|
|
14
|
+
FEATURES = %i[
|
|
15
|
+
active_job
|
|
16
|
+
manual_offset_management
|
|
17
|
+
].freeze
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|