karafka 2.4.8 → 2.4.9

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +0 -1
  4. data/CHANGELOG.md +8 -0
  5. data/Gemfile +8 -5
  6. data/Gemfile.lock +23 -14
  7. data/bin/integrations +5 -0
  8. data/certs/cert.pem +26 -0
  9. data/config/locales/errors.yml +4 -0
  10. data/config/locales/pro_errors.yml +17 -0
  11. data/karafka.gemspec +1 -1
  12. data/lib/karafka/admin.rb +42 -0
  13. data/lib/karafka/contracts/config.rb +2 -0
  14. data/lib/karafka/errors.rb +3 -2
  15. data/lib/karafka/pro/loader.rb +2 -1
  16. data/lib/karafka/pro/processing/strategies/dlq/default.rb +16 -1
  17. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +5 -1
  18. data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +17 -1
  19. data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +17 -1
  20. data/lib/karafka/pro/processing/strategies/dlq/mom.rb +22 -6
  21. data/lib/karafka/pro/recurring_tasks/consumer.rb +105 -0
  22. data/lib/karafka/pro/recurring_tasks/contracts/config.rb +53 -0
  23. data/lib/karafka/pro/recurring_tasks/contracts/task.rb +41 -0
  24. data/lib/karafka/pro/recurring_tasks/deserializer.rb +35 -0
  25. data/lib/karafka/pro/recurring_tasks/dispatcher.rb +87 -0
  26. data/lib/karafka/pro/recurring_tasks/errors.rb +34 -0
  27. data/lib/karafka/pro/recurring_tasks/executor.rb +152 -0
  28. data/lib/karafka/pro/recurring_tasks/listener.rb +38 -0
  29. data/lib/karafka/pro/recurring_tasks/matcher.rb +38 -0
  30. data/lib/karafka/pro/recurring_tasks/schedule.rb +63 -0
  31. data/lib/karafka/pro/recurring_tasks/serializer.rb +113 -0
  32. data/lib/karafka/pro/recurring_tasks/setup/config.rb +52 -0
  33. data/lib/karafka/pro/recurring_tasks/task.rb +151 -0
  34. data/lib/karafka/pro/recurring_tasks.rb +87 -0
  35. data/lib/karafka/pro/routing/features/recurring_tasks/builder.rb +130 -0
  36. data/lib/karafka/pro/routing/features/recurring_tasks/config.rb +28 -0
  37. data/lib/karafka/pro/routing/features/recurring_tasks/contracts/topic.rb +40 -0
  38. data/lib/karafka/pro/routing/features/recurring_tasks/proxy.rb +27 -0
  39. data/lib/karafka/pro/routing/features/recurring_tasks/topic.rb +44 -0
  40. data/lib/karafka/pro/routing/features/recurring_tasks.rb +25 -0
  41. data/lib/karafka/processing/strategies/dlq.rb +16 -2
  42. data/lib/karafka/processing/strategies/dlq_mom.rb +25 -6
  43. data/lib/karafka/processing/worker.rb +11 -1
  44. data/lib/karafka/railtie.rb +11 -22
  45. data/lib/karafka/routing/features/dead_letter_queue/config.rb +3 -0
  46. data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +1 -0
  47. data/lib/karafka/routing/features/dead_letter_queue/topic.rb +7 -2
  48. data/lib/karafka/routing/features/eofed/contracts/topic.rb +12 -0
  49. data/lib/karafka/routing/topic.rb +14 -0
  50. data/lib/karafka/setup/config.rb +3 -0
  51. data/lib/karafka/version.rb +1 -1
  52. data.tar.gz.sig +0 -0
  53. metadata +44 -24
  54. metadata.gz.sig +0 -0
  55. data/certs/cert_chain.pem +0 -26
