karafka 2.0.37 → 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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +34 -0
- data/Gemfile.lock +7 -7
- data/README.md +1 -1
- data/bin/integrations +1 -1
- data/config/locales/errors.yml +0 -7
- data/config/locales/pro_errors.yml +18 -0
- data/lib/karafka/active_job/consumer.rb +22 -7
- data/lib/karafka/admin.rb +46 -14
- data/lib/karafka/base_consumer.rb +35 -55
- data/lib/karafka/connection/listener.rb +15 -10
- data/lib/karafka/errors.rb +0 -3
- data/lib/karafka/instrumentation/logger_listener.rb +44 -3
- data/lib/karafka/instrumentation/notifications.rb +7 -0
- data/lib/karafka/pro/active_job/consumer.rb +10 -5
- data/lib/karafka/pro/processing/coordinator.rb +13 -4
- 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_applier.rb +100 -0
- data/lib/karafka/pro/processing/jobs_builder.rb +7 -3
- data/lib/karafka/pro/processing/scheduler.rb +24 -7
- 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 +62 -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/{dlq_lrj_vp.rb → aj/lrj_mom.rb} +14 -13
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +77 -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/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 +74 -0
- data/lib/karafka/pro/processing/strategies/{mom.rb → dlq/ftr_lrj_vp.rb} +16 -19
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +73 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_vp.rb +39 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +63 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +66 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_vp.rb +38 -0
- data/lib/karafka/pro/processing/strategies/dlq/mom.rb +67 -0
- data/lib/karafka/pro/processing/strategies/dlq/vp.rb +39 -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 +85 -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/{vp.rb → lrj/ftr_vp.rb} +15 -13
- data/lib/karafka/pro/processing/strategies/lrj/mom.rb +78 -0
- data/lib/karafka/pro/processing/strategies/{aj_lrj_mom.rb → lrj/vp.rb} +13 -12
- 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/vp/default.rb +53 -0
- data/lib/karafka/pro/processing/{strategies/lrj_vp.rb → strategies.rb} +1 -13
- data/lib/karafka/pro/processing/strategy_selector.rb +44 -18
- data/lib/karafka/pro/{processing/strategies/aj_mom.rb → routing/features/delaying/config.rb} +7 -13
- 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/contract.rb +1 -1
- 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/processing/coordinator.rb +60 -30
- data/lib/karafka/processing/coordinators_buffer.rb +5 -1
- data/lib/karafka/processing/executor.rb +23 -16
- data/lib/karafka/processing/executors_buffer.rb +10 -26
- data/lib/karafka/processing/jobs/consume.rb +2 -4
- data/lib/karafka/processing/jobs/idle.rb +24 -0
- data/lib/karafka/processing/jobs_builder.rb +2 -3
- data/lib/karafka/processing/result.rb +5 -0
- data/lib/karafka/processing/strategies/aj_dlq_mom.rb +1 -1
- data/lib/karafka/processing/strategies/base.rb +5 -0
- data/lib/karafka/processing/strategies/default.rb +50 -0
- data/lib/karafka/processing/strategies/dlq.rb +13 -4
- data/lib/karafka/processing/strategies/dlq_mom.rb +8 -3
- data/lib/karafka/processing/strategy_selector.rb +27 -10
- data/lib/karafka/version.rb +1 -1
- data/renovate.json +6 -0
- data.tar.gz.sig +0 -0
- metadata +66 -22
- metadata.gz.sig +0 -0
- data/lib/karafka/pro/processing/strategies/aj_dlq_lrj_mom.rb +0 -42
- data/lib/karafka/pro/processing/strategies/aj_dlq_lrj_mom_vp.rb +0 -70
- data/lib/karafka/pro/processing/strategies/aj_dlq_mom.rb +0 -62
- data/lib/karafka/pro/processing/strategies/aj_dlq_mom_vp.rb +0 -68
- data/lib/karafka/pro/processing/strategies/aj_lrj_mom_vp.rb +0 -75
- data/lib/karafka/pro/processing/strategies/aj_mom_vp.rb +0 -62
- data/lib/karafka/pro/processing/strategies/dlq.rb +0 -120
- data/lib/karafka/pro/processing/strategies/dlq_lrj.rb +0 -65
- data/lib/karafka/pro/processing/strategies/dlq_lrj_mom.rb +0 -62
- data/lib/karafka/pro/processing/strategies/dlq_mom.rb +0 -62
- data/lib/karafka/pro/processing/strategies/dlq_vp.rb +0 -37
- data/lib/karafka/pro/processing/strategies/lrj.rb +0 -83
- data/lib/karafka/pro/processing/strategies/lrj_mom.rb +0 -73
|
@@ -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 Filtering < Base
|
|
19
|
+
# Filtering feature configuration
|
|
20
|
+
Config = Struct.new(:factories, keyword_init: true) do
|
|
21
|
+
# @return [Boolean] is this feature in use. Are any filters defined
|
|
22
|
+
def active?
|
|
23
|
+
!factories.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Array<Object>] array of filters applicable to a topic partition
|
|
27
|
+
def filters
|
|
28
|
+
factories.map(&:call)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Hash] this config hash
|
|
32
|
+
def to_h
|
|
33
|
+
super.merge(active: active?)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
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 Filtering < Base
|
|
19
|
+
# Contract to validate configuration of the filtering feature
|
|
20
|
+
class Contract < Contracts::Base
|
|
21
|
+
configure do |config|
|
|
22
|
+
config.error_messages = YAML.safe_load(
|
|
23
|
+
File.read(
|
|
24
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
|
25
|
+
)
|
|
26
|
+
).fetch('en').fetch('validations').fetch('topic')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
nested(:filtering) do
|
|
30
|
+
required(:active) { |val| [true, false].include?(val) }
|
|
31
|
+
|
|
32
|
+
required(:factories) do |val|
|
|
33
|
+
val.is_a?(Array) && val.all? { |factory| factory.respond_to?(:call) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -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 Routing
|
|
17
|
+
module Features
|
|
18
|
+
class Filtering < Base
|
|
19
|
+
# Filtering feature topic extensions
|
|
20
|
+
module Topic
|
|
21
|
+
# @param factory [#call, nil] Callable that can produce new filters instances per
|
|
22
|
+
# assigned topic partition. nil as default so this feature is disabled
|
|
23
|
+
def filter(factory = nil)
|
|
24
|
+
@filtering ||= Config.new(factories: [])
|
|
25
|
+
@filtering.factories << factory if factory
|
|
26
|
+
@filtering
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @param args [Array] Anything `#filter` accepts
|
|
30
|
+
# @return [Filtering::Config] alias to match the naming API for features
|
|
31
|
+
def filtering(*args)
|
|
32
|
+
filter(*args)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [Boolean] is a given job throttled
|
|
36
|
+
def filtering?
|
|
37
|
+
filtering.active?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [Hash] topic with all its native configuration options plus throttling
|
|
41
|
+
def to_h
|
|
42
|
+
super.merge(
|
|
43
|
+
filtering: filtering.to_h
|
|
44
|
+
).freeze
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
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
|
+
# Filtering provides a generic API allowing you to pre-filter messages before they are
|
|
19
|
+
# dispatched to jobs and processed.
|
|
20
|
+
#
|
|
21
|
+
# It allows for throttling, delayed jobs and other filtering implementations.
|
|
22
|
+
class Filtering < Base
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -16,7 +16,7 @@ module Karafka
|
|
|
16
16
|
module Routing
|
|
17
17
|
module Features
|
|
18
18
|
class LongRunningJob < Base
|
|
19
|
-
# Rules around
|
|
19
|
+
# Rules around long-running job settings
|
|
20
20
|
class Contract < Contracts::Base
|
|
21
21
|
configure do |config|
|
|
22
22
|
config.error_messages = YAML.safe_load(
|
|
@@ -0,0 +1,32 @@
|
|
|
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 Throttling < Base
|
|
19
|
+
# Throttling feature configuration
|
|
20
|
+
Config = Struct.new(
|
|
21
|
+
:active,
|
|
22
|
+
:limit,
|
|
23
|
+
:interval,
|
|
24
|
+
keyword_init: true
|
|
25
|
+
) do
|
|
26
|
+
alias_method :active?, :active
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
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 Throttling < Base
|
|
19
|
+
# Rules around throttling settings
|
|
20
|
+
class Contract < Contracts::Base
|
|
21
|
+
configure do |config|
|
|
22
|
+
config.error_messages = YAML.safe_load(
|
|
23
|
+
File.read(
|
|
24
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
|
25
|
+
)
|
|
26
|
+
).fetch('en').fetch('validations').fetch('topic')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
nested(:throttling) do
|
|
30
|
+
required(:active) { |val| [true, false].include?(val) }
|
|
31
|
+
required(:interval) { |val| val.is_a?(Integer) && val.positive? }
|
|
32
|
+
required(:limit) do |val|
|
|
33
|
+
(val.is_a?(Integer) || val == Float::INFINITY) && val.positive?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
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 Throttling < Base
|
|
19
|
+
# Topic throttling API extensions
|
|
20
|
+
module Topic
|
|
21
|
+
# @param limit [Integer] max messages to process in an time interval
|
|
22
|
+
# @param interval [Integer] time interval for processing
|
|
23
|
+
def throttling(
|
|
24
|
+
limit: Float::INFINITY,
|
|
25
|
+
interval: 60_000
|
|
26
|
+
)
|
|
27
|
+
# Those settings are used for validation
|
|
28
|
+
@throttling ||= begin
|
|
29
|
+
config = Config.new(
|
|
30
|
+
active: limit != Float::INFINITY,
|
|
31
|
+
limit: limit,
|
|
32
|
+
interval: interval
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# If someone defined throttling setup, we need to create appropriate filter for it
|
|
36
|
+
# and inject it via filtering feature
|
|
37
|
+
if config.active?
|
|
38
|
+
factory = ->(*) { Pro::Processing::Filters::Throttler.new(limit, interval) }
|
|
39
|
+
filter(factory)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
config
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Just an alias for nice API
|
|
47
|
+
#
|
|
48
|
+
# @param args [Array] Anything `#throttling` accepts
|
|
49
|
+
def throttle(**args)
|
|
50
|
+
throttling(**args)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [Boolean] is a given job throttled
|
|
54
|
+
def throttling?
|
|
55
|
+
throttling.active?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [Hash] topic with all its native configuration options plus throttling
|
|
59
|
+
def to_h
|
|
60
|
+
super.merge(
|
|
61
|
+
throttling: throttling.to_h
|
|
62
|
+
).freeze
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
# Ability to throttle ingestion of data per topic partition
|
|
19
|
+
# Useful when we have fixed limit of things we can process in a given time period without
|
|
20
|
+
# getting into trouble. It can be used for example to:
|
|
21
|
+
# - make sure we do not insert things to DB too fast
|
|
22
|
+
# - make sure we do not dispatch HTTP requests to external resources too fast
|
|
23
|
+
#
|
|
24
|
+
# This feature is virtual. It materializes itself via the `Filtering` feature.
|
|
25
|
+
class Throttling < Base
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -10,57 +10,65 @@ 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
|
|
15
|
-
|
|
16
|
-
attr_reader :seek_offset
|
|
13
|
+
attr_reader :pause_tracker, :seek_offset, :topic, :partition
|
|
17
14
|
|
|
15
|
+
# @param topic [Karafka::Routing::Topic]
|
|
16
|
+
# @param partition [Integer]
|
|
18
17
|
# @param pause_tracker [Karafka::TimeTrackers::Pause] pause tracker for given topic partition
|
|
19
|
-
def initialize(pause_tracker)
|
|
18
|
+
def initialize(topic, partition, pause_tracker)
|
|
19
|
+
@topic = topic
|
|
20
|
+
@partition = partition
|
|
20
21
|
@pause_tracker = pause_tracker
|
|
21
22
|
@revoked = false
|
|
22
23
|
@consumptions = {}
|
|
23
24
|
@running_jobs = 0
|
|
24
25
|
@manual_pause = false
|
|
25
26
|
@mutex = Mutex.new
|
|
27
|
+
@marked = false
|
|
28
|
+
@failure = false
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
# Starts the coordinator for given consumption jobs
|
|
29
32
|
# @param messages [Array<Karafka::Messages::Message>] batch of message for which we are
|
|
30
33
|
# going to coordinate work. Not used with regular coordinator.
|
|
31
34
|
def start(messages)
|
|
32
|
-
@
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
49
54
|
end
|
|
50
55
|
|
|
51
56
|
# @param offset [Integer] message offset
|
|
52
57
|
def seek_offset=(offset)
|
|
53
|
-
|
|
58
|
+
synchronize do
|
|
59
|
+
@marked = true
|
|
60
|
+
@seek_offset = offset
|
|
61
|
+
end
|
|
54
62
|
end
|
|
55
63
|
|
|
56
64
|
# Increases number of jobs that we handle with this coordinator
|
|
57
65
|
def increment
|
|
58
|
-
|
|
66
|
+
synchronize { @running_jobs += 1 }
|
|
59
67
|
end
|
|
60
68
|
|
|
61
69
|
# Decrements number of jobs we handle at the moment
|
|
62
70
|
def decrement
|
|
63
|
-
|
|
71
|
+
synchronize do
|
|
64
72
|
@running_jobs -= 1
|
|
65
73
|
|
|
66
74
|
return @running_jobs unless @running_jobs.negative?
|
|
@@ -72,8 +80,10 @@ module Karafka
|
|
|
72
80
|
end
|
|
73
81
|
|
|
74
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.
|
|
75
85
|
def success?
|
|
76
|
-
|
|
86
|
+
synchronize do
|
|
77
87
|
@running_jobs.zero? && @consumptions.values.all?(&:success?)
|
|
78
88
|
end
|
|
79
89
|
end
|
|
@@ -81,7 +91,7 @@ module Karafka
|
|
|
81
91
|
# Mark given consumption on consumer as successful
|
|
82
92
|
# @param consumer [Karafka::BaseConsumer] consumer that finished successfully
|
|
83
93
|
def success!(consumer)
|
|
84
|
-
|
|
94
|
+
synchronize do
|
|
85
95
|
consumption(consumer).success!
|
|
86
96
|
end
|
|
87
97
|
end
|
|
@@ -90,11 +100,17 @@ module Karafka
|
|
|
90
100
|
# @param consumer [Karafka::BaseConsumer] consumer that failed
|
|
91
101
|
# @param error [StandardError] error that occurred
|
|
92
102
|
def failure!(consumer, error)
|
|
93
|
-
|
|
103
|
+
synchronize do
|
|
104
|
+
@failure = true
|
|
94
105
|
consumption(consumer).failure!(error)
|
|
95
106
|
end
|
|
96
107
|
end
|
|
97
108
|
|
|
109
|
+
# @return [Boolean] true if any of work we were running failed
|
|
110
|
+
def failure?
|
|
111
|
+
@failure
|
|
112
|
+
end
|
|
113
|
+
|
|
98
114
|
# Marks given coordinator for processing group as revoked
|
|
99
115
|
#
|
|
100
116
|
# This is invoked in two places:
|
|
@@ -105,7 +121,7 @@ module Karafka
|
|
|
105
121
|
# listener loop dispatching the revocation job. It is ok, as effectively nothing will be
|
|
106
122
|
# processed until revocation jobs are done.
|
|
107
123
|
def revoke
|
|
108
|
-
|
|
124
|
+
synchronize { @revoked = true }
|
|
109
125
|
end
|
|
110
126
|
|
|
111
127
|
# @return [Boolean] is the partition we are processing revoked or not
|
|
@@ -113,10 +129,18 @@ module Karafka
|
|
|
113
129
|
@revoked
|
|
114
130
|
end
|
|
115
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
|
+
|
|
116
140
|
# Store in the coordinator info, that this pause was done manually by the end user and not
|
|
117
141
|
# by the system itself
|
|
118
142
|
def manual_pause
|
|
119
|
-
@
|
|
143
|
+
@manual_pause = true
|
|
120
144
|
end
|
|
121
145
|
|
|
122
146
|
# @return [Boolean] are we in a pause that was initiated by the user
|
|
@@ -124,6 +148,12 @@ module Karafka
|
|
|
124
148
|
@pause_tracker.paused? && @manual_pause
|
|
125
149
|
end
|
|
126
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
|
+
|
|
127
157
|
private
|
|
128
158
|
|
|
129
159
|
# @param consumer [Object] karafka consumer (normal or pro)
|
|
@@ -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,20 +25,17 @@ 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,
|
|
33
|
+
# @param coordinator [Karafka::Processing::Coordinator]
|
|
34
|
+
def initialize(group_id, client, coordinator)
|
|
34
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
|
# Allows us to prepare the consumer in the listener thread prior to the job being send to
|
|
@@ -42,8 +43,7 @@ module Karafka
|
|
|
42
43
|
# queue as it could cause starvation.
|
|
43
44
|
#
|
|
44
45
|
# @param messages [Array<Karafka::Messages::Message>]
|
|
45
|
-
|
|
46
|
-
def before_enqueue(messages, coordinator)
|
|
46
|
+
def before_enqueue(messages)
|
|
47
47
|
# the moment we've received the batch or actually the moment we've enqueued it,
|
|
48
48
|
# but good enough
|
|
49
49
|
@enqueued_at = Time.now
|
|
@@ -54,12 +54,10 @@ module Karafka
|
|
|
54
54
|
# middle state, where re-creation of a consumer instance would occur only sometimes
|
|
55
55
|
@consumer = nil unless ::Karafka::App.config.consumer_persistence
|
|
56
56
|
|
|
57
|
-
consumer.coordinator = coordinator
|
|
58
|
-
|
|
59
57
|
# First we build messages batch...
|
|
60
58
|
consumer.messages = Messages::Builders::Messages.call(
|
|
61
59
|
messages,
|
|
62
|
-
|
|
60
|
+
coordinator.topic,
|
|
63
61
|
@enqueued_at
|
|
64
62
|
)
|
|
65
63
|
|
|
@@ -82,6 +80,13 @@ module Karafka
|
|
|
82
80
|
consumer.on_after_consume
|
|
83
81
|
end
|
|
84
82
|
|
|
83
|
+
# Runs consumer idle operations
|
|
84
|
+
# This may include house-keeping or other state management changes that can occur but that
|
|
85
|
+
# not mean there are any new messages available for the end user to process
|
|
86
|
+
def idle
|
|
87
|
+
consumer.on_idle
|
|
88
|
+
end
|
|
89
|
+
|
|
85
90
|
# Runs the controller `#revoked` method that should be triggered when a given consumer is
|
|
86
91
|
# no longer needed due to partitions reassignment.
|
|
87
92
|
#
|
|
@@ -114,15 +119,17 @@ module Karafka
|
|
|
114
119
|
# @return [Object] cached consumer instance
|
|
115
120
|
def consumer
|
|
116
121
|
@consumer ||= begin
|
|
117
|
-
|
|
122
|
+
topic = @coordinator.topic
|
|
123
|
+
|
|
124
|
+
strategy = ::Karafka::App.config.internal.processing.strategy_selector.find(topic)
|
|
118
125
|
|
|
119
|
-
consumer =
|
|
126
|
+
consumer = topic.consumer_class.new
|
|
120
127
|
# We use singleton class as the same consumer class may be used to process different
|
|
121
128
|
# topics with different settings
|
|
122
129
|
consumer.singleton_class.include(strategy)
|
|
123
|
-
consumer.topic = @topic
|
|
124
130
|
consumer.client = @client
|
|
125
131
|
consumer.producer = ::Karafka::App.producer
|
|
132
|
+
consumer.coordinator = @coordinator
|
|
126
133
|
|
|
127
134
|
consumer
|
|
128
135
|
end
|