karafka 2.0.38 → 2.0.39

Sign up to get free protection for your applications and to get access to all the features.
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