karafka 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/ci.yml +40 -16
- data/.rspec +4 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +459 -796
- data/Gemfile +3 -2
- data/Gemfile.lock +44 -32
- data/LICENSE +1 -1
- data/README.md +22 -17
- data/bin/integrations +86 -26
- data/bin/karafka +1 -13
- data/bin/rspecs +6 -0
- data/bin/verify_license_integrity +37 -0
- data/certs/cert_chain.pem +26 -0
- data/config/{errors.yml → locales/errors.yml} +34 -6
- data/config/locales/pro_errors.yml +34 -0
- data/docker-compose.yml +4 -30
- data/karafka.gemspec +17 -10
- data/lib/active_job/karafka.rb +3 -7
- data/lib/active_job/queue_adapters/karafka_adapter.rb +7 -1
- data/lib/karafka/active_job/consumer.rb +29 -6
- data/lib/karafka/active_job/current_attributes/loading.rb +36 -0
- data/lib/karafka/active_job/current_attributes/persistence.rb +28 -0
- data/lib/karafka/active_job/current_attributes.rb +42 -0
- data/lib/karafka/active_job/dispatcher.rb +34 -3
- data/lib/karafka/active_job/job_options_contract.rb +13 -2
- data/lib/karafka/admin.rb +246 -0
- data/lib/karafka/app.rb +30 -7
- data/lib/karafka/base_consumer.rb +124 -82
- data/lib/karafka/cli/base.rb +20 -0
- data/lib/karafka/cli/console.rb +13 -8
- data/lib/karafka/cli/info.rb +3 -2
- data/lib/karafka/cli/server.rb +55 -8
- data/lib/karafka/cli/topics.rb +146 -0
- data/lib/karafka/connection/client.rb +118 -61
- data/lib/karafka/connection/consumer_group_coordinator.rb +48 -0
- data/lib/karafka/connection/listener.rb +78 -30
- data/lib/karafka/connection/listeners_batch.rb +18 -2
- data/lib/karafka/connection/rebalance_manager.rb +3 -0
- data/lib/karafka/contracts/config.rb +1 -1
- data/lib/karafka/contracts/consumer_group.rb +22 -9
- data/lib/karafka/contracts/server_cli_options.rb +71 -11
- data/lib/karafka/contracts/topic.rb +65 -0
- data/lib/karafka/contracts.rb +1 -1
- data/lib/karafka/embedded.rb +36 -0
- data/lib/karafka/errors.rb +6 -4
- data/lib/karafka/helpers/colorize.rb +6 -0
- data/lib/karafka/instrumentation/callbacks/error.rb +3 -4
- data/lib/karafka/instrumentation/callbacks/statistics.rb +14 -4
- data/lib/karafka/instrumentation/logger_listener.rb +151 -22
- data/lib/karafka/instrumentation/notifications.rb +26 -7
- data/lib/karafka/instrumentation/proctitle_listener.rb +7 -16
- data/lib/karafka/instrumentation/vendors/datadog/dashboard.json +1 -1
- data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +153 -0
- data/lib/karafka/instrumentation/vendors/datadog/{listener.rb → metrics_listener.rb} +62 -35
- data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +166 -0
- data/lib/karafka/licenser.rb +53 -50
- data/lib/karafka/messages/builders/batch_metadata.rb +8 -8
- data/lib/karafka/messages/builders/messages.rb +6 -2
- data/lib/karafka/messages/message.rb +14 -2
- data/lib/karafka/messages/messages.rb +5 -0
- data/lib/karafka/messages/parser.rb +14 -0
- data/lib/karafka/patches/rdkafka/bindings.rb +132 -0
- data/lib/karafka/pro/active_job/consumer.rb +10 -9
- data/lib/karafka/pro/active_job/dispatcher.rb +28 -3
- data/lib/karafka/pro/active_job/job_options_contract.rb +16 -3
- data/lib/karafka/pro/encryption/cipher.rb +58 -0
- data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
- data/lib/karafka/pro/encryption/errors.rb +24 -0
- data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
- data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
- data/lib/karafka/pro/encryption/setup/config.rb +48 -0
- data/lib/karafka/pro/encryption.rb +47 -0
- data/lib/karafka/pro/iterator.rb +253 -0
- data/lib/karafka/pro/loader.rb +52 -26
- data/lib/karafka/pro/performance_tracker.rb +10 -6
- data/lib/karafka/pro/processing/collapser.rb +62 -0
- data/lib/karafka/pro/processing/coordinator.rb +92 -16
- data/lib/karafka/pro/processing/filters/base.rb +61 -0
- data/lib/karafka/pro/processing/filters/delayer.rb +70 -0
- data/lib/karafka/pro/processing/filters/expirer.rb +51 -0
- data/lib/karafka/pro/processing/filters/throttler.rb +84 -0
- data/lib/karafka/pro/processing/filters/virtual_limiter.rb +52 -0
- data/lib/karafka/pro/processing/filters_applier.rb +104 -0
- data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +7 -5
- data/lib/karafka/pro/processing/jobs/revoked_non_blocking.rb +37 -0
- data/lib/karafka/pro/processing/jobs_builder.rb +22 -4
- data/lib/karafka/pro/processing/partitioner.rb +33 -24
- data/lib/karafka/pro/processing/scheduler.rb +27 -8
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom.rb +68 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom_vp.rb +74 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom.rb +72 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom_vp.rb +76 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom_vp.rb +68 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom_vp.rb +69 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom.rb +38 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom_vp.rb +64 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_mom.rb +38 -0
- data/lib/karafka/pro/processing/strategies/aj/ftr_mom_vp.rb +58 -0
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom.rb +37 -0
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +79 -0
- data/lib/karafka/pro/processing/strategies/aj/mom.rb +36 -0
- data/lib/karafka/pro/processing/strategies/aj/mom_vp.rb +52 -0
- data/lib/karafka/pro/processing/strategies/base.rb +26 -0
- data/lib/karafka/pro/processing/strategies/default.rb +105 -0
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +131 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr.rb +61 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj.rb +75 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +71 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom_vp.rb +43 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_vp.rb +41 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +69 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom_vp.rb +41 -0
- data/lib/karafka/pro/processing/strategies/dlq/ftr_vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +63 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +63 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom_vp.rb +36 -0
- data/lib/karafka/pro/processing/strategies/dlq/lrj_vp.rb +39 -0
- data/lib/karafka/pro/processing/strategies/dlq/mom.rb +68 -0
- data/lib/karafka/pro/processing/strategies/dlq/mom_vp.rb +37 -0
- data/lib/karafka/pro/processing/strategies/dlq/vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/ftr/default.rb +104 -0
- data/lib/karafka/pro/processing/strategies/ftr/vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/lrj/default.rb +87 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr.rb +69 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_mom.rb +67 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_mom_vp.rb +40 -0
- data/lib/karafka/pro/processing/strategies/lrj/ftr_vp.rb +39 -0
- data/lib/karafka/pro/processing/strategies/lrj/mom.rb +80 -0
- data/lib/karafka/pro/processing/strategies/lrj/mom_vp.rb +38 -0
- data/lib/karafka/pro/processing/strategies/lrj/vp.rb +36 -0
- data/lib/karafka/pro/processing/strategies/mom/default.rb +46 -0
- data/lib/karafka/pro/processing/strategies/mom/ftr.rb +53 -0
- data/lib/karafka/pro/processing/strategies/mom/ftr_vp.rb +37 -0
- data/lib/karafka/pro/processing/strategies/mom/vp.rb +35 -0
- data/lib/karafka/pro/processing/strategies/vp/default.rb +104 -0
- data/lib/karafka/pro/{contracts/base.rb → processing/strategies.rb} +6 -5
- data/lib/karafka/pro/processing/strategy_selector.rb +84 -0
- data/lib/karafka/pro/processing/virtual_offset_manager.rb +147 -0
- data/lib/karafka/pro/routing/features/base.rb +24 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +50 -0
- data/lib/karafka/pro/routing/features/dead_letter_queue.rb +27 -0
- data/lib/karafka/pro/routing/{builder_extensions.rb → features/delaying/config.rb} +8 -11
- data/lib/karafka/pro/routing/features/delaying/contract.rb +38 -0
- data/lib/karafka/pro/routing/features/delaying/topic.rb +59 -0
- data/lib/karafka/pro/routing/features/delaying.rb +29 -0
- data/lib/karafka/pro/routing/features/expiring/config.rb +27 -0
- data/lib/karafka/pro/routing/features/expiring/contract.rb +38 -0
- data/lib/karafka/pro/routing/features/expiring/topic.rb +59 -0
- data/lib/karafka/pro/routing/features/expiring.rb +27 -0
- data/lib/karafka/pro/routing/features/filtering/config.rb +40 -0
- data/lib/karafka/pro/routing/features/filtering/contract.rb +41 -0
- data/lib/karafka/pro/routing/features/filtering/topic.rb +51 -0
- data/lib/karafka/pro/routing/features/filtering.rb +27 -0
- data/lib/karafka/pro/routing/features/long_running_job/config.rb +28 -0
- data/lib/karafka/pro/routing/features/long_running_job/contract.rb +37 -0
- data/lib/karafka/pro/routing/features/long_running_job/topic.rb +42 -0
- data/lib/karafka/pro/routing/features/long_running_job.rb +28 -0
- data/lib/karafka/pro/routing/features/throttling/config.rb +32 -0
- data/lib/karafka/pro/routing/features/throttling/contract.rb +41 -0
- data/lib/karafka/pro/routing/features/throttling/topic.rb +69 -0
- data/lib/karafka/pro/routing/features/throttling.rb +30 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/config.rb +30 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +52 -0
- data/lib/karafka/pro/routing/features/virtual_partitions/topic.rb +56 -0
- data/lib/karafka/pro/routing/features/virtual_partitions.rb +27 -0
- data/lib/karafka/process.rb +23 -8
- data/lib/karafka/processing/coordinator.rb +102 -23
- data/lib/karafka/processing/coordinators_buffer.rb +5 -1
- data/lib/karafka/processing/executor.rb +51 -21
- data/lib/karafka/processing/executors_buffer.rb +10 -26
- data/lib/karafka/processing/jobs/base.rb +6 -2
- data/lib/karafka/processing/jobs/consume.rb +8 -5
- data/lib/karafka/processing/jobs/idle.rb +24 -0
- data/lib/karafka/processing/jobs_builder.rb +2 -3
- data/lib/karafka/processing/jobs_queue.rb +14 -8
- data/lib/karafka/processing/partitioner.rb +3 -1
- data/lib/karafka/processing/result.rb +14 -1
- data/lib/karafka/processing/strategies/aj_dlq_mom.rb +44 -0
- data/lib/karafka/processing/strategies/aj_mom.rb +21 -0
- data/lib/karafka/processing/strategies/base.rb +52 -0
- data/lib/karafka/processing/strategies/default.rb +142 -0
- data/lib/karafka/processing/strategies/dlq.rb +88 -0
- data/lib/karafka/processing/strategies/dlq_mom.rb +49 -0
- data/lib/karafka/processing/strategies/mom.rb +29 -0
- data/lib/karafka/processing/strategy_selector.rb +47 -0
- data/lib/karafka/processing/worker.rb +8 -3
- data/lib/karafka/railtie.rb +24 -12
- data/lib/karafka/routing/activity_manager.rb +84 -0
- data/lib/karafka/routing/builder.rb +20 -0
- data/lib/karafka/routing/consumer_group.rb +38 -4
- data/lib/karafka/routing/consumer_mapper.rb +0 -10
- data/lib/karafka/routing/features/active_job/builder.rb +33 -0
- data/lib/karafka/routing/features/active_job/config.rb +15 -0
- data/lib/karafka/routing/features/active_job/contract.rb +41 -0
- data/lib/karafka/routing/features/active_job/topic.rb +33 -0
- data/lib/karafka/routing/features/active_job.rb +13 -0
- data/lib/karafka/routing/features/base/expander.rb +53 -0
- data/lib/karafka/routing/features/base.rb +34 -0
- data/lib/karafka/routing/features/dead_letter_queue/config.rb +19 -0
- data/lib/karafka/routing/features/dead_letter_queue/contract.rb +42 -0
- data/lib/karafka/routing/features/dead_letter_queue/topic.rb +41 -0
- data/lib/karafka/routing/features/dead_letter_queue.rb +16 -0
- data/lib/karafka/routing/features/declaratives/config.rb +18 -0
- data/lib/karafka/routing/features/declaratives/contract.rb +30 -0
- data/lib/karafka/routing/features/declaratives/topic.rb +44 -0
- data/lib/karafka/routing/features/declaratives.rb +14 -0
- data/lib/karafka/routing/features/manual_offset_management/config.rb +15 -0
- data/lib/karafka/routing/features/manual_offset_management/contract.rb +24 -0
- data/lib/karafka/routing/features/manual_offset_management/topic.rb +35 -0
- data/lib/karafka/routing/features/manual_offset_management.rb +18 -0
- data/lib/karafka/routing/proxy.rb +18 -20
- data/lib/karafka/routing/router.rb +27 -2
- data/lib/karafka/routing/subscription_group.rb +43 -5
- data/lib/karafka/routing/subscription_groups_builder.rb +6 -1
- data/lib/karafka/routing/topic.rb +17 -5
- data/lib/karafka/routing/topics.rb +8 -0
- data/lib/karafka/runner.rb +1 -0
- data/lib/karafka/serialization/json/deserializer.rb +1 -0
- data/lib/karafka/server.rb +58 -28
- data/lib/karafka/setup/attributes_map.rb +344 -0
- data/lib/karafka/setup/config.rb +48 -19
- data/lib/karafka/status.rb +35 -7
- data/lib/karafka/templates/karafka.rb.erb +14 -6
- data/lib/karafka/time_trackers/base.rb +1 -6
- data/lib/karafka/time_trackers/pause.rb +49 -19
- data/lib/karafka/time_trackers/poll.rb +7 -3
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +56 -3
- data/renovate.json +6 -0
- data.tar.gz.sig +0 -0
- metadata +189 -61
- metadata.gz.sig +0 -0
- data/bin/wait_for_kafka +0 -20
- data/certs/mensfeld.pem +0 -25
- data/lib/karafka/active_job/routing/extensions.rb +0 -31
- data/lib/karafka/contracts/consumer_group_topic.rb +0 -42
- data/lib/karafka/instrumentation.rb +0 -21
- data/lib/karafka/pro/base_consumer.rb +0 -82
- data/lib/karafka/pro/contracts/consumer_group.rb +0 -34
- data/lib/karafka/pro/contracts/consumer_group_topic.rb +0 -33
- data/lib/karafka/pro/routing/topic_extensions.rb +0 -38
|
@@ -0,0 +1,46 @@
|
|
|
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 Encryption
|
|
17
|
+
# Encryption related messages components
|
|
18
|
+
module Messages
|
|
19
|
+
# Middleware for WaterDrop. It automatically encrypts messages payload.
|
|
20
|
+
# It is injected only if encryption is enabled.
|
|
21
|
+
class Middleware
|
|
22
|
+
# @param message [Hash] WaterDrop message hash
|
|
23
|
+
# @return [Hash] hash with encrypted payload and encryption version indicator
|
|
24
|
+
def call(message)
|
|
25
|
+
message[:headers] ||= {}
|
|
26
|
+
message[:headers]['encryption'] = version
|
|
27
|
+
message[:payload] = cipher.encrypt(message[:payload])
|
|
28
|
+
message
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# @return [::Karafka::Pro::Encryption::Cipher]
|
|
34
|
+
def cipher
|
|
35
|
+
@cipher ||= ::Karafka::App.config.encryption.cipher
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [String] encryption version
|
|
39
|
+
def version
|
|
40
|
+
@version ||= ::Karafka::App.config.encryption.version
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
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 Encryption
|
|
17
|
+
module Messages
|
|
18
|
+
# Pro parser that takes into consideration encryption usage
|
|
19
|
+
# @note There may be a case where someone decides not to encrypt data and we start getting
|
|
20
|
+
# unencrypted payloads. That is why we always rely on message headers for encryption
|
|
21
|
+
# indication.
|
|
22
|
+
class Parser < ::Karafka::Messages::Parser
|
|
23
|
+
# @param message [::Karafka::Messages::Message]
|
|
24
|
+
# @return [Object] deserialized payload
|
|
25
|
+
def call(message)
|
|
26
|
+
if active? && message.headers.key?('encryption')
|
|
27
|
+
# Decrypt raw payload so it can be handled by the default parser logic
|
|
28
|
+
message.raw_payload = cipher.decrypt(
|
|
29
|
+
message.headers['encryption'],
|
|
30
|
+
message.raw_payload
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
super(message)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# @return [::Karafka::Pro::Encryption::Cipher]
|
|
40
|
+
def cipher
|
|
41
|
+
@cipher ||= ::Karafka::App.config.encryption.cipher
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [Boolean] is encryption active
|
|
45
|
+
def active?
|
|
46
|
+
return @active unless @active.nil?
|
|
47
|
+
|
|
48
|
+
@active = ::Karafka::App.config.encryption.active
|
|
49
|
+
|
|
50
|
+
@active
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
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 Encryption
|
|
17
|
+
# Setup and config related encryption components
|
|
18
|
+
module Setup
|
|
19
|
+
# Config for encryption
|
|
20
|
+
class Config
|
|
21
|
+
extend ::Karafka::Core::Configurable
|
|
22
|
+
|
|
23
|
+
# Should this feature be in use
|
|
24
|
+
setting(:active, default: false)
|
|
25
|
+
|
|
26
|
+
# Supporting versions allows us to be able to rotate private and public keys in case
|
|
27
|
+
# we would need this. We can increase the version, rotate and Karafka when decrypting
|
|
28
|
+
# will figure out proper private key based on the version
|
|
29
|
+
setting(:version, default: '1')
|
|
30
|
+
|
|
31
|
+
# We always support one public key for producing messages
|
|
32
|
+
# Public key needs to be always present even if we do not plan to produce messages from
|
|
33
|
+
# a Karafka process. This is because of the web-ui and potentially other cases like this
|
|
34
|
+
setting(:public_key, default: '')
|
|
35
|
+
|
|
36
|
+
# Private keys in pem format, where the key is the version and value is the key.
|
|
37
|
+
# This allows us to support key rotation
|
|
38
|
+
setting(:private_keys, default: {})
|
|
39
|
+
|
|
40
|
+
# Cipher used to encrypt and decrypt data
|
|
41
|
+
setting(:cipher, default: Encryption::Cipher.new)
|
|
42
|
+
|
|
43
|
+
configure
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
# Out of the box encryption engine for both Karafka and WaterDrop
|
|
17
|
+
# It uses asymmetric encryption via RSA. We use asymmetric so we can have producers that won't
|
|
18
|
+
# have ability (when private key not added) to decrypt messages.
|
|
19
|
+
module Encryption
|
|
20
|
+
class << self
|
|
21
|
+
# Sets up additional config scope, validations and other things
|
|
22
|
+
#
|
|
23
|
+
# @param config [Karafka::Core::Configurable::Node] root node config
|
|
24
|
+
def pre_setup(config)
|
|
25
|
+
# Expand the config with this feature specific stuff
|
|
26
|
+
config.instance_eval do
|
|
27
|
+
setting(:encryption, default: Setup::Config.config)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @param config [Karafka::Core::Configurable::Node] root node config
|
|
32
|
+
def post_setup(config)
|
|
33
|
+
Encryption::Contracts::Config.new.validate!(config.to_h)
|
|
34
|
+
|
|
35
|
+
# Don't inject extra components if encryption is not active
|
|
36
|
+
return unless config.encryption.active
|
|
37
|
+
|
|
38
|
+
# This parser is encryption aware
|
|
39
|
+
config.internal.messages.parser = Messages::Parser.new
|
|
40
|
+
|
|
41
|
+
# Encryption for WaterDrop
|
|
42
|
+
config.producer.middleware.append(Messages::Middleware.new)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,253 @@
|
|
|
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
|
+
# Topic iterator allows you to iterate over topic/partition data and perform lookups for
|
|
17
|
+
# information that you need.
|
|
18
|
+
#
|
|
19
|
+
# It supports early stops on finding the requested data and allows for seeking till
|
|
20
|
+
# the end. It also allows for signaling, when a given message should be last out of certain
|
|
21
|
+
# partition, but we still want to continue iterating in other messages.
|
|
22
|
+
#
|
|
23
|
+
# It does **not** create a consumer group and does not have any offset management.
|
|
24
|
+
class Iterator
|
|
25
|
+
# Local partition reference for librdkafka
|
|
26
|
+
Partition = Struct.new(:partition, :offset)
|
|
27
|
+
|
|
28
|
+
private_constant :Partition
|
|
29
|
+
|
|
30
|
+
# A simple API allowing to iterate over topic/partition data, without having to subscribe
|
|
31
|
+
# and deal with rebalances. This API allows for multi-partition streaming and is optimized
|
|
32
|
+
# for data lookups. It allows for explicit stopping iteration over any partition during
|
|
33
|
+
# the iteration process, allowing for optimized lookups.
|
|
34
|
+
#
|
|
35
|
+
# @param topics [Array<String>, Hash] list of strings if we want to subscribe to multiple
|
|
36
|
+
# topics and all of their partitions or a hash where keys are the topics and values are
|
|
37
|
+
# hashes with partitions and their initial offsets.
|
|
38
|
+
# @param settings [Hash] extra settings for the consumer. Please keep in mind, that if
|
|
39
|
+
# overwritten, you may want to include `auto.offset.reset` to match your case.
|
|
40
|
+
# @param yield_nil [Boolean] should we yield also `nil` values when poll returns nothing.
|
|
41
|
+
# Useful in particular for long-living iterators.
|
|
42
|
+
#
|
|
43
|
+
# @note It is worth keeping in mind, that this API also needs to operate within
|
|
44
|
+
# `max.poll.interval.ms` limitations on each iteration
|
|
45
|
+
#
|
|
46
|
+
# @note In case of a never-ending iterator, you need to set `enable.partition.eof` to `false`
|
|
47
|
+
# so we don't stop polling data even when reaching the end (end on a given moment)
|
|
48
|
+
def initialize(
|
|
49
|
+
topics,
|
|
50
|
+
settings: { 'auto.offset.reset': 'beginning' },
|
|
51
|
+
yield_nil: false
|
|
52
|
+
)
|
|
53
|
+
@topics_with_partitions = expand_topics_with_partitions(topics)
|
|
54
|
+
|
|
55
|
+
@routing_topics = @topics_with_partitions.map do |name, _|
|
|
56
|
+
[name, ::Karafka::Routing::Router.find_or_initialize_by_name(name)]
|
|
57
|
+
end.to_h
|
|
58
|
+
|
|
59
|
+
@total_partitions = @topics_with_partitions.map(&:last).sum(&:count)
|
|
60
|
+
|
|
61
|
+
@stopped_partitions = 0
|
|
62
|
+
|
|
63
|
+
@settings = settings
|
|
64
|
+
@yield_nil = yield_nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Iterates over requested topic partitions and yields the results with the iterator itself
|
|
68
|
+
# Iterator instance is yielded because one can run `stop_partition` to stop iterating over
|
|
69
|
+
# part of data. It is useful for scenarios where we are looking for some information in all
|
|
70
|
+
# the partitions but once we found it, given partition data is no longer needed and would
|
|
71
|
+
# only eat up resources.
|
|
72
|
+
def each
|
|
73
|
+
Admin.with_consumer(@settings) do |consumer|
|
|
74
|
+
tpl = tpl_with_expanded_offsets(consumer)
|
|
75
|
+
consumer.assign(tpl)
|
|
76
|
+
|
|
77
|
+
# We need this for self-referenced APIs like pausing
|
|
78
|
+
@current_consumer = consumer
|
|
79
|
+
|
|
80
|
+
# Stream data until we reach the end of all the partitions or until the end user
|
|
81
|
+
# indicates that they are done
|
|
82
|
+
until done?
|
|
83
|
+
message = poll(200)
|
|
84
|
+
|
|
85
|
+
# Skip nils if not explicitly required
|
|
86
|
+
next if message.nil? && !@yield_nil
|
|
87
|
+
|
|
88
|
+
if message
|
|
89
|
+
@current_message = build_message(message)
|
|
90
|
+
|
|
91
|
+
yield(@current_message, self)
|
|
92
|
+
else
|
|
93
|
+
yield(nil, self)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
@current_message = nil
|
|
98
|
+
@current_consumer = nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Reset so we can use the same iterator again if needed
|
|
102
|
+
@stopped_partitions = 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Stops the partition we're currently yielded into
|
|
106
|
+
def stop_current_partition
|
|
107
|
+
stop_partition(
|
|
108
|
+
@current_message.topic,
|
|
109
|
+
@current_message.partition
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Stops processing of a given partition
|
|
114
|
+
# We expect the partition to be provided because of a scenario, where there is a
|
|
115
|
+
# multi-partition iteration and we want to stop a different partition that the one that
|
|
116
|
+
# is currently yielded.
|
|
117
|
+
#
|
|
118
|
+
# We pause it forever and no longer work with it.
|
|
119
|
+
#
|
|
120
|
+
# @param name [String] topic name of which partition we want to stop
|
|
121
|
+
# @param partition [Integer] partition we want to stop processing
|
|
122
|
+
def stop_partition(name, partition)
|
|
123
|
+
@stopped_partitions += 1
|
|
124
|
+
|
|
125
|
+
@current_consumer.pause(
|
|
126
|
+
Rdkafka::Consumer::TopicPartitionList.new(
|
|
127
|
+
name => [Partition.new(partition, 0)]
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
# Expands topics to which we want to subscribe with partitions information in case this
|
|
135
|
+
# info is not provided. For our convenience we want to support 5 formats of defining
|
|
136
|
+
# the subscribed topics:
|
|
137
|
+
#
|
|
138
|
+
# - 'topic1' - just a string with one topic name
|
|
139
|
+
# - ['topic1', 'topic2'] - just the names
|
|
140
|
+
# - { 'topic1' => -100 } - names with negative lookup offset
|
|
141
|
+
# - { 'topic1' => { 0 => 5 } } - names with exact partitions offsets
|
|
142
|
+
# - { 'topic1' => { 0 => -5 }, 'topic2' => { 1 => 5 } } - with per partition negative offsets
|
|
143
|
+
#
|
|
144
|
+
# @param topics [Array, Hash] topics definitions
|
|
145
|
+
# @return [Hash] hash with topics containing partitions definitions
|
|
146
|
+
def expand_topics_with_partitions(topics)
|
|
147
|
+
# Simplification for the single topic case
|
|
148
|
+
topics = [topics] if topics.is_a?(String)
|
|
149
|
+
# If we've got just array with topics, we need to convert that into a representation
|
|
150
|
+
# that we can expand with offsets
|
|
151
|
+
topics = topics.map { |name| [name, false] }.to_h if topics.is_a?(Array)
|
|
152
|
+
|
|
153
|
+
expanded = Hash.new { |h, k| h[k] = {} }
|
|
154
|
+
|
|
155
|
+
topics.map do |topic, details|
|
|
156
|
+
if details.is_a?(Hash)
|
|
157
|
+
details.each do |partition, offset|
|
|
158
|
+
expanded[topic][partition] = offset
|
|
159
|
+
end
|
|
160
|
+
else
|
|
161
|
+
partition_count(topic.to_s).times do |partition|
|
|
162
|
+
# If no offsets are provided, we just start from zero
|
|
163
|
+
expanded[topic][partition] = details || 0
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
expanded
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# @param timeout [Integer] timeout in ms
|
|
172
|
+
# @return [Rdkafka::Consumer::Message, nil] message or nil if nothing to do
|
|
173
|
+
def poll(timeout)
|
|
174
|
+
@current_consumer.poll(timeout)
|
|
175
|
+
rescue Rdkafka::RdkafkaError => e
|
|
176
|
+
# End of partition
|
|
177
|
+
if e.code == :partition_eof
|
|
178
|
+
@stopped_partitions += 1
|
|
179
|
+
|
|
180
|
+
retry
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
raise e
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Converts raw rdkafka message into Karafka message
|
|
187
|
+
#
|
|
188
|
+
# @param message [Rdkafka::Consumer::Message] raw rdkafka message
|
|
189
|
+
# @return [::Karafka::Messages::Message]
|
|
190
|
+
def build_message(message)
|
|
191
|
+
Messages::Builders::Message.call(
|
|
192
|
+
message,
|
|
193
|
+
@routing_topics.fetch(message.topic),
|
|
194
|
+
Time.now
|
|
195
|
+
)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Do we have all the data we wanted or did every topic partition has reached eof.
|
|
199
|
+
# @return [Boolean]
|
|
200
|
+
def done?
|
|
201
|
+
@stopped_partitions >= @total_partitions
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Builds the tpl representing all the subscriptions we want to run
|
|
205
|
+
#
|
|
206
|
+
# Additionally for negative offsets, does the watermark calculation where to start
|
|
207
|
+
#
|
|
208
|
+
# @param consumer [Rdkafka::Consumer] consumer we need in case of negative offsets as
|
|
209
|
+
# negative are going to be used to do "give me last X". We use the already initialized
|
|
210
|
+
# consumer instance, not to start another one again.
|
|
211
|
+
# @return [Rdkafka::Consumer::TopicPartitionList]
|
|
212
|
+
def tpl_with_expanded_offsets(consumer)
|
|
213
|
+
tpl = Rdkafka::Consumer::TopicPartitionList.new
|
|
214
|
+
|
|
215
|
+
@topics_with_partitions.each do |name, partitions|
|
|
216
|
+
partitions_with_offsets = {}
|
|
217
|
+
|
|
218
|
+
# When no offsets defined, we just start from zero
|
|
219
|
+
if partitions.is_a?(Array) || partitions.is_a?(Range)
|
|
220
|
+
partitions_with_offsets = partitions.map { |partition| [partition, 0] }.to_h
|
|
221
|
+
else
|
|
222
|
+
# When offsets defined, we can either use them if positive or expand and move back
|
|
223
|
+
# in case of negative (-1000 means last 1000 messages, etc)
|
|
224
|
+
partitions.each do |partition, offset|
|
|
225
|
+
if offset.negative?
|
|
226
|
+
_, high_watermark_offset = consumer.query_watermark_offsets(name, partition)
|
|
227
|
+
# We add because this offset is negative
|
|
228
|
+
partitions_with_offsets[partition] = high_watermark_offset + offset
|
|
229
|
+
else
|
|
230
|
+
partitions_with_offsets[partition] = offset
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
tpl.add_topic_and_partitions_with_offsets(name, partitions_with_offsets)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
tpl
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# @param name [String] topic name
|
|
242
|
+
# @return [Integer] number of partitions of the topic we want to iterate over
|
|
243
|
+
def partition_count(name)
|
|
244
|
+
Admin
|
|
245
|
+
.cluster_info
|
|
246
|
+
.topics
|
|
247
|
+
.find { |topic| topic.fetch(:topic_name) == name }
|
|
248
|
+
.fetch(:partitions)
|
|
249
|
+
.count
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
data/lib/karafka/pro/loader.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# This Karafka component is a Pro component.
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
4
6
|
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
5
7
|
# repository and their usage requires commercial license agreement.
|
|
6
8
|
#
|
|
@@ -13,43 +15,66 @@ module Karafka
|
|
|
13
15
|
module Pro
|
|
14
16
|
# Loader requires and loads all the pro components only when they are needed
|
|
15
17
|
class Loader
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
processing/scheduler
|
|
21
|
-
processing/jobs/consume_non_blocking
|
|
22
|
-
processing/jobs_builder
|
|
23
|
-
processing/coordinator
|
|
24
|
-
processing/partitioner
|
|
25
|
-
contracts/base
|
|
26
|
-
contracts/consumer_group
|
|
27
|
-
contracts/consumer_group_topic
|
|
28
|
-
routing/topic_extensions
|
|
29
|
-
routing/builder_extensions
|
|
30
|
-
active_job/consumer
|
|
18
|
+
# There seems to be a conflict in between using two Zeitwerk instances and it makes lookups
|
|
19
|
+
# for nested namespaces instead of creating them.
|
|
20
|
+
# We require those not to deal with this and then all works as expected
|
|
21
|
+
FORCE_LOADED = %w[
|
|
31
22
|
active_job/dispatcher
|
|
32
|
-
|
|
23
|
+
processing/jobs/consume_non_blocking
|
|
24
|
+
processing/strategies/base
|
|
25
|
+
routing/features/base
|
|
26
|
+
encryption
|
|
27
|
+
encryption/cipher
|
|
28
|
+
encryption/setup/config
|
|
29
|
+
encryption/contracts/config
|
|
30
|
+
encryption/messages/parser
|
|
33
31
|
].freeze
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
# Zeitwerk pro loader
|
|
34
|
+
# We need to have one per process, that's why it's set as a constant
|
|
35
|
+
PRO_LOADER = Zeitwerk::Loader.new
|
|
36
|
+
|
|
37
|
+
private_constant :PRO_LOADER
|
|
36
38
|
|
|
37
39
|
class << self
|
|
40
|
+
# Requires all the components without using them anywhere
|
|
41
|
+
def require_all
|
|
42
|
+
FORCE_LOADED.each { |file| require_relative(file) }
|
|
43
|
+
|
|
44
|
+
PRO_LOADER.push_dir(Karafka.core_root.join('pro'), namespace: Karafka::Pro)
|
|
45
|
+
PRO_LOADER.setup
|
|
46
|
+
PRO_LOADER.eager_load
|
|
47
|
+
end
|
|
48
|
+
|
|
38
49
|
# Loads all the pro components and configures them wherever it is expected
|
|
39
50
|
# @param config [Karafka::Core::Configurable::Node] app config that we can alter with pro
|
|
40
51
|
# components
|
|
41
|
-
def
|
|
42
|
-
|
|
52
|
+
def pre_setup(config)
|
|
53
|
+
features.each { |feature| feature.pre_setup(config) }
|
|
43
54
|
|
|
44
55
|
reconfigure(config)
|
|
45
56
|
|
|
46
|
-
|
|
57
|
+
load_topic_features
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Runs post setup features configuration operations
|
|
61
|
+
#
|
|
62
|
+
# @param config [Karafka::Core::Configurable::Node]
|
|
63
|
+
def post_setup(config)
|
|
64
|
+
features.each { |feature| feature.post_setup(config) }
|
|
47
65
|
end
|
|
48
66
|
|
|
49
67
|
private
|
|
50
68
|
|
|
69
|
+
# @return [Array<Module>] extra non-routing related pro features
|
|
70
|
+
def features
|
|
71
|
+
[
|
|
72
|
+
Encryption
|
|
73
|
+
]
|
|
74
|
+
end
|
|
75
|
+
|
|
51
76
|
# Sets proper config options to use pro components
|
|
52
|
-
# @param config [
|
|
77
|
+
# @param config [::Karafka::Core::Configurable::Node] root config node
|
|
53
78
|
def reconfigure(config)
|
|
54
79
|
icfg = config.internal
|
|
55
80
|
|
|
@@ -57,6 +82,7 @@ module Karafka
|
|
|
57
82
|
icfg.processing.partitioner_class = Processing::Partitioner
|
|
58
83
|
icfg.processing.scheduler = Processing::Scheduler.new
|
|
59
84
|
icfg.processing.jobs_builder = Processing::JobsBuilder.new
|
|
85
|
+
icfg.processing.strategy_selector = Processing::StrategySelector.new
|
|
60
86
|
|
|
61
87
|
icfg.active_job.consumer_class = ActiveJob::Consumer
|
|
62
88
|
icfg.active_job.dispatcher = ActiveJob::Dispatcher.new
|
|
@@ -65,10 +91,10 @@ module Karafka
|
|
|
65
91
|
config.monitor.subscribe(PerformanceTracker.instance)
|
|
66
92
|
end
|
|
67
93
|
|
|
68
|
-
# Loads
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
::Karafka::Routing::
|
|
94
|
+
# Loads the Pro features of Karafka
|
|
95
|
+
# @note Object space lookup is not the fastest but we do it once during boot, so it's ok
|
|
96
|
+
def load_topic_features
|
|
97
|
+
::Karafka::Pro::Routing::Features::Base.load_all
|
|
72
98
|
end
|
|
73
99
|
end
|
|
74
100
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# This Karafka component is a Pro component.
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
4
6
|
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
5
7
|
# repository and their usage requires commercial license agreement.
|
|
6
8
|
#
|
|
@@ -23,11 +25,13 @@ module Karafka
|
|
|
23
25
|
|
|
24
26
|
# Builds up nested concurrent hash for data tracking
|
|
25
27
|
def initialize
|
|
26
|
-
@processing_times = Concurrent::
|
|
27
|
-
topics_hash
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
@processing_times = Concurrent::Map.new do |topics_hash, topic|
|
|
29
|
+
topics_hash.compute_if_absent(topic) do
|
|
30
|
+
Concurrent::Map.new do |partitions_hash, partition|
|
|
31
|
+
# This array does not have to be concurrent because we always access single
|
|
32
|
+
# partition data via instrumentation that operates in a single thread via consumer
|
|
33
|
+
partitions_hash.compute_if_absent(partition) { [] }
|
|
34
|
+
end
|
|
31
35
|
end
|
|
32
36
|
end
|
|
33
37
|
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
|
+
# Manages the collapse of virtual partitions
|
|
18
|
+
# Since any non-virtual partition is actually a virtual partition of size one, we can use
|
|
19
|
+
# it in a generic manner without having to distinguish between those cases.
|
|
20
|
+
#
|
|
21
|
+
# We need to have notion of the offset until we want to collapse because upon pause and retry
|
|
22
|
+
# rdkafka may purge the buffer. This means, that we may end up with smaller or bigger
|
|
23
|
+
# (different) dataset and without tracking the end of collapse, there would be a chance for
|
|
24
|
+
# things to flicker. Tracking allows us to ensure, that collapse is happening until all the
|
|
25
|
+
# messages from the corrupted batch are processed.
|
|
26
|
+
class Collapser
|
|
27
|
+
# When initialized, nothing is collapsed
|
|
28
|
+
def initialize
|
|
29
|
+
@collapsed = false
|
|
30
|
+
@until_offset = -1
|
|
31
|
+
@mutex = Mutex.new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Boolean] Should we collapse into a single consumer
|
|
35
|
+
def collapsed?
|
|
36
|
+
@collapsed
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Collapse until given offset. Until given offset is encountered or offset bigger than that
|
|
40
|
+
# we keep collapsing.
|
|
41
|
+
# @param offset [Integer] offset until which we keep the collapse
|
|
42
|
+
def collapse_until!(offset)
|
|
43
|
+
@mutex.synchronize do
|
|
44
|
+
# We check it here in case after a pause and re-fetch we would get less messages and
|
|
45
|
+
# one of them would cause an error. We do not want to overwrite the offset here unless
|
|
46
|
+
# it is bigger.
|
|
47
|
+
@until_offset = offset if offset > @until_offset
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Sets the collapse state based on the first collective offset that we are going to process
|
|
52
|
+
# and makes the decision whether or not we need to still keep the collapse.
|
|
53
|
+
# @param first_offset [Integer] first offset from a collective batch
|
|
54
|
+
def refresh!(first_offset)
|
|
55
|
+
@mutex.synchronize do
|
|
56
|
+
@collapsed = first_offset < @until_offset
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|