@@ -0,0 +1,151 @@
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 RecurringTasks
17
+ # Represents a single recurring task that can be executed when the time comes.
18
+ # Tasks should be lightweight. Anything heavy should be executed by scheduling appropriate
19
+ # jobs here.
20
+ class Task
21
+ include Helpers::ConfigImporter.new(
22
+ monitor: %i[monitor]
23
+ )
24
+
25
+ # @return [String] this task id
26
+ attr_reader :id
27
+
28
+ # @return [Fugit::Cron] cron from parsing the raw cron expression
29
+ attr_reader :cron
30
+
31
+ # Allows for update of previous time when restoring the materialized state
32
+ attr_accessor :previous_time
33
+
34
+ # @param id [String] unique id. If re-used between versions, will replace older occurrence.
35
+ # @param cron [String] cron expression matching this task expected execution times.
36
+ # @param previous_time [Integer, Time] previous time this task was executed. 0 if never.
37
+ # @param enabled [Boolean] should this task be enabled. Users may disable given task
38
+ # temporarily, this is why we need to know that.
39
+ # @param block [Proc] code to execute.
40
+ def initialize(id:, cron:, previous_time: 0, enabled: true, &block)
41
+ @id = id
42
+ @cron = ::Fugit::Cron.do_parse(cron)
43
+ @previous_time = previous_time
44
+ @start_time = Time.now
45
+ @executable = block
46
+ @enabled = enabled
47
+ @trigger = false
48
+ @changed = false
49
+ end
50
+
51
+ # @return [Boolean] true if anything in the task has changed and we should persist it
52
+ def changed?
53
+ @changed
54
+ end
55
+
56
+ # Disables this task execution indefinitely
57
+ def disable
58
+ touch
59
+ @enabled = false
60
+ end
61
+
62
+ # Enables back this task
63
+ def enable
64
+ touch
65
+ @enabled = true
66
+ end
67
+
68
+ # @return [Boolean] is this an executable task
69
+ def enabled?
70
+ @enabled
71
+ end
72
+
73
+ # Triggers the execution of this task at the earliest opportunity
74
+ def trigger
75
+ touch
76
+ @trigger = true
77
+ end
78
+
79
+ # @return [EtOrbi::EoTime] next execution time
80
+ def next_time
81
+ @cron.next_time(@previous_time.to_i.zero? ? @start_time : @previous_time)
82
+ end
83
+
84
+ # @return [Boolean] should we execute this task at this moment in time
85
+ def call?
86
+ return true if @trigger
87
+ return false unless enabled?
88
+
89
+ # Ensure the job is only due if current_time is strictly after the next_time
90
+ # Please note that we can only compare eorbi against time and not the other way around
91
+ next_time <= Time.now
92
+ end
93
+
94
+ # Executes the given task and publishes appropriate notification bus events.
95
+ def call
96
+ monitor.instrument(
97
+ 'recurring_tasks.task.executed',
98
+ task: self
99
+ ) do
100
+ # We check for presence of the `@executable` because user can define cron schedule
101
+ # without the code block
102
+ return unless @executable
103
+
104
+ execute
105
+ end
106
+ rescue StandardError => e
107
+ monitor.instrument(
108
+ 'error.occurred',
109
+ caller: self,
110
+ error: e,
111
+ task: self,
112
+ type: 'recurring_tasks.task.execute.error'
113
+ )
114
+ ensure
115
+ @trigger = false
116
+ @previous_time = Time.now
117
+ end
118
+
119
+ # Runs the executable block without any instrumentation or error handling. Useful for
120
+ # debugging and testing
121
+ def execute
122
+ @executable.call
123
+ end
124
+
125
+ # Removes the changes indicator flag
126
+ def clear
127
+ @changed = false
128
+ end
129
+
130
+ # @return [Hash] hash version of the task. Used for contract validation.
131
+ def to_h
132
+ {
133
+ id: id,
134
+ cron: @cron.original,
135
+ previous_time: previous_time,
136
+ next_time: next_time,
137
+ changed: changed?,
138
+ enabled: enabled?
139
+ }
140
+ end
141
+
142
+ private
143
+
144
+ # Marks the task as changed
145
+ def touch
146
+ @changed = true
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,87 @@
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
+ # Recurring tasks functionality
17
+ module RecurringTasks
18
+ class << self
19
+ # @return [Schedule, nil] current defined schedule or nil if not defined
20
+ def schedule
21
+ @schedule || define('0.0.0') {}
22
+ end
23
+
24
+ # Simplified API for schedules definitions and validates the tasks data
25
+ #
26
+ # @param version [String]
27
+ # @param block [Proc]
28
+ #
29
+ # @example
30
+ # Karafka::Pro::RecurringTasks.define('1.0.1') do
31
+ # schedule(id: 'mailer', cron: '* * * * *') do
32
+ # MailingJob.perform_async
33
+ # end
34
+ # end
35
+ def define(version = '1.0.0', &block)
36
+ @schedule = Schedule.new(version: version)
37
+ @schedule.instance_exec(&block)
38
+
39
+ @schedule.each do |task|
40
+ Contracts::Task.new.validate!(task.to_h)
41
+ end
42
+
43
+ @schedule
44
+ end
45
+
46
+ # Defines nice command methods to dispatch cron requests
47
+ Executor::COMMANDS.each do |command_name|
48
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
49
+ # @param task_id [String] task to which we want to dispatch command or '*' if to all
50
+ def #{command_name}(task_id)
51
+ Dispatcher.command('#{command_name}', task_id)
52
+ end
53
+ RUBY
54
+ end
55
+
56
+ # Below are private APIs
57
+
58
+ # Sets up additional config scope, validations and other things
59
+ #
60
+ # @param config [Karafka::Core::Configurable::Node] root node config
61
+ def pre_setup(config)
62
+ # Expand the config with this feature specific stuff
63
+ config.instance_eval do
64
+ setting(:recurring_tasks, default: Setup::Config.config)
65
+ end
66
+ end
67
+
68
+ # @param config [Karafka::Core::Configurable::Node] root node config
69
+ def post_setup(config)
70
+ RecurringTasks::Contracts::Config.new.validate!(config.to_h)
71
+
72
+ # Published after task is successfully executed
73
+ Karafka.monitor.notifications_bus.register_event('recurring_tasks.task.executed')
74
+
75
+ # Initialize empty dummy schedule, so we always have one and so we do not have to
76
+ # deal with a case where there is no schedule
77
+ RecurringTasks.schedule
78
+
79
+ # User can disable logging of executions, in which case we don't track them
80
+ return unless Karafka::App.config.recurring_tasks.logging
81
+
82
+ Karafka.monitor.subscribe(Listener.new)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,130 @@
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 Routing
17
+ module Features
18
+ class RecurringTasks < Base
19
+ # Routing extensions for recurring tasks
20
+ module Builder
21
+ # Enabled recurring tasks operations and adds needed topics and other stuff.
22
+ #
23
+ # @param active [Boolean] should recurring tasks be active. We use a boolean flag to
24
+ # have API consistency in the system, so it matches other routing related APIs.
25
+ # @param block [Proc] optional reconfiguration of the tasks topic definitions.
26
+ # @note Since we cannot provide two blocks, reconfiguration of logs topic can be only
27
+ # done if user explicitly redefines it in the routing.
28
+ def recurring_tasks(active = false, &block)
29
+ return unless active
30
+
31
+ # We only require zlib when we decide to run recurring tasks because it is not needed
32
+ # otherwise.
33
+ require 'zlib'
34
+ ensure_fugit_availability!
35
+
36
+ tasks_cfg = App.config.recurring_tasks
37
+ topics_cfg = tasks_cfg.topics
38
+
39
+ consumer_group tasks_cfg.group_id do
40
+ # Registers the primary topic that we use to control schedules execution. This is
41
+ # the one that we use to trigger recurring tasks.
42
+ topic(topics_cfg.schedules) do
43
+ consumer tasks_cfg.consumer_class
44
+ deserializer tasks_cfg.deserializer
45
+ # Because the topic method name as well as builder proxy method name is the same
46
+ # we need to reference it via target directly
47
+ target.recurring_tasks(true)
48
+
49
+ # We manage offsets directly because messages can have both schedules and
50
+ # commands and we need to apply them only when we need to
51
+ manual_offset_management(true)
52
+
53
+ # We use multi-batch operations and in-memory state for schedules. This needs to
54
+ # always operate without re-creation.
55
+ consumer_persistence(true)
56
+
57
+ # This needs to be enabled for the eof to work correctly
58
+ kafka('enable.partition.eof': true, inherit: true)
59
+ eofed(true)
60
+
61
+ # Favour latency. This is a low traffic topic that only accepts user initiated
62
+ # low-frequency commands
63
+ max_messages(1)
64
+ # Since we always react on the received message, we can block for longer periods
65
+ # of time
66
+ max_wait_time(10_000)
67
+
68
+ # Since the execution of particular tasks is isolated and guarded, it should not
69
+ # leak. This means, that this is to handle errors like schedule version
70
+ # incompatibility and other errors that will not go away without a redeployment
71
+ pause_timeout(60 * 1_000)
72
+ pause_max_timeout(60 * 1_000)
73
+
74
+ # Keep older data for a day and compact to the last state available
75
+ config(
76
+ 'cleanup.policy': 'compact,delete',
77
+ 'retention.ms': 86_400_000
78
+ )
79
+
80
+ # This is the core of execution. Since we're producers of states, we need a way
81
+ # to tick without having new data
82
+ periodic(
83
+ interval: App.config.recurring_tasks.interval,
84
+ during_pause: false,
85
+ during_retry: false
86
+ )
87
+
88
+ next unless block
89
+
90
+ instance_eval(&block)
91
+ end
92
+
93
+ # This topic is to store logs that we can then inspect either from the admin or via
94
+ # the Web UI
95
+ topic(topics_cfg.logs) do
96
+ active(false)
97
+ deserializer tasks_cfg.deserializer
98
+ target.recurring_tasks(true)
99
+
100
+ # Keep cron logs of executions for a week and after that remove. Week should be
101
+ # enough and should not produce too much data.
102
+ config(
103
+ 'cleanup.policy': 'delete',
104
+ 'retention.ms': 604_800_000
105
+ )
106
+ end
107
+ end
108
+ end
109
+
110
+ # Checks if fugit is present. If not, will try to require it as it might not have
111
+ # been required but is available. If fails, will crash.
112
+ def ensure_fugit_availability!
113
+ return if Object.const_defined?(:Fugit)
114
+
115
+ require 'fugit'
116
+ rescue LoadError
117
+ raise(
118
+ ::Karafka::Errors::DependencyConstraintsError,
119
+ <<~ERROR_MSG
120
+ Failed to require fugit gem.
121
+ Add it to your Gemfile, as it is required for the recurring tasks to work.
122
+ ERROR_MSG
123
+ )
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,28 @@
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 Routing
17
+ module Features
18
+ class RecurringTasks < Base
19
+ # Recurring tasks configuration
20
+ Config = Struct.new(
21
+ :active,
22
+ keyword_init: true
23
+ ) { alias_method :active?, :active }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,40 @@
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 Routing
17
+ module Features
18
+ class RecurringTasks < Base
19
+ # Namespace for recurring tasks contracts
20
+ module Contracts
21
+ # Rules around recurring tasks settings
22
+ class Topic < Karafka::Contracts::Base
23
+ configure do |config|
24
+ config.error_messages = YAML.safe_load(
25
+ File.read(
26
+ File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
27
+ )
28
+ ).fetch('en').fetch('validations').fetch('topic')
29
+ end
30
+
31
+ nested(:recurring_tasks) do
32
+ required(:active) { |val| [true, false].include?(val) }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
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 Routing
17
+ module Features
18
+ class RecurringTasks < Base
19
+ # Routing proxy extensions for recurring tasks
20
+ module Proxy
21
+ include Builder
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,44 @@
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 Routing
17
+ module Features
18
+ class RecurringTasks < Base
19
+ # Topic extensions to be able to check if given topic is a recurring tasks topic
20
+ # Please note, that this applies to both the schedules topics and reports topics
21
+ module Topic
22
+ # @param active [Boolean] should this topic be considered related to recurring tasks
23
+ def recurring_tasks(active = false)
24
+ @recurring_tasks ||= Config.new(active: active)
25
+ end
26
+
27
+ # @return [Boolean] is this an ActiveJob topic
28
+ def recurring_tasks?
29
+ recurring_tasks.active?
30
+ end
31
+
32
+ # @return [Hash] topic with all its native configuration options plus active job
33
+ # namespace settings
34
+ def to_h
35
+ super.merge(
36
+ recurring_tasks: recurring_tasks.to_h
37
+ ).freeze
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,25 @@
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 Routing
17
+ module Features
18
+ # This feature allows you to run recurring tasks aka cron jobs from within Karafka
19
+ # It uses two special topics for tracking the schedule and reporting executions
20
+ class RecurringTasks < Base
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -76,9 +76,13 @@ module Karafka
76
76
  dispatch_to_dlq(skippable_message)
