karafka 2.0.38 → 2.0.39

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 (113) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +1 -1
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +28 -0
  6. data/Gemfile.lock +4 -4
  7. data/bin/integrations +1 -1
  8. data/config/locales/errors.yml +0 -7
  9. data/config/locales/pro_errors.yml +18 -0
  10. data/lib/karafka/base_consumer.rb +35 -55
  11. data/lib/karafka/connection/listener.rb +15 -10
  12. data/lib/karafka/errors.rb +0 -3
  13. data/lib/karafka/instrumentation/logger_listener.rb +44 -3
  14. data/lib/karafka/instrumentation/notifications.rb +4 -0
  15. data/lib/karafka/pro/active_job/consumer.rb +10 -1
  16. data/lib/karafka/pro/processing/coordinator.rb +13 -4
  17. data/lib/karafka/pro/processing/filters/base.rb +61 -0
  18. data/lib/karafka/pro/processing/filters/delayer.rb +70 -0
  19. data/lib/karafka/pro/processing/filters/expirer.rb +51 -0
  20. data/lib/karafka/pro/processing/filters/throttler.rb +84 -0
  21. data/lib/karafka/pro/processing/filters_applier.rb +100 -0
  22. data/lib/karafka/pro/processing/jobs_builder.rb +7 -3
  23. data/lib/karafka/pro/processing/scheduler.rb +24 -7
  24. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom.rb +68 -0
  25. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom_vp.rb +74 -0
  26. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom.rb +72 -0
  27. data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom_vp.rb +76 -0
  28. data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom.rb +62 -0
  29. data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom_vp.rb +68 -0
  30. data/lib/karafka/pro/processing/strategies/aj/dlq_mom.rb +64 -0
  31. data/lib/karafka/pro/processing/strategies/aj/dlq_mom_vp.rb +69 -0
  32. data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom.rb +38 -0
  33. data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom_vp.rb +64 -0
  34. data/lib/karafka/pro/processing/strategies/aj/ftr_mom.rb +38 -0
  35. data/lib/karafka/pro/processing/strategies/aj/ftr_mom_vp.rb +58 -0
  36. data/lib/karafka/pro/processing/strategies/{dlq_lrj_vp.rb → aj/lrj_mom.rb} +14 -13
  37. data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +77 -0
  38. data/lib/karafka/pro/processing/strategies/aj/mom.rb +36 -0
  39. data/lib/karafka/pro/processing/strategies/aj/mom_vp.rb +52 -0
  40. data/lib/karafka/pro/processing/strategies/dlq/default.rb +131 -0
  41. data/lib/karafka/pro/processing/strategies/dlq/ftr.rb +61 -0
  42. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj.rb +75 -0
  43. data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +74 -0
  44. data/lib/karafka/pro/processing/strategies/{mom.rb → dlq/ftr_lrj_vp.rb} +16 -19
  45. data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +73 -0
  46. data/lib/karafka/pro/processing/strategies/dlq/ftr_vp.rb +39 -0
  47. data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +63 -0
  48. data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +66 -0
  49. data/lib/karafka/pro/processing/strategies/dlq/lrj_vp.rb +38 -0
  50. data/lib/karafka/pro/processing/strategies/dlq/mom.rb +67 -0
  51. data/lib/karafka/pro/processing/strategies/dlq/vp.rb +39 -0
  52. data/lib/karafka/pro/processing/strategies/ftr/default.rb +104 -0
  53. data/lib/karafka/pro/processing/strategies/ftr/vp.rb +40 -0
  54. data/lib/karafka/pro/processing/strategies/lrj/default.rb +85 -0
  55. data/lib/karafka/pro/processing/strategies/lrj/ftr.rb +69 -0
  56. data/lib/karafka/pro/processing/strategies/lrj/ftr_mom.rb +67 -0
  57. data/lib/karafka/pro/processing/strategies/{vp.rb → lrj/ftr_vp.rb} +15 -13
  58. data/lib/karafka/pro/processing/strategies/lrj/mom.rb +78 -0
  59. data/lib/karafka/pro/processing/strategies/{aj_lrj_mom.rb → lrj/vp.rb} +13 -12
  60. data/lib/karafka/pro/processing/strategies/mom/default.rb +46 -0
  61. data/lib/karafka/pro/processing/strategies/mom/ftr.rb +53 -0
  62. data/lib/karafka/pro/processing/strategies/vp/default.rb +53 -0
  63. data/lib/karafka/pro/processing/{strategies/lrj_vp.rb → strategies.rb} +1 -13
  64. data/lib/karafka/pro/processing/strategy_selector.rb +44 -18
  65. data/lib/karafka/pro/{processing/strategies/aj_mom.rb → routing/features/delaying/config.rb} +7 -13
  66. data/lib/karafka/pro/routing/features/delaying/contract.rb +38 -0
  67. data/lib/karafka/pro/routing/features/delaying/topic.rb +59 -0
  68. data/lib/karafka/pro/routing/features/delaying.rb +29 -0
  69. data/lib/karafka/pro/routing/features/expiring/config.rb +27 -0
  70. data/lib/karafka/pro/routing/features/expiring/contract.rb +38 -0
  71. data/lib/karafka/pro/routing/features/expiring/topic.rb +59 -0
  72. data/lib/karafka/pro/routing/features/expiring.rb +27 -0
  73. data/lib/karafka/pro/routing/features/filtering/config.rb +40 -0
  74. data/lib/karafka/pro/routing/features/filtering/contract.rb +41 -0
  75. data/lib/karafka/pro/routing/features/filtering/topic.rb +51 -0
  76. data/lib/karafka/pro/routing/features/filtering.rb +27 -0
  77. data/lib/karafka/pro/routing/features/long_running_job/contract.rb +1 -1
  78. data/lib/karafka/pro/routing/features/throttling/config.rb +32 -0
  79. data/lib/karafka/pro/routing/features/throttling/contract.rb +41 -0
  80. data/lib/karafka/pro/routing/features/throttling/topic.rb +69 -0
  81. data/lib/karafka/pro/routing/features/throttling.rb +30 -0
  82. data/lib/karafka/processing/coordinator.rb +60 -30
  83. data/lib/karafka/processing/coordinators_buffer.rb +5 -1
  84. data/lib/karafka/processing/executor.rb +23 -16
  85. data/lib/karafka/processing/executors_buffer.rb +10 -26
  86. data/lib/karafka/processing/jobs/consume.rb +2 -4
  87. data/lib/karafka/processing/jobs/idle.rb +24 -0
  88. data/lib/karafka/processing/jobs_builder.rb +2 -3
  89. data/lib/karafka/processing/result.rb +5 -0
  90. data/lib/karafka/processing/strategies/aj_dlq_mom.rb +1 -1
  91. data/lib/karafka/processing/strategies/base.rb +5 -0
  92. data/lib/karafka/processing/strategies/default.rb +50 -0
  93. data/lib/karafka/processing/strategies/dlq.rb +13 -4
  94. data/lib/karafka/processing/strategies/dlq_mom.rb +8 -3
  95. data/lib/karafka/processing/strategy_selector.rb +27 -10
  96. data/lib/karafka/version.rb +1 -1
  97. data/renovate.json +6 -0
  98. data.tar.gz.sig +0 -0
  99. metadata +66 -22
  100. metadata.gz.sig +0 -0
  101. data/lib/karafka/pro/processing/strategies/aj_dlq_lrj_mom.rb +0 -42
  102. data/lib/karafka/pro/processing/strategies/aj_dlq_lrj_mom_vp.rb +0 -70
  103. data/lib/karafka/pro/processing/strategies/aj_dlq_mom.rb +0 -62
  104. data/lib/karafka/pro/processing/strategies/aj_dlq_mom_vp.rb +0 -68
  105. data/lib/karafka/pro/processing/strategies/aj_lrj_mom_vp.rb +0 -75
  106. data/lib/karafka/pro/processing/strategies/aj_mom_vp.rb +0 -62
  107. data/lib/karafka/pro/processing/strategies/dlq.rb +0 -120
  108. data/lib/karafka/pro/processing/strategies/dlq_lrj.rb +0 -65
  109. data/lib/karafka/pro/processing/strategies/dlq_lrj_mom.rb +0 -62
  110. data/lib/karafka/pro/processing/strategies/dlq_mom.rb +0 -62
  111. data/lib/karafka/pro/processing/strategies/dlq_vp.rb +0 -37
  112. data/lib/karafka/pro/processing/strategies/lrj.rb +0 -83
  113. data/lib/karafka/pro/processing/strategies/lrj_mom.rb +0 -73
