karafka 2.5.1 → 2.5.2
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
- data/.github/workflows/ci_linux_ubuntu_x86_64_gnu.yml +3 -29
- data/.github/workflows/ci_macos_arm64.yml +1 -1
- data/.github/workflows/push.yml +2 -2
- data/.github/workflows/trigger-wiki-refresh.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +14 -4
- data/Gemfile +0 -2
- data/Gemfile.lock +30 -31
- data/bin/integrations +2 -1
- data/bin/rspecs +4 -0
- data/config/locales/errors.yml +6 -4
- data/config/locales/pro_errors.yml +5 -4
- data/docker-compose.yml +1 -1
- data/examples/payloads/json/sample_set_02/download.json +191 -0
- data/examples/payloads/json/sample_set_03/event_type_1.json +18 -0
- data/examples/payloads/json/sample_set_03/event_type_2.json +263 -0
- data/examples/payloads/json/sample_set_03/event_type_3.json +41 -0
- data/karafka.gemspec +1 -1
- data/lib/active_job/queue_adapters/karafka_adapter.rb +1 -1
- data/lib/karafka/active_job/consumer.rb +5 -1
- data/lib/karafka/active_job/current_attributes/job_wrapper.rb +45 -0
- data/lib/karafka/active_job/current_attributes/loading.rb +1 -1
- data/lib/karafka/active_job/current_attributes/persistence.rb +19 -7
- data/lib/karafka/active_job/current_attributes.rb +1 -0
- data/lib/karafka/active_job/deserializer.rb +61 -0
- data/lib/karafka/active_job/dispatcher.rb +32 -12
- data/lib/karafka/active_job/job_options_contract.rb +2 -4
- data/lib/karafka/admin/acl.rb +8 -4
- data/lib/karafka/admin/configs/config.rb +6 -4
- data/lib/karafka/admin/consumer_groups.rb +74 -4
- data/lib/karafka/admin/topics.rb +40 -7
- data/lib/karafka/admin.rb +13 -4
- data/lib/karafka/base_consumer.rb +5 -5
- data/lib/karafka/cli/base.rb +1 -1
- data/lib/karafka/cli/contracts/server.rb +2 -4
- data/lib/karafka/cli/install.rb +1 -1
- data/lib/karafka/cli/topics/align.rb +1 -1
- data/lib/karafka/cli/topics/repartition.rb +2 -2
- data/lib/karafka/connection/client.rb +12 -2
- data/lib/karafka/connection/listeners_batch.rb +2 -3
- data/lib/karafka/connection/proxy.rb +11 -7
- data/lib/karafka/env.rb +1 -2
- data/lib/karafka/helpers/interval_runner.rb +4 -1
- data/lib/karafka/instrumentation/assignments_tracker.rb +17 -0
- data/lib/karafka/instrumentation/monitor.rb +1 -1
- data/lib/karafka/instrumentation/notifications.rb +1 -0
- data/lib/karafka/instrumentation/vendors/appsignal/base.rb +2 -3
- data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +2 -3
- data/lib/karafka/instrumentation/vendors/datadog/metrics_listener.rb +8 -9
- data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +2 -3
- data/lib/karafka/messages/builders/batch_metadata.rb +1 -1
- data/lib/karafka/messages/builders/message.rb +1 -1
- data/lib/karafka/messages/messages.rb +2 -3
- data/lib/karafka/patches/rdkafka/bindings.rb +6 -6
- data/lib/karafka/patches/rdkafka/opaque.rb +1 -1
- data/lib/karafka/pro/active_job/dispatcher.rb +7 -3
- data/lib/karafka/pro/active_job/job_options_contract.rb +2 -4
- data/lib/karafka/pro/cleaner/messages/messages.rb +2 -3
- data/lib/karafka/pro/cli/contracts/server.rb +2 -4
- data/lib/karafka/pro/cli/parallel_segments/base.rb +1 -2
- data/lib/karafka/pro/cli/parallel_segments/collapse.rb +2 -2
- data/lib/karafka/pro/cli/parallel_segments/distribute.rb +2 -2
- data/lib/karafka/pro/connection/manager.rb +2 -2
- data/lib/karafka/pro/encryption/contracts/config.rb +4 -6
- data/lib/karafka/pro/encryption/messages/parser.rb +3 -3
- data/lib/karafka/pro/instrumentation/performance_tracker.rb +3 -3
- data/lib/karafka/pro/iterator/expander.rb +1 -1
- data/lib/karafka/pro/iterator/tpl_builder.rb +1 -1
- data/lib/karafka/pro/iterator.rb +2 -2
- data/lib/karafka/pro/processing/coordinators/errors_tracker.rb +2 -3
- data/lib/karafka/pro/processing/coordinators/filters_applier.rb +3 -3
- data/lib/karafka/pro/processing/filters/delayer.rb +1 -1
- data/lib/karafka/pro/processing/filters/expirer.rb +1 -1
- data/lib/karafka/pro/processing/filters/throttler.rb +1 -1
- data/lib/karafka/pro/processing/schedulers/default.rb +2 -4
- data/lib/karafka/pro/processing/strategies/lrj/default.rb +2 -4
- data/lib/karafka/pro/processing/strategies/vp/default.rb +2 -4
- data/lib/karafka/pro/processing/subscription_groups_coordinator.rb +2 -3
- data/lib/karafka/pro/recurring_tasks/contracts/config.rb +2 -4
- data/lib/karafka/pro/recurring_tasks/contracts/task.rb +2 -4
- data/lib/karafka/pro/recurring_tasks/dispatcher.rb +6 -5
- data/lib/karafka/pro/recurring_tasks/schedule.rb +4 -6
- data/lib/karafka/pro/recurring_tasks.rb +8 -5
- data/lib/karafka/pro/routing/features/adaptive_iterator/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/dead_letter_queue/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/delaying/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/delaying/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/direct_assignments/contracts/consumer_group.rb +4 -8
- data/lib/karafka/pro/routing/features/direct_assignments/contracts/topic.rb +5 -7
- data/lib/karafka/pro/routing/features/direct_assignments/subscription_group.rb +7 -6
- data/lib/karafka/pro/routing/features/direct_assignments/topic.rb +2 -2
- data/lib/karafka/pro/routing/features/expiring/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/expiring/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/filtering/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/filtering/topic.rb +2 -3
- data/lib/karafka/pro/routing/features/inline_insights/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/long_running_job/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/multiplexing/contracts/topic.rb +3 -5
- data/lib/karafka/pro/routing/features/non_blocking_job/topic.rb +3 -3
- data/lib/karafka/pro/routing/features/offset_metadata/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/parallel_segments/contracts/consumer_group.rb +2 -4
- data/lib/karafka/pro/routing/features/patterns/contracts/consumer_group.rb +3 -5
- data/lib/karafka/pro/routing/features/patterns/contracts/pattern.rb +2 -4
- data/lib/karafka/pro/routing/features/patterns/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/pausing/config.rb +26 -0
- data/lib/karafka/pro/routing/features/pausing/contracts/topic.rb +17 -11
- data/lib/karafka/pro/routing/features/pausing/topic.rb +69 -8
- data/lib/karafka/pro/routing/features/periodic_job/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/recurring_tasks/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/scheduled_messages/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/swarm/contracts/routing.rb +2 -4
- data/lib/karafka/pro/routing/features/swarm/contracts/topic.rb +6 -8
- data/lib/karafka/pro/routing/features/throttling/contracts/topic.rb +2 -4
- data/lib/karafka/pro/routing/features/virtual_partitions/contracts/topic.rb +2 -4
- data/lib/karafka/pro/scheduled_messages/contracts/config.rb +2 -4
- data/lib/karafka/pro/scheduled_messages/contracts/message.rb +2 -4
- data/lib/karafka/pro/scheduled_messages.rb +4 -6
- data/lib/karafka/pro/swarm/liveness_listener.rb +2 -2
- data/lib/karafka/processing/coordinator.rb +2 -4
- data/lib/karafka/processing/coordinators_buffer.rb +2 -3
- data/lib/karafka/processing/executor.rb +2 -3
- data/lib/karafka/processing/jobs/base.rb +2 -3
- data/lib/karafka/processing/workers_batch.rb +2 -3
- data/lib/karafka/railtie.rb +1 -0
- data/lib/karafka/routing/activity_manager.rb +2 -2
- data/lib/karafka/routing/builder.rb +5 -7
- data/lib/karafka/routing/consumer_group.rb +4 -6
- data/lib/karafka/routing/contracts/consumer_group.rb +3 -5
- data/lib/karafka/routing/contracts/routing.rb +2 -4
- data/lib/karafka/routing/contracts/topic.rb +2 -4
- data/lib/karafka/routing/features/active_job/contracts/topic.rb +2 -4
- data/lib/karafka/routing/features/active_job/topic.rb +6 -0
- data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +2 -4
- data/lib/karafka/routing/features/declaratives/contracts/topic.rb +3 -5
- data/lib/karafka/routing/features/deserializers/contracts/topic.rb +2 -4
- data/lib/karafka/routing/features/eofed/contracts/topic.rb +2 -4
- data/lib/karafka/routing/features/inline_insights/contracts/topic.rb +2 -4
- data/lib/karafka/routing/features/manual_offset_management/contracts/topic.rb +2 -4
- data/lib/karafka/routing/topics.rb +4 -9
- data/lib/karafka/server.rb +1 -1
- data/lib/karafka/setup/config.rb +66 -9
- data/lib/karafka/setup/contracts/config.rb +12 -10
- data/lib/karafka/setup/defaults_injector.rb +3 -2
- data/lib/karafka/setup/dsl.rb +2 -3
- data/lib/karafka/swarm/liveness_listener.rb +2 -3
- data/lib/karafka/swarm/supervisor.rb +1 -1
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +2 -2
- metadata +8 -2
- data/.diffend.yml +0 -3
|
@@ -234,6 +234,76 @@ module Karafka
|
|
|
234
234
|
end
|
|
235
235
|
end
|
|
236
236
|
|
|
237
|
+
# Triggers a rebalance for the specified consumer group by briefly joining and leaving
|
|
238
|
+
#
|
|
239
|
+
# @param consumer_group_id [String] consumer group id to trigger rebalance for
|
|
240
|
+
#
|
|
241
|
+
# @return [void]
|
|
242
|
+
#
|
|
243
|
+
# @raise [Karafka::Errors::InvalidConfigurationError] when consumer group is not found in
|
|
244
|
+
# routing or has no topics
|
|
245
|
+
#
|
|
246
|
+
# @note This method creates a temporary "fake" consumer that joins the consumer group,
|
|
247
|
+
# triggering a rebalance when it joins and another when it leaves. This should only be
|
|
248
|
+
# used for operational/testing purposes as it causes two rebalances.
|
|
249
|
+
#
|
|
250
|
+
# @note The consumer group does not need to be running for this to work, but if it is,
|
|
251
|
+
# it will experience rebalances.
|
|
252
|
+
#
|
|
253
|
+
# @note The behavior follows the configured rebalance protocol. For cooperative sticky
|
|
254
|
+
# rebalancing or KIP-848 based protocols, there may be no immediate reaction to the
|
|
255
|
+
# rebalance trigger as these protocols allow incremental partition reassignments without
|
|
256
|
+
# stopping all consumers.
|
|
257
|
+
#
|
|
258
|
+
# @note Topics are always detected from the routing configuration. The consumer settings
|
|
259
|
+
# (kafka config) are taken from the first topic in the consumer group to ensure
|
|
260
|
+
# consistency with the actual consumer configuration.
|
|
261
|
+
#
|
|
262
|
+
# @example Trigger rebalance for a consumer group
|
|
263
|
+
# Karafka::Admin::ConsumerGroups.trigger_rebalance('my-group')
|
|
264
|
+
def trigger_rebalance(consumer_group_id)
|
|
265
|
+
consumer_group = Karafka::App.routes.find { |cg| cg.id == consumer_group_id }
|
|
266
|
+
|
|
267
|
+
unless consumer_group
|
|
268
|
+
raise(
|
|
269
|
+
Errors::InvalidConfigurationError,
|
|
270
|
+
"Consumer group '#{consumer_group_id}' not found in routing"
|
|
271
|
+
)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
topics = consumer_group.topics.map(&:name)
|
|
275
|
+
|
|
276
|
+
if topics.empty?
|
|
277
|
+
raise(
|
|
278
|
+
Errors::InvalidConfigurationError,
|
|
279
|
+
"Consumer group '#{consumer_group_id}' has no topics"
|
|
280
|
+
)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Get the first topic to extract kafka settings
|
|
284
|
+
first_topic = consumer_group.topics.first
|
|
285
|
+
|
|
286
|
+
# Build consumer settings using the consumer group's kafka config from first topic
|
|
287
|
+
# This ensures we use the same settings as the actual consumers
|
|
288
|
+
# Following the same pattern as in Karafka::Connection::Client#build_kafka
|
|
289
|
+
consumer_settings = Setup::AttributesMap.consumer(first_topic.kafka.dup)
|
|
290
|
+
consumer_settings[:'group.id'] = consumer_group.id
|
|
291
|
+
consumer_settings[:'enable.auto.offset.store'] = false
|
|
292
|
+
consumer_settings[:'auto.offset.reset'] ||= first_topic.initial_offset
|
|
293
|
+
|
|
294
|
+
with_consumer(consumer_settings) do |consumer|
|
|
295
|
+
# Subscribe to the topics - this triggers the first rebalance
|
|
296
|
+
consumer.subscribe(*topics)
|
|
297
|
+
|
|
298
|
+
# Wait briefly (100ms) to allow the rebalance to initiate
|
|
299
|
+
# The actual rebalance happens asynchronously, so we just need to give it a moment
|
|
300
|
+
sleep(0.1)
|
|
301
|
+
|
|
302
|
+
# Unsubscribe - this will trigger the second rebalance when the consumer closes
|
|
303
|
+
# The ensure block in with_consumer will handle the unsubscribe and close
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
237
307
|
# Reads lags and offsets for given topics in the context of consumer groups defined in the
|
|
238
308
|
# routing
|
|
239
309
|
#
|
|
@@ -257,19 +327,19 @@ module Karafka
|
|
|
257
327
|
# We first fetch all the topics with partitions count that exist in the cluster so we
|
|
258
328
|
# do not query for topics that do not exist and so we can get partitions count for all
|
|
259
329
|
# the topics we may need. The non-existent and not consumed will be filled at the end
|
|
260
|
-
existing_topics = cluster_info.topics.
|
|
330
|
+
existing_topics = cluster_info.topics.to_h do |topic|
|
|
261
331
|
[topic[:topic_name], topic[:partition_count]]
|
|
262
|
-
end.
|
|
332
|
+
end.freeze
|
|
263
333
|
|
|
264
334
|
# If no expected CGs, we use all from routing that have active topics
|
|
265
335
|
if consumer_groups_with_topics.empty?
|
|
266
|
-
consumer_groups_with_topics = Karafka::App.routes.
|
|
336
|
+
consumer_groups_with_topics = Karafka::App.routes.to_h do |cg|
|
|
267
337
|
cg_topics = cg.topics.select do |cg_topic|
|
|
268
338
|
active_topics_only ? cg_topic.active? : true
|
|
269
339
|
end
|
|
270
340
|
|
|
271
341
|
[cg.id, cg_topics.map(&:name)]
|
|
272
|
-
end
|
|
342
|
+
end
|
|
273
343
|
end
|
|
274
344
|
|
|
275
345
|
# We make a copy because we will remove once with non-existing topics
|
data/lib/karafka/admin/topics.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Karafka
|
|
|
36
36
|
|
|
37
37
|
# Build the requested range - since first element is on the start offset we need to
|
|
38
38
|
# subtract one from requested count to end up with expected number of elements
|
|
39
|
-
requested_range = (start_offset..start_offset + (count - 1))
|
|
39
|
+
requested_range = (start_offset..(start_offset + (count - 1)))
|
|
40
40
|
# Establish theoretical available range. Note, that this does not handle cases related
|
|
41
41
|
# to log retention or compaction
|
|
42
42
|
available_range = (low_offset..(high_offset - 1))
|
|
@@ -139,15 +139,48 @@ module Karafka
|
|
|
139
139
|
end
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
# Fetches the watermark offsets for a given topic partition
|
|
142
|
+
# Fetches the watermark offsets for a given topic partition or multiple topics and
|
|
143
|
+
# partitions
|
|
143
144
|
#
|
|
144
|
-
# @param
|
|
145
|
-
# @param partition [Integer] partition
|
|
146
|
-
#
|
|
147
|
-
|
|
145
|
+
# @param name_or_hash [String, Symbol, Hash] topic name or hash with topics and partitions
|
|
146
|
+
# @param partition [Integer, nil] partition number (required when first param is topic name)
|
|
147
|
+
#
|
|
148
|
+
# @return [Array<Integer, Integer>, Hash] when querying single partition returns array with
|
|
149
|
+
# low and high watermark offsets, when querying multiple returns nested hash
|
|
150
|
+
#
|
|
151
|
+
# @example Query single partition
|
|
152
|
+
# Karafka::Admin::Topics.read_watermark_offsets('events', 0)
|
|
153
|
+
# # => [0, 100]
|
|
154
|
+
#
|
|
155
|
+
# @example Query specific partitions across multiple topics
|
|
156
|
+
# Karafka::Admin::Topics.read_watermark_offsets(
|
|
157
|
+
# { 'events' => [0, 1], 'logs' => [0] }
|
|
158
|
+
# )
|
|
159
|
+
# # => {
|
|
160
|
+
# # 'events' => {
|
|
161
|
+
# # 0 => [0, 100],
|
|
162
|
+
# # 1 => [0, 150]
|
|
163
|
+
# # },
|
|
164
|
+
# # 'logs' => {
|
|
165
|
+
# # 0 => [0, 50]
|
|
166
|
+
# # }
|
|
167
|
+
# # }
|
|
168
|
+
def read_watermark_offsets(name_or_hash, partition = nil)
|
|
169
|
+
# Normalize input to hash format
|
|
170
|
+
topics_with_partitions = partition ? { name_or_hash => [partition] } : name_or_hash
|
|
171
|
+
|
|
172
|
+
result = Hash.new { |h, k| h[k] = {} }
|
|
173
|
+
|
|
148
174
|
with_consumer do |consumer|
|
|
149
|
-
|
|
175
|
+
topics_with_partitions.each do |topic, partitions|
|
|
176
|
+
partitions.each do |partition_id|
|
|
177
|
+
result[topic][partition_id] = consumer.query_watermark_offsets(topic, partition_id)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
150
180
|
end
|
|
181
|
+
|
|
182
|
+
# Return single array for single partition query, hash for multiple
|
|
183
|
+
partition ? result.dig(name_or_hash, partition) : result
|
|
151
184
|
end
|
|
152
185
|
|
|
153
186
|
# Returns basic topic metadata
|
data/lib/karafka/admin.rb
CHANGED
|
@@ -59,11 +59,11 @@ module Karafka
|
|
|
59
59
|
Topics.create_partitions(name, partitions)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
# @param
|
|
63
|
-
# @param partition [Integer] partition
|
|
62
|
+
# @param name_or_hash [String, Symbol, Hash] topic name or hash with topics and partitions
|
|
63
|
+
# @param partition [Integer, nil] partition (nil when using hash format)
|
|
64
64
|
# @see Topics.read_watermark_offsets
|
|
65
|
-
def read_watermark_offsets(
|
|
66
|
-
Topics.read_watermark_offsets(
|
|
65
|
+
def read_watermark_offsets(name_or_hash, partition = nil)
|
|
66
|
+
Topics.read_watermark_offsets(name_or_hash, partition)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# @param topic_name [String] name of the topic we're interested in
|
|
@@ -113,6 +113,15 @@ module Karafka
|
|
|
113
113
|
ConsumerGroups.delete(consumer_group_id)
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
+
# Triggers a rebalance for the specified consumer group
|
|
117
|
+
#
|
|
118
|
+
# @param consumer_group_id [String] consumer group id to trigger rebalance for
|
|
119
|
+
# @see ConsumerGroups.trigger_rebalance
|
|
120
|
+
# @note This API should be used only for development.
|
|
121
|
+
def trigger_rebalance(consumer_group_id)
|
|
122
|
+
ConsumerGroups.trigger_rebalance(consumer_group_id)
|
|
123
|
+
end
|
|
124
|
+
|
|
116
125
|
# Reads lags and offsets for given topics in the context of consumer groups defined in the
|
|
117
126
|
# routing
|
|
118
127
|
# @param consumer_groups_with_topics [Hash<String, Array<String>>] hash with consumer groups
|
|
@@ -14,8 +14,9 @@ module Karafka
|
|
|
14
14
|
|
|
15
15
|
def_delegators :@coordinator, :topic, :partition, :eofed?, :seek_offset, :seek_offset=
|
|
16
16
|
|
|
17
|
-
def_delegators
|
|
18
|
-
|
|
17
|
+
def_delegators(
|
|
18
|
+
:producer, :produce_async, :produce_sync, :produce_many_async, :produce_many_sync
|
|
19
|
+
)
|
|
19
20
|
|
|
20
21
|
def_delegators :messages, :each
|
|
21
22
|
|
|
@@ -81,9 +82,8 @@ module Karafka
|
|
|
81
82
|
# @private
|
|
82
83
|
#
|
|
83
84
|
# @param action [Symbol]
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
handle_wrap(action, &block)
|
|
85
|
+
def on_wrap(action, &)
|
|
86
|
+
handle_wrap(action, &)
|
|
87
87
|
rescue StandardError => e
|
|
88
88
|
monitor.instrument(
|
|
89
89
|
'error.occurred',
|
data/lib/karafka/cli/base.rb
CHANGED
|
@@ -68,7 +68,7 @@ module Karafka
|
|
|
68
68
|
|
|
69
69
|
# However when it is unavailable, we still want to be able to run help command
|
|
70
70
|
# and install command as they don't require configured app itself to run
|
|
71
|
-
return if %w[-h install].any?
|
|
71
|
+
return if %w[-h install].any?(ARGV[0])
|
|
72
72
|
|
|
73
73
|
# All other commands except help and install do require an existing boot file if it was
|
|
74
74
|
# declared
|
|
@@ -7,10 +7,8 @@ module Karafka
|
|
|
7
7
|
# Contract for validating correctness of the server cli command options.
|
|
8
8
|
class Server < ::Karafka::Contracts::Base
|
|
9
9
|
configure do |config|
|
|
10
|
-
config.error_messages = YAML.
|
|
11
|
-
File.
|
|
12
|
-
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
|
13
|
-
)
|
|
10
|
+
config.error_messages = YAML.safe_load_file(
|
|
11
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
|
|
14
12
|
).fetch('en').fetch('validations').fetch('cli').fetch('server')
|
|
15
13
|
end
|
|
16
14
|
|
data/lib/karafka/cli/install.rb
CHANGED
|
@@ -54,7 +54,7 @@ module Karafka
|
|
|
54
54
|
template = File.read(Karafka.core_root.join("templates/#{source}"))
|
|
55
55
|
render = ::ERB.new(template, trim_mode: '-').result(binding)
|
|
56
56
|
|
|
57
|
-
File.
|
|
57
|
+
File.write(pathed_target, render)
|
|
58
58
|
|
|
59
59
|
puts "#{green('Created')} #{target}"
|
|
60
60
|
end
|
|
@@ -95,7 +95,7 @@ module Karafka
|
|
|
95
95
|
names = config.synonyms.map(&:name) << config.name
|
|
96
96
|
|
|
97
97
|
# We move forward only if given topic config is for altering
|
|
98
|
-
next
|
|
98
|
+
next unless desired_configs.keys.intersect?(names)
|
|
99
99
|
|
|
100
100
|
desired_config = nil
|
|
101
101
|
|
|
@@ -10,9 +10,9 @@ module Karafka
|
|
|
10
10
|
def call
|
|
11
11
|
any_repartitioned = false
|
|
12
12
|
|
|
13
|
-
existing_partitions = existing_topics.
|
|
13
|
+
existing_partitions = existing_topics.to_h do |topic|
|
|
14
14
|
[topic.fetch(:topic_name), topic.fetch(:partition_count)]
|
|
15
|
-
end
|
|
15
|
+
end
|
|
16
16
|
|
|
17
17
|
declaratives_routing_topics.each do |topic|
|
|
18
18
|
name = topic.name
|
|
@@ -51,7 +51,9 @@ module Karafka
|
|
|
51
51
|
:fenced, # -144
|
|
52
52
|
:auto_offset_reset, # -140
|
|
53
53
|
# This can happen for many reasons, including issues with static membership being fenced
|
|
54
|
-
:fatal # -150
|
|
54
|
+
:fatal, # -150,
|
|
55
|
+
# This can happen with new rebalance protocol and same group.instance.id in use
|
|
56
|
+
:unreleased_instance_id # 111
|
|
55
57
|
].freeze
|
|
56
58
|
|
|
57
59
|
private_constant :MAX_POLL_RETRIES, :COOP_UNSUBSCRIBE_FACTOR, :EARLY_REPORT_ERRORS
|
|
@@ -407,6 +409,14 @@ module Karafka
|
|
|
407
409
|
# 2ms when no callbacks are triggered.
|
|
408
410
|
def events_poll(timeout = 0)
|
|
409
411
|
kafka.events_poll(timeout)
|
|
412
|
+
|
|
413
|
+
# Emit event for monitoring - happens once per tick_interval (default 5s)
|
|
414
|
+
# Listeners can check assignment_lost?, track polling health, etc.
|
|
415
|
+
Karafka.monitor.instrument(
|
|
416
|
+
'client.events_poll',
|
|
417
|
+
caller: self,
|
|
418
|
+
subscription_group: @subscription_group
|
|
419
|
+
)
|
|
410
420
|
end
|
|
411
421
|
|
|
412
422
|
# Returns pointer to the consumer group metadata. It is used only in the context of
|
|
@@ -612,7 +622,7 @@ module Karafka
|
|
|
612
622
|
# We should not run a single poll longer than the tick frequency. Otherwise during a single
|
|
613
623
|
# `#batch_poll` we would not be able to run `#events_poll` often enough effectively
|
|
614
624
|
# blocking events from being handled.
|
|
615
|
-
poll_tick = timeout
|
|
625
|
+
poll_tick = [timeout, tick_interval].min
|
|
616
626
|
|
|
617
627
|
result = kafka.poll(poll_tick)
|
|
618
628
|
|
|
@@ -25,9 +25,8 @@ module Karafka
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Iterates over available listeners and yields each listener
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@batch.each(&block)
|
|
28
|
+
def each(&)
|
|
29
|
+
@batch.each(&)
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
# @return [Array<Listener>] active listeners
|
|
@@ -10,6 +10,10 @@ module Karafka
|
|
|
10
10
|
# do still want to be able to alter some functionalities. This wrapper helps us do it when
|
|
11
11
|
# it would be needed
|
|
12
12
|
class Proxy < SimpleDelegator
|
|
13
|
+
include Helpers::ConfigImporter.new(
|
|
14
|
+
proxy_config: %i[internal connection proxy]
|
|
15
|
+
)
|
|
16
|
+
|
|
13
17
|
# Errors on which we want to retry
|
|
14
18
|
# Includes temporary errors related to node not being (or not yet being) coordinator or a
|
|
15
19
|
# leader to a given set of partitions. Usually goes away after a retry
|
|
@@ -21,6 +25,7 @@ module Karafka
|
|
|
21
25
|
not_coordinator
|
|
22
26
|
not_leader_for_partition
|
|
23
27
|
coordinator_load_in_progress
|
|
28
|
+
stale_member_epoch
|
|
24
29
|
].freeze
|
|
25
30
|
|
|
26
31
|
private_constant :RETRYABLE_DEFAULT_ERRORS
|
|
@@ -36,7 +41,6 @@ module Karafka
|
|
|
36
41
|
# wrap an already wrapped object with another proxy level. Simplifies passing consumers
|
|
37
42
|
# and makes it safe to wrap without type checking
|
|
38
43
|
@wrapped = obj.is_a?(self.class) ? obj.wrapped : obj
|
|
39
|
-
@config = ::Karafka::App.config.internal.connection.proxy
|
|
40
44
|
end
|
|
41
45
|
|
|
42
46
|
# Proxies the `#query_watermark_offsets` with extra recovery from timeout problems.
|
|
@@ -47,7 +51,7 @@ module Karafka
|
|
|
47
51
|
# @param partition [Integer] partition number
|
|
48
52
|
# @return [Array<Integer, Integer>] watermark offsets
|
|
49
53
|
def query_watermark_offsets(topic, partition)
|
|
50
|
-
l_config =
|
|
54
|
+
l_config = proxy_config.query_watermark_offsets
|
|
51
55
|
|
|
52
56
|
# For newly created topics or in cases where we're trying to get them but there is no
|
|
53
57
|
# leader, this can fail. It happens more often for new topics under KRaft, however we
|
|
@@ -67,7 +71,7 @@ module Karafka
|
|
|
67
71
|
# @param tpl [Rdkafka::Consumer::TopicPartitionList] tpl to get time offsets
|
|
68
72
|
# @return [Rdkafka::Consumer::TopicPartitionList] tpl with time offsets
|
|
69
73
|
def offsets_for_times(tpl)
|
|
70
|
-
l_config =
|
|
74
|
+
l_config = proxy_config.offsets_for_times
|
|
71
75
|
|
|
72
76
|
with_broker_errors_retry(
|
|
73
77
|
# required to be in seconds, not ms
|
|
@@ -84,7 +88,7 @@ module Karafka
|
|
|
84
88
|
# assignment tpl usage
|
|
85
89
|
# @return [Rdkafka::Consumer::TopicPartitionList] tpl with committed offsets and metadata
|
|
86
90
|
def committed(tpl = nil)
|
|
87
|
-
c_config =
|
|
91
|
+
c_config = proxy_config.committed
|
|
88
92
|
|
|
89
93
|
with_broker_errors_retry(
|
|
90
94
|
# required to be in seconds, not ms
|
|
@@ -121,7 +125,7 @@ module Karafka
|
|
|
121
125
|
# even when no stored, because with sync commit, it refreshes the ownership state of the
|
|
122
126
|
# consumer in a sync way.
|
|
123
127
|
def commit_offsets(tpl = nil, async: true)
|
|
124
|
-
c_config =
|
|
128
|
+
c_config = proxy_config.commit
|
|
125
129
|
|
|
126
130
|
with_broker_errors_retry(
|
|
127
131
|
wait_time: c_config.wait_time / 1_000.to_f,
|
|
@@ -153,7 +157,7 @@ module Karafka
|
|
|
153
157
|
# we want to get the lag on the defined CG
|
|
154
158
|
# @return [Hash<String, Hash>] hash with topics and their partitions lags
|
|
155
159
|
def lag(tpl)
|
|
156
|
-
l_config =
|
|
160
|
+
l_config = proxy_config.committed
|
|
157
161
|
|
|
158
162
|
with_broker_errors_retry(
|
|
159
163
|
# required to be in seconds, not ms
|
|
@@ -168,7 +172,7 @@ module Karafka
|
|
|
168
172
|
# get info on all topics
|
|
169
173
|
# @return [Rdkafka::Metadata] rdkafka metadata object with the requested details
|
|
170
174
|
def metadata(topic_name = nil)
|
|
171
|
-
m_config =
|
|
175
|
+
m_config = proxy_config.metadata
|
|
172
176
|
|
|
173
177
|
with_broker_errors_retry(
|
|
174
178
|
# required to be in seconds, not ms
|
data/lib/karafka/env.rb
CHANGED
|
@@ -11,11 +11,14 @@ module Karafka
|
|
|
11
11
|
# or other places but would only slow things down if would run with each tick.
|
|
12
12
|
class IntervalRunner
|
|
13
13
|
include Karafka::Core::Helpers::Time
|
|
14
|
+
include Helpers::ConfigImporter.new(
|
|
15
|
+
tick_interval: %i[internal tick_interval]
|
|
16
|
+
)
|
|
14
17
|
|
|
15
18
|
# @param interval [Integer] interval in ms for running the provided code. Defaults to the
|
|
16
19
|
# `internal.tick_interval` value
|
|
17
20
|
# @param block [Proc] block of code we want to run once in a while
|
|
18
|
-
def initialize(interval:
|
|
21
|
+
def initialize(interval: tick_interval, &block)
|
|
19
22
|
@block = block
|
|
20
23
|
@interval = interval
|
|
21
24
|
@last_called_at = monotonic_now - @interval
|
|
@@ -79,6 +79,23 @@ module Karafka
|
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
# Handles events_poll notification to detect assignment loss
|
|
83
|
+
# This is called regularly (every tick_interval) so we check if assignment was lost
|
|
84
|
+
#
|
|
85
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
|
86
|
+
# @note We can run the `#assignment_lost?` on each events poll because they happen once every
|
|
87
|
+
# 5 seconds during processing plus prior to each messages poll. It takes
|
|
88
|
+
# 0.6 microseconds per call.
|
|
89
|
+
def on_client_events_poll(event)
|
|
90
|
+
client = event[:caller]
|
|
91
|
+
|
|
92
|
+
# Only clear assignments if they were actually lost
|
|
93
|
+
return unless client.assignment_lost?
|
|
94
|
+
|
|
95
|
+
# Cleaning happens the same way as with the consumer reset
|
|
96
|
+
on_client_reset(event)
|
|
97
|
+
end
|
|
98
|
+
|
|
82
99
|
# Removes partitions from the current assignments hash
|
|
83
100
|
#
|
|
84
101
|
# @param event [Karafka::Core::Monitoring::Event]
|
|
@@ -18,10 +18,9 @@ module Karafka
|
|
|
18
18
|
setup(&block) if block
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
# @param block [Proc] configuration block
|
|
22
21
|
# @note We define this alias to be consistent with `Karafka#setup`
|
|
23
|
-
def setup(&
|
|
24
|
-
configure(&
|
|
22
|
+
def setup(&)
|
|
23
|
+
configure(&)
|
|
25
24
|
end
|
|
26
25
|
end
|
|
27
26
|
end
|
|
@@ -38,10 +38,9 @@ module Karafka
|
|
|
38
38
|
@job_types_cache = {}
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
# @param block [Proc] configuration block
|
|
42
41
|
# @note We define this alias to be consistent with `WaterDrop#setup`
|
|
43
|
-
def setup(&
|
|
44
|
-
configure(&
|
|
42
|
+
def setup(&)
|
|
43
|
+
configure(&)
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
# Prints info about the fact that a given job has started
|
|
@@ -14,8 +14,9 @@ module Karafka
|
|
|
14
14
|
include ::Karafka::Core::Configurable
|
|
15
15
|
extend Forwardable
|
|
16
16
|
|
|
17
|
-
def_delegators
|
|
18
|
-
|
|
17
|
+
def_delegators(
|
|
18
|
+
:config, :client, :rd_kafka_metrics, :namespace, :default_tags, :distribution_mode
|
|
19
|
+
)
|
|
19
20
|
|
|
20
21
|
# Value object for storing a single rdkafka metric publishing details
|
|
21
22
|
RdKafkaMetric = Struct.new(:type, :scope, :name, :key_location)
|
|
@@ -69,10 +70,9 @@ module Karafka
|
|
|
69
70
|
setup(&block) if block
|
|
70
71
|
end
|
|
71
72
|
|
|
72
|
-
# @param block [Proc] configuration block
|
|
73
73
|
# @note We define this alias to be consistent with `WaterDrop#setup`
|
|
74
|
-
def setup(&
|
|
75
|
-
configure(&
|
|
74
|
+
def setup(&)
|
|
75
|
+
configure(&)
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
# Hooks up to Karafka instrumentation for emitted statistics
|
|
@@ -198,18 +198,17 @@ module Karafka
|
|
|
198
198
|
|
|
199
199
|
# Selects the histogram mode configured and uses it to report to DD client
|
|
200
200
|
# @param key [String] non-namespaced key
|
|
201
|
-
|
|
202
|
-
def histogram(key, *args)
|
|
201
|
+
def histogram(key, *)
|
|
203
202
|
case distribution_mode
|
|
204
203
|
when :histogram
|
|
205
204
|
client.histogram(
|
|
206
205
|
namespaced_metric(key),
|
|
207
|
-
*
|
|
206
|
+
*
|
|
208
207
|
)
|
|
209
208
|
when :distribution
|
|
210
209
|
client.distribution(
|
|
211
210
|
namespaced_metric(key),
|
|
212
|
-
*
|
|
211
|
+
*
|
|
213
212
|
)
|
|
214
213
|
else
|
|
215
214
|
raise(
|
|
@@ -166,9 +166,8 @@ module Karafka
|
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
# Wraps the logic with a mutex
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
@mutex.synchronize(&block)
|
|
169
|
+
def synchronize(&)
|
|
170
|
+
@mutex.synchronize(&)
|
|
172
171
|
end
|
|
173
172
|
|
|
174
173
|
# @return [Integer] object id of the current thread
|
|
@@ -28,7 +28,7 @@ module Karafka
|
|
|
28
28
|
|
|
29
29
|
# And nullify it in the kafka message. This can save a lot of memory when used with
|
|
30
30
|
# the Pro Cleaner API
|
|
31
|
-
kafka_message.instance_variable_set(
|
|
31
|
+
kafka_message.instance_variable_set(:@payload, nil)
|
|
32
32
|
|
|
33
33
|
# Karafka messages cannot be frozen because of the lazy deserialization feature
|
|
34
34
|
message = Karafka::Messages::Message.new(payload, metadata)
|
|
@@ -16,10 +16,9 @@ module Karafka
|
|
|
16
16
|
@metadata = metadata
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# @param block [Proc] block we want to execute per each message
|
|
20
19
|
# @note Invocation of this method will not cause loading and deserializing of messages.
|
|
21
|
-
def each(&
|
|
22
|
-
@messages_array.each(&
|
|
20
|
+
def each(&)
|
|
21
|
+
@messages_array.each(&)
|
|
23
22
|
end
|
|
24
23
|
|
|
25
24
|
# Runs deserialization of all the messages and returns them
|
|
@@ -93,29 +93,29 @@ end
|
|
|
93
93
|
|
|
94
94
|
# We need to replace the original callback with ours.
|
|
95
95
|
# At the moment there is no API in rdkafka-ruby to do so
|
|
96
|
-
|
|
96
|
+
Rdkafka::Bindings.send(
|
|
97
97
|
:remove_const,
|
|
98
98
|
'RebalanceCallback'
|
|
99
99
|
)
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
Rdkafka::Bindings.const_set(
|
|
102
|
+
:RebalanceCallback,
|
|
103
103
|
Karafka::Patches::Rdkafka::Bindings::RebalanceCallback
|
|
104
104
|
)
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
Rdkafka::Bindings.attach_function(
|
|
107
107
|
:rd_kafka_rebalance_protocol,
|
|
108
108
|
%i[pointer],
|
|
109
109
|
:string
|
|
110
110
|
)
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
Rdkafka::Bindings.attach_function(
|
|
113
113
|
:rd_kafka_incremental_assign,
|
|
114
114
|
%i[pointer pointer],
|
|
115
115
|
:string
|
|
116
116
|
)
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
Rdkafka::Bindings.attach_function(
|
|
119
119
|
:rd_kafka_incremental_unassign,
|
|
120
120
|
%i[pointer pointer],
|
|
121
121
|
:string
|