77
77
 
78
78
  # We mark the broken message as consumed and move on
79
- mark_dispatched_to_dlq(skippable_message)
79
+ if mark_after_dispatch?
80
+ mark_dispatched_to_dlq(skippable_message)
80
81
 
81
- return if revoked?
82
+ return if revoked?
83
+ else
84
+ coordinator.seek_offset = skippable_message.offset + 1
85
+ end
82
86
 
83
87
  # We pause to backoff once just in case.
84
88
  pause(coordinator.seek_offset, nil, false)
@@ -120,6 +124,16 @@ module Karafka
120
124
  )
121
125
  end
122
126
 
127
+ # @return [Boolean] should we mark given message as consumed after dispatch. For default
128
+ # non MOM strategies if user did not explicitly tell us not to, we mark it. Default is
129
+ # `nil`, which means `true` in this case. If user provided alternative value, we go
130
+ # with it.
131
+ def mark_after_dispatch?
132
+ return true if topic.dead_letter_queue.mark_after_dispatch.nil?
133
+
134
+ topic.dead_letter_queue.mark_after_dispatch
135
+ end
136
+
123
137
  # Marks message that went to DLQ (if applicable) based on the requested method
124
138
  # @param skippable_message [Karafka::Messages::Message]
125
139
  def mark_dispatched_to_dlq(skippable_message)