@@ -0,0 +1,51 @@
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 Filters
18
+ # Expirer for removing too old messages.
19
+ # It never moves offsets in any way and does not impact the processing flow. It always
20
+ # runs `:skip` action.
21
+ class Expirer < Base
22
+ # @param ttl [Integer] maximum age of a message (in ms)
23
+ def initialize(ttl)
24
+ super()
25
+
26
+ @ttl = ttl
27
+ end
28
+
29
+ # Removes too old messages
30
+ #
31
+ # @param messages [Array<Karafka::Messages::Message>]
32
+ def apply!(messages)
33
+ @applied = false
34
+
35
+ # Time on message is in seconds with ms precision, so we need to convert the ttl that
36
+ # is in ms to this format
37
+ border = ::Time.now.utc - @ttl / 1_000.to_f
38
+
39
+ messages.delete_if do |message|
40
+ too_old = message.timestamp < border
41
+
42
+ @applied = true if too_old
43
+
44
+ too_old
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,84 @@
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
+ # Namespace containing Pro out of the box filters used by various strategies
18
+ module Filters
19
+ # Throttler used to limit number of messages we can process in a given time interval
20
+ # The tricky thing is, that even if we throttle on 100 messages, if we've reached 100, we
21
+ # still need to indicate, that we throttle despite not receiving 101. Otherwise we will
22
+ # not pause the partition and will fetch more data that we should not process.
23
+ #
24
+ # This is a special type of a filter that always throttles and makes us wait / seek if
25
+ # anything is applied out.
26
+ class Throttler < Base
27
+ # @param limit [Integer] how many messages we can process in a given time
28
+ # @param interval [Integer] interval in milliseconds for which we want to process
29
+ def initialize(limit, interval)
30
+ super()
31
+
32
+ @limit = limit
33
+ @interval = interval
34
+ @requests = Hash.new { |h, k| h[k] = 0 }
35
+ end
36
+
37
+ # Limits number of messages to a range that we can process (if needed) and keeps track
38
+ # of how many messages we've processed in a given time
39
+ # @param messages [Array<Karafka::Messages::Message>] limits the number of messages to
40
+ # number we can accept in the context of throttling constraints
41
+ def apply!(messages)
42
+ @applied = false
43
+ @cursor = nil
44
+ @time = monotonic_now
45
+ @requests.delete_if { |timestamp, _| timestamp < (@time - @interval) }
46
+ values = @requests.values.sum
47
+ accepted = 0
48
+
49
+ messages.delete_if do |message|
50
+ # +1 because of current
51
+ @applied = (values + accepted + 1) > @limit
52
+
53
+ @cursor = message if @applied && @cursor.nil?
54
+
55
+ next true if @applied
56
+
57
+ accepted += 1
58
+
59
+ false
60
+ end
61
+
62
+ @requests[@time] += accepted
63
+ end
64
+
65
+ # @return [Symbol] action to take upon throttler reaching certain state
66
+ def action
67
+ if applied?
68
+ timeout.zero? ? :seek : :pause
69
+ else
70
+ :skip
71
+ end
72
+ end
73
+
74
+ # @return [Integer] minimum number of milliseconds to wait before getting more messages
75
+ # so we are no longer throttled and so we can process at least one message
76
+ def timeout
77
+ timeout = @interval - (monotonic_now - @time)
78
+ timeout <= 0 ? 0 : timeout
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,100 @@
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
+ # Applier for all filters we want to have. Whether related to limiting messages based
18
+ # on the payload or any other things.
19
+ #
20
+ # From the outside world perspective, this encapsulates all the filters.
21
+ # This means that this is the API we expose as a single filter, allowing us to control
22
+ # the filtering via many filters easily.
23
+ class FiltersApplier
24
+ # @param coordinator [Pro::Coordinator] pro coordinator
25
+ def initialize(coordinator)
26
+ # Builds filters out of their factories
27
+ # We build it that way (providing topic and partition) because there may be a case where
28
+ # someone wants to have a specific logic that is per topic or partition. Like for example
29
+ # a case where there is a cache bypassing revocations for topic partition.
30
+ #
31
+ # We provide full Karafka routing topic here and not the name only, in case the filter
32
+ # would be customized based on other topic settings (like VPs, etc)
33
+ #
34
+ # This setup allows for biggest flexibility also because topic object holds the reference
35
+ # to the subscription group and consumer group
36
+ @filters = coordinator.topic.filtering.factories.map do |factory|
37
+ factory.call(coordinator.topic, coordinator.partition)
38
+ end
39
+ end
40
+
41
+ # @param messages [Array<Karafka::Messages::Message>] array with messages from the
42
+ # partition
43
+ def apply!(messages)
44
+ return unless active?
45
+
46
+ @filters.each { |filter| filter.apply!(messages) }
47
+ end
48
+
49
+ # @return [Boolean] did we filter out any messages during filtering run
50
+ def applied?
51
+ return false unless active?
52
+
53
+ !applied.empty?
54
+ end
55
+
56
+ # @return [Symbol] consumer post-filtering action that should be taken
57
+ def action
58
+ return :skip unless applied?
59
+
60
+ # The highest priority is on a potential backoff from any of the filters because it is
61
+ # the less risky (delay and continue later)
62
+ return :pause if applied.any? { |filter| filter.action == :pause }
63
+
64
+ # If none of the filters wanted to pause, we can check for any that would want to seek
65
+ # and if there is any, we can go with this strategy
66
+ return :seek if applied.any? { |filter| filter.action == :seek }
67
+
68
+ :skip
69
+ end
70
+
71
+ # @return [Integer] minimum timeout we need to pause. This is the minimum for all the
72
+ # filters to satisfy all of them.
73
+ def timeout
74
+ applied.map(&:timeout).compact.min || 0
75
+ end
76
+
77
+ # The first message we do need to get next time we poll. We use the minimum not to jump
78
+ # accidentally by over any.
79
+ # @return [Karafka::Messages::Message, nil] cursor message or nil if none
80
+ def cursor
81
+ return nil unless active?
82
+
83
+ applied.map(&:cursor).compact.min_by(&:offset)
84
+ end
85
+
86
+ private
87
+
88
+ # @return [Boolean] is filtering active
89
+ def active?
90
+ !@filters.empty?
91
+ end
92
+
93
+ # @return [Array<Object>] filters that applied any sort of messages limiting
94
+ def applied
95
+ @filters.select(&:applied?)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -16,14 +16,18 @@ module Karafka
16
16
  module Processing
17
17
  # Pro jobs builder that supports lrj
18
18
  class JobsBuilder < ::Karafka::Processing::JobsBuilder
19
+ # @param executor [Karafka::Processing::Executor]
20
+ def idle(executor)
21
+ Karafka::Processing::Jobs::Idle.new(executor)
22
+ end
23
+
19
24
  # @param executor [Karafka::Processing::Executor]
20
25
  # @param messages [Karafka::Messages::Messages] messages batch to be consumed
21
- # @param coordinator [Karafka::Processing::Coordinator]
22
26
  # @return [Karafka::Processing::Jobs::Consume] blocking job
23
27
  # @return [Karafka::Pro::Processing::Jobs::ConsumeNonBlocking] non blocking for lrj
24
- def consume(executor, messages, coordinator)
28
+ def consume(executor, messages)
25
29
  if executor.topic.long_running_job?
26
- Jobs::ConsumeNonBlocking.new(executor, messages, coordinator)
30
+ Jobs::ConsumeNonBlocking.new(executor, messages)
27
31
  else
28
32
  super
29
33
  end
@@ -31,17 +31,15 @@ module Karafka
31
31
  # @param jobs_array [Array<Karafka::Processing::Jobs::Base>] jobs we want to schedule
32
32
  #
33
33
  def schedule_consumption(queue, jobs_array)