@@ -33,16 +33,35 @@ module Karafka
33
33
 
34
34
  dispatch_to_dlq(skippable_message)
35
35
 
36
- # Save the next offset we want to go with after moving given message to DLQ
37
- # Without this, we would not be able to move forward and we would end up
38
- # in an infinite loop trying to un-pause from the message we've already processed
39
- # Of course, since it's a MoM a rebalance or kill, will move it back as no
40
- # offsets are being committed
41
- coordinator.seek_offset = skippable_message.offset + 1
36
+ # We mark the broken message as consumed and move on
37
+ if mark_after_dispatch?
38
+ mark_dispatched_to_dlq(skippable_message)
39
+
40
+ return if revoked?
41
+ else
42
+ # Save the next offset we want to go with after moving given message to DLQ
43
+ # Without this, we would not be able to move forward and we would end up
44
+ # in an infinite loop trying to un-pause from the message we've already processed
45
+ # Of course, since it's a MoM a rebalance or kill, will move it back as no
46
+ # offsets are being committed
47
+ coordinator.seek_offset = skippable_message.offset + 1
48
+ end
42
49
 
43
50
  pause(coordinator.seek_offset, nil, false)
44
51
  end
45
52
  end
53
+
54
+ # @return [Boolean] should we mark given message as consumed after dispatch. For
55
+ # MOM strategies if user did not explicitly tell us to mark, we do not mark. Default is
56
+ # `nil`, which means `false` in this case. If user provided alternative value, we go
57
+ # with it.
58
+ #
59
+ # @note Please note, this is the opposite behavior than in case of AOM strategies.
60
+ def mark_after_dispatch?
61
+ return false if topic.dead_letter_queue.mark_after_dispatch.nil?
62
+
63
+ topic.dead_letter_queue.mark_after_dispatch
64
+ end
46
65
  end
47
66
  end
48
67
  end
@@ -18,6 +18,9 @@ module Karafka
18
18
  # not user code but need to run after user code base is executed.
19
19
  class Worker
20
20
  include Helpers::Async
21
+ include Helpers::ConfigImporter.new(
22
+ worker_job_call_wrapper: %i[internal processing worker_job_call_wrapper]
23
+ )
21
24
 
22
25
  # @return [String] id of this worker
23
26
  attr_reader :id
@@ -27,6 +30,7 @@ module Karafka
27
30
  def initialize(jobs_queue)
28
31
  @id = SecureRandom.hex(6)
29
32
  @jobs_queue = jobs_queue
33
+ @non_wrapped_flow = worker_job_call_wrapper == false
30
34
  end
31
35
 
32
36
  private
@@ -60,7 +64,13 @@ module Karafka
60
64
  # pass it until done.
61
65
  @jobs_queue.tick(job.group_id) if job.non_blocking?
62
66
 
63
- job.call
67
+ if @non_wrapped_flow
68
+ job.call
69
+ else
70
+ worker_job_call_wrapper.wrap do
71
+ job.call
72
+ end
73
+ end
64
74
 
65
75
  job.after_call
66
76