34
- pt = PerformanceTracker.instance
34
+ perf_tracker = PerformanceTracker.instance
35
35
 
36
36
  ordered = []
37
37
 
38
38
  jobs_array.each do |job|
39
- messages = job.messages
40
- message = messages.first
41
-
42
- cost = pt.processing_time_p95(message.topic, message.partition) * messages.size
43
-
44
- ordered << [job, cost]
39
+ ordered << [
40
+ job,
41
+ processing_cost(perf_tracker, job)
42
+ ]
45
43
  end
46
44
 
47
45
  ordered.sort_by!(&:last)
@@ -52,6 +50,25 @@ module Karafka
52
50
  queue << job
53
51
  end
54
52
  end
53
+
54
+ private
55
+
56
+ # @param perf_tracker [PerformanceTracker]
57
+ # @param job [Karafka::Processing::Jobs::Base] job we will be processing
58
+ # @return [Numeric] estimated cost of processing this job
59
+ def processing_cost(perf_tracker, job)
60
+ if job.is_a?(::Karafka::Processing::Jobs::Consume)
61
+ messages = job.messages
62
+ message = messages.first
63
+
64
+ perf_tracker.processing_time_p95(message.topic, message.partition) * messages.size
65
+ else
66
+ # LJF will set first the most expensive, but we want to run the zero cost jobs
67
+ # related to the lifecycle always first. That is why we "emulate" that they
68
+ # the longest possible jobs that anyone can run
69
+ Float::INFINITY
70
+ end
71
+ end
55
72
  end
56
73
  end
57
74
  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
+ module Aj
19
+ # ActiveJob enabled
20
+ # DLQ enabled
21
+ # Filtering enabled
22
+ # Long-Running Job enabled
23
+ # Manual offset management enabled
24
+ module DlqFtrLrjMom
25
+ include Strategies::Aj::FtrMom
26
+ include Strategies::Aj::DlqMom
27
+ include Strategies::Aj::LrjMom
28
+
29
+ # Features for this strategy
30
+ FEATURES = %i[
31
+ active_job
32
+ dead_letter_queue
33
+ filtering
34
+ long_running_job
35
+ manual_offset_management
36
+ ].freeze
37
+
38
+ # This strategy assumes we do not early break on shutdown as it has VP
39
+ def handle_after_consume
40
+ coordinator.on_finished do
41
+ if coordinator.success?
42
+ coordinator.pause_tracker.reset
43
+
44
+ if coordinator.filtered? && !revoked?
45
+ handle_post_filtering
46
+ elsif !revoked?
47
+ seek(coordinator.seek_offset)
48
+ resume
49
+ else
50
+ resume
51
+ end
52
+ elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
53
+ retry_after_pause
54
+ else
55
+ coordinator.pause_tracker.reset
56
+ skippable_message, = find_skippable_message
57
+ dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
58
+ mark_as_consumed(skippable_message)
59
+ pause(coordinator.seek_offset, nil, false)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,74 @@
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
+ module Aj
19
+ # ActiveJob enabled
20
+ # DLQ enabled
21
+ # Filtering enabled
22
+ # Long-Running Job enabled
23
+ # Manual offset management enabled
24
+ # Virtual Partitions enabled
25
+ module DlqFtrLrjMomVp
26
+ include Strategies::Aj::FtrMom
27
+ include Strategies::Aj::DlqMomVp
28
+ include Strategies::Aj::LrjMom
29
+
30
+ # Features for this strategy
31
+ FEATURES = %i[
32
+ active_job
33
+ dead_letter_queue
34
+ filtering
35
+ long_running_job
36
+ manual_offset_management
37
+ virtual_partitions
38
+ ].freeze
39
+
40
+ # This strategy assumes we do not early break on shutdown as it has VP
41
+ def handle_after_consume
42
+ coordinator.on_finished do |last_group_message|
43
+ if coordinator.success?
44
+ coordinator.pause_tracker.reset
45
+
46
+ # Since we have VP here we do not commit intermediate offsets and need to commit
47
+ # them here. We do commit in collapsed mode but this is generalized.
48
+ mark_as_consumed(last_group_message) unless revoked?
49
+
50
+ if coordinator.filtered? && !revoked?
51
+ handle_post_filtering
52
+ elsif !revoked?
53
+ seek(coordinator.seek_offset)
54
+ resume
55
+ else
56
+ resume
57
+ end
58
+ elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
59
+ retry_after_pause
60
+ else
61
+ coordinator.pause_tracker.reset
62
+ skippable_message, = find_skippable_message
63
+ dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
64
+ mark_as_consumed(skippable_message)
65
+ pause(coordinator.seek_offset, nil, false)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,72 @@
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
+ module Aj
19
+ # - Aj
20
+ # - Dlq
21
+ # - Ftr
22
+ # - Mom
23
+ module DlqFtrMom
24
+ include Strategies::Dlq::FtrMom
25
+
26
+ # Features for this strategy
27
+ FEATURES = %i[
28
+ active_job
29
+ dead_letter_queue
30
+ filtering
31
+ manual_offset_management
32
+ ].freeze
33
+
34
+ # We write our own custom handler for after consume here, because we mark as consumed
35
+ # per each job in the AJ consumer itself (for this strategy). This means, that for DLQ
36
+ # dispatch, we can mark this message as consumed as well.
37
+ def handle_after_consume
38
+ coordinator.on_finished do
39
+ return if revoked?
40
+
41
+ if coordinator.success?
42
+ coordinator.pause_tracker.reset
43
+
44
+ return if coordinator.manual_pause?
45
+
46
+ handle_post_filtering
47
+ elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
48
+ retry_after_pause
49
+ # If we've reached number of retries that we could, we need to skip the first
50
+ # message that was not marked as consumed, pause and continue, while also moving
51
+ # this message to the dead topic.
52
+ #
53
+ # For a Mom setup, this means, that user has to manage the checkpointing by
54
+ # himself. If no checkpointing is ever done, we end up with an endless loop.
55
+ else
56
+ coordinator.pause_tracker.reset
57
+ skippable_message, = find_skippable_message
58
+ dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
59
+ # We can commit the offset here because we know that we skip it "forever" and
60
+ # since AJ consumer commits the offset after each job, we also know that the
61
+ # previous job was successful
62
+ mark_as_consumed(skippable_message)
63
+ pause(coordinator.seek_offset, nil, false)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,76 @@
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
+ module Aj
19
+ # - Aj
20
+ # - Dlq
21
+ # - Ftr
22
+ # - Mom
23
+ # - VP
24
+ module DlqFtrMomVp
25
+ include Strategies::Vp::Default
26
+ include Strategies::Aj::DlqFtrMom
27
+
28
+ # Features for this strategy
29
+ FEATURES = %i[
30
+ active_job
31
+ dead_letter_queue
32
+ filtering
33
+ manual_offset_management
34
+ virtual_partitions
35
+ ].freeze
36
+
37
+ # AJ VP does not early stop on shutdown, hence here we can mark as consumed at the
38
+ # end of all VPs
39
+ def handle_after_consume
40
+ coordinator.on_finished do |last_group_message|
41
+ return if revoked?
42
+
43
+ if coordinator.success?
44
+ coordinator.pause_tracker.reset
45
+
46
+ return if coordinator.manual_pause?
47
+
48
+ mark_as_consumed(last_group_message)
49
+
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
+ else
60
+ coordinator.pause_tracker.reset
61
+ skippable_message, = find_skippable_message
62
+ dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
63
+ # We can commit the offset here because we know that we skip it "forever" and
64
+ # since AJ consumer commits the offset after each job, we also know that the
65
+ # previous job was successful
66
+ mark_as_consumed(skippable_message)
67
+ pause(coordinator.seek_offset, nil, false)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ 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
+ module Strategies
18
+ module Aj
19
+ # ActiveJob enabled
20
+ # DLQ enabled
21
+ # Long-Running Job enabled
22
+ # Manual offset management 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 DlqLrjMom
27
+ include Strategies::Aj::DlqLrjMomVp
28
+
29
+ # Features for this strategy
30
+ FEATURES = %i[
31
+ active_job
32
+ dead_letter_queue
33
+ long_running_job
34
+ manual_offset_management
35
+ ].freeze
36
+
37
+ # We cannot use a VP version of this, because non-VP can early stop on shutdown
38
+ def handle_after_consume
39
+ coordinator.on_finished do
40
+ if coordinator.success?
41
+ coordinator.pause_tracker.reset
42
+
43
+ seek(coordinator.seek_offset) unless revoked?
44
+
45
+ resume
46
+ elsif coordinator.pause_tracker.attempt <= topic.dead_letter_queue.max_retries
47
+ retry_after_pause
48
+ else
49
+ coordinator.pause_tracker.reset
50
+ skippable_message, = find_skippable_message
51
+ dispatch_to_dlq(skippable_message) if dispatch_to_dlq?
52
+ mark_as_consumed(skippable_message)
53
+ pause(coordinator.seek_offset, nil, false)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end