karafka 2.2.11 → 2.2.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +13 -13
- data/config/locales/errors.yml +3 -1
- data/docker-compose.yml +1 -1
- data/karafka.gemspec +2 -2
- data/lib/karafka/connection/client.rb +77 -11
- data/lib/karafka/connection/consumer_group_coordinator.rb +3 -3
- data/lib/karafka/connection/listener.rb +30 -7
- data/lib/karafka/connection/listeners_batch.rb +6 -1
- data/lib/karafka/contracts/config.rb +5 -1
- data/lib/karafka/helpers/interval_runner.rb +39 -0
- data/lib/karafka/instrumentation/notifications.rb +1 -0
- data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +1 -9
- data/lib/karafka/pro/loader.rb +2 -1
- data/lib/karafka/pro/processing/coordinator.rb +12 -6
- data/lib/karafka/pro/processing/jobs_queue.rb +109 -0
- data/lib/karafka/pro/processing/scheduler.rb +2 -3
- data/lib/karafka/pro/processing/strategies/default.rb +2 -0
- data/lib/karafka/pro/processing/strategies/lrj/default.rb +9 -0
- data/lib/karafka/pro/processing/strategies/vp/default.rb +8 -4
- data/lib/karafka/processing/coordinator.rb +13 -7
- data/lib/karafka/processing/inline_insights/consumer.rb +2 -0
- data/lib/karafka/processing/jobs_queue.rb +41 -13
- data/lib/karafka/processing/scheduler.rb +19 -3
- data/lib/karafka/processing/strategies/default.rb +2 -0
- data/lib/karafka/processing/timed_queue.rb +62 -0
- data/lib/karafka/routing/builder.rb +32 -17
- data/lib/karafka/routing/subscription_group.rb +11 -6
- data/lib/karafka/runner.rb +1 -1
- data/lib/karafka/setup/config.rb +13 -1
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +0 -1
- data.tar.gz.sig +0 -0
- metadata +9 -6
- metadata.gz.sig +0 -0
@@ -0,0 +1,109 @@
|
|
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
|
+
# Enhanced processing queue that provides ability to build complex work-distribution
|
18
|
+
# schedulers dedicated to particular job types
|
19
|
+
#
|
20
|
+
# Aside from the OSS queue capabilities it allows for jobless locking for advanced schedulers
|
21
|
+
class JobsQueue < Karafka::Processing::JobsQueue
|
22
|
+
attr_accessor :in_processing
|
23
|
+
|
24
|
+
# @return [Karafka::Pro::Processing::JobsQueue]
|
25
|
+
def initialize
|
26
|
+
super
|
27
|
+
|
28
|
+
@in_waiting = Hash.new { |h, k| h[k] = [] }
|
29
|
+
|
30
|
+
@statistics[:waiting] = 0
|
31
|
+
end
|
32
|
+
|
33
|
+
# Method that allows us to lock queue on a given subscription group without enqueuing the a
|
34
|
+
# job. This can be used when building complex schedulers that want to postpone enqueuing
|
35
|
+
# before certain conditions are met.
|
36
|
+
#
|
37
|
+
# @param job [Jobs::Base] job used for locking
|
38
|
+
def lock(job)
|
39
|
+
@mutex.synchronize do
|
40
|
+
group = @in_waiting[job.group_id]
|
41
|
+
|
42
|
+
# This should never happen. Same job should not be locked twice
|
43
|
+
raise(Errors::JobsQueueSynchronizationError, job.group_id) if group.include?(job)
|
44
|
+
|
45
|
+
@statistics[:waiting] += 1
|
46
|
+
|
47
|
+
group << job
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Method for unlocking the given subscription group queue space that was locked with a
|
52
|
+
# given job that was **not** added to the queue but used via `#lock`.
|
53
|
+
#
|
54
|
+
# @param job [Jobs::Base] job that locked the queue
|
55
|
+
def unlock(job)
|
56
|
+
@mutex.synchronize do
|
57
|
+
@statistics[:waiting] -= 1
|
58
|
+
|
59
|
+
return if @in_waiting[job.group_id].delete(job)
|
60
|
+
|
61
|
+
# This should never happen. It means there was a job being unlocked that was never
|
62
|
+
# locked in the first place
|
63
|
+
raise(Errors::JobsQueueSynchronizationError, job.group_id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Clears the processing states for a provided group. Useful when a recovery happens and we
|
68
|
+
# need to clean up state but only for a given subscription group.
|
69
|
+
#
|
70
|
+
# @param group_id [String]
|
71
|
+
def clear(group_id)
|
72
|
+
@mutex.synchronize do
|
73
|
+
@in_processing[group_id].clear
|
74
|
+
|
75
|
+
@statistics[:waiting] -= @in_waiting[group_id].size
|
76
|
+
@in_waiting[group_id].clear
|
77
|
+
|
78
|
+
# We unlock it just in case it was blocked when clearing started
|
79
|
+
tick(group_id)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param group_id [String]
|
84
|
+
#
|
85
|
+
# @return [Boolean] tell us if we have anything in the processing (or for processing) from
|
86
|
+
# a given group.
|
87
|
+
def empty?(group_id)
|
88
|
+
@mutex.synchronize do
|
89
|
+
@in_processing[group_id].empty? &&
|
90
|
+
@in_waiting[group_id].empty?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# @param group_id [String] id of the group in which jobs we're interested.
|
97
|
+
# @return [Boolean] should we keep waiting or not
|
98
|
+
# @note We do not wait for non-blocking jobs. Their flow should allow for `poll` running
|
99
|
+
# as they may exceed `max.poll.interval`
|
100
|
+
def wait?(group_id)
|
101
|
+
!(
|
102
|
+
@in_processing[group_id].all?(&:non_blocking?) &&
|
103
|
+
@in_waiting[group_id].all?(&:non_blocking?)
|
104
|
+
)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -27,10 +27,9 @@ module Karafka
|
|
27
27
|
class Scheduler < ::Karafka::Processing::Scheduler
|
28
28
|
# Schedules jobs in the LJF order for consumption
|
29
29
|
#
|
30
|
-
# @param queue [Karafka::Processing::JobsQueue] queue where we want to put the jobs
|
31
30
|
# @param jobs_array [Array<Karafka::Processing::Jobs::Base>] jobs we want to schedule
|
32
31
|
#
|
33
|
-
def schedule_consumption(
|
32
|
+
def schedule_consumption(jobs_array)
|
34
33
|
perf_tracker = PerformanceTracker.instance
|
35
34
|
|
36
35
|
ordered = []
|
@@ -47,7 +46,7 @@ module Karafka
|
|
47
46
|
ordered.map!(&:first)
|
48
47
|
|
49
48
|
ordered.each do |job|
|
50
|
-
queue << job
|
49
|
+
@queue << job
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
@@ -77,6 +77,15 @@ module Karafka
|
|
77
77
|
revoked
|
78
78
|
end
|
79
79
|
end
|
80
|
+
|
81
|
+
# Allows for LRJ to synchronize its work. It may be needed because LRJ can run
|
82
|
+
# lifecycle events like revocation while the LRJ work is running and there may be a
|
83
|
+
# need for a critical section.
|
84
|
+
#
|
85
|
+
# @param block [Proc] block we want to run in a mutex to prevent race-conditions
|
86
|
+
def synchronize(&block)
|
87
|
+
coordinator.shared_mutex.synchronize(&block)
|
88
|
+
end
|
80
89
|
end
|
81
90
|
end
|
82
91
|
end
|
@@ -94,13 +94,15 @@ module Karafka
|
|
94
94
|
|
95
95
|
# Allows for cross-virtual-partition consumers locks
|
96
96
|
#
|
97
|
-
# This is not needed in the non-VP flows because there is always only one
|
98
|
-
# per partition at the same time, so no coordination is needed directly for
|
99
|
-
# end users
|
97
|
+
# This is not needed in the non-VP flows except LRJ because there is always only one
|
98
|
+
# consumer per partition at the same time, so no coordination is needed directly for
|
99
|
+
# the end users. With LRJ it is needed and provided in the `LRJ::Default` strategy,
|
100
|
+
# because lifecycle events on revocation can run in parallel to the LRJ job as it is
|
101
|
+
# non-blocking.
|
100
102
|
#
|
101
103
|
# @param block [Proc] block we want to run in a mutex to prevent race-conditions
|
102
104
|
def synchronize(&block)
|
103
|
-
coordinator.synchronize(&block)
|
105
|
+
coordinator.shared_mutex.synchronize(&block)
|
104
106
|
end
|
105
107
|
|
106
108
|
private
|
@@ -111,6 +113,8 @@ module Karafka
|
|
111
113
|
# @note This can be done without the mutex, because it happens from the same thread
|
112
114
|
# for all the work (listener thread)
|
113
115
|
def handle_before_enqueue
|
116
|
+
super
|
117
|
+
|
114
118
|
coordinator.virtual_offset_manager.register(
|
115
119
|
messages.map(&:offset)
|
116
120
|
)
|
@@ -162,11 +162,24 @@ module Karafka
|
|
162
162
|
@manual_seek
|
163
163
|
end
|
164
164
|
|
165
|
+
# @param consumer [Object] karafka consumer (normal or pro)
|
166
|
+
# @return [Karafka::Processing::Result] result object which we can use to indicate
|
167
|
+
# consumption processing state.
|
168
|
+
def consumption(consumer)
|
169
|
+
@consumptions[consumer] ||= Processing::Result.new
|
170
|
+
end
|
171
|
+
|
165
172
|
# Allows to run synchronized (locked) code that can operate only from a given thread
|
166
173
|
#
|
167
174
|
# @param block [Proc] code we want to run in the synchronized mode
|
175
|
+
#
|
168
176
|
# @note We check if mutex is not owned already by the current thread so we won't end up with
|
169
177
|
# a deadlock in case user runs coordinated code from inside of his own lock
|
178
|
+
#
|
179
|
+
# @note This is internal and should **not** be used to synchronize user-facing code.
|
180
|
+
# Otherwise user indirectly could cause deadlocks or prolonged locks by running his logic.
|
181
|
+
# This can and should however be used for multi-thread strategy applications and other
|
182
|
+
# internal operations locks.
|
170
183
|
def synchronize(&block)
|
171
184
|
if @mutex.owned?
|
172
185
|
yield
|
@@ -174,13 +187,6 @@ module Karafka
|
|
174
187
|
@mutex.synchronize(&block)
|
175
188
|
end
|
176
189
|
end
|
177
|
-
|
178
|
-
# @param consumer [Object] karafka consumer (normal or pro)
|
179
|
-
# @return [Karafka::Processing::Result] result object which we can use to indicate
|
180
|
-
# consumption processing state.
|
181
|
-
def consumption(consumer)
|
182
|
-
@consumptions[consumer] ||= Processing::Result.new
|
183
|
-
end
|
184
190
|
end
|
185
191
|
end
|
186
192
|
end
|
@@ -9,6 +9,9 @@ module Karafka
|
|
9
9
|
# on this queue, that's why internally we keep track of processing per group.
|
10
10
|
#
|
11
11
|
# We work with the assumption, that partitions data is evenly distributed.
|
12
|
+
#
|
13
|
+
# @note This job queue also keeps track / understands number of busy workers. This is because
|
14
|
+
# we use a single workers poll that can have granular scheduling.
|
12
15
|
class JobsQueue
|
13
16
|
# @return [Karafka::Processing::JobsQueue]
|
14
17
|
def initialize
|
@@ -21,21 +24,19 @@ module Karafka
|
|
21
24
|
# We cannot use a single semaphore as it could potentially block in listeners that should
|
22
25
|
# process with their data and also could unlock when a given group needs to remain locked
|
23
26
|
@semaphores = Concurrent::Map.new do |h, k|
|
24
|
-
|
27
|
+
# Ruby prior to 3.2 did not have queue with a timeout on `#pop`, that is why for those
|
28
|
+
# versions we use our custom queue wrapper
|
29
|
+
h.compute_if_absent(k) { RUBY_VERSION < '3.2' ? TimedQueue.new : Queue.new }
|
25
30
|
end
|
26
31
|
|
32
|
+
@concurrency = Karafka::App.config.concurrency
|
33
|
+
@tick_interval = ::Karafka::App.config.internal.tick_interval
|
27
34
|
@in_processing = Hash.new { |h, k| h[k] = [] }
|
35
|
+
@statistics = { busy: 0, enqueued: 0 }
|
28
36
|
|
29
37
|
@mutex = Mutex.new
|
30
38
|
end
|
31
39
|
|
32
|
-
# Returns number of jobs that are either enqueued or in processing (but not finished)
|
33
|
-
# @return [Integer] number of elements in the queue
|
34
|
-
# @note Using `#pop` won't decrease this number as only marking job as completed does this
|
35
|
-
def size
|
36
|
-
@in_processing.values.map(&:size).sum
|
37
|
-
end
|
38
|
-
|
39
40
|
# Adds the job to the internal main queue, scheduling it for execution in a worker and marks
|
40
41
|
# this job as in processing pipeline.
|
41
42
|
#
|
@@ -52,6 +53,16 @@ module Karafka
|
|
52
53
|
|
53
54
|
group << job
|
54
55
|
|
56
|
+
# Assume that moving to queue means being picked up immediately not to create stats
|
57
|
+
# race conditions because of pop overhead. If there are workers available, we assume
|
58
|
+
# work is going to be handled as we never reject enqueued jobs
|
59
|
+
if @statistics[:busy] < @concurrency
|
60
|
+
@statistics[:busy] += 1
|
61
|
+
else
|
62
|
+
# If system is fully loaded, it means this job is indeed enqueued
|
63
|
+
@statistics[:enqueued] += 1
|
64
|
+
end
|
65
|
+
|
55
66
|
@queue << job
|
56
67
|
end
|
57
68
|
end
|
@@ -77,7 +88,16 @@ module Karafka
|
|
77
88
|
# @param [Jobs::Base] job that was completed
|
78
89
|
def complete(job)
|
79
90
|
@mutex.synchronize do
|
91
|
+
# We finish one job and if there is another, we pick it up
|
92
|
+
if @statistics[:enqueued].positive?
|
93
|
+
@statistics[:enqueued] -= 1
|
94
|
+
# If no more enqueued jobs, we will be just less busy
|
95
|
+
else
|
96
|
+
@statistics[:busy] -= 1
|
97
|
+
end
|
98
|
+
|
80
99
|
@in_processing[job.group_id].delete(job)
|
100
|
+
|
81
101
|
tick(job.group_id)
|
82
102
|
end
|
83
103
|
end
|
@@ -118,11 +138,19 @@ module Karafka
|
|
118
138
|
# jobs from a given group are completed
|
119
139
|
#
|
120
140
|
# @param group_id [String] id of the group in which jobs we're interested.
|
141
|
+
# @yieldparam [Block] block we want to run before each pop (in case of Ruby pre 3.2) or
|
142
|
+
# before each pop and on every tick interval.
|
143
|
+
# This allows us to run extra code that needs to be executed even when we are waiting on
|
144
|
+
# the work to be finished.
|
121
145
|
# @note This method is blocking.
|
122
146
|
def wait(group_id)
|
123
147
|
# Go doing other things while we cannot process and wait for anyone to finish their work
|
124
148
|
# and re-check the wait status
|
125
|
-
|
149
|
+
while wait?(group_id)
|
150
|
+
yield if block_given?
|
151
|
+
|
152
|
+
@semaphores[group_id].pop(timeout: @tick_interval / 1_000.0)
|
153
|
+
end
|
126
154
|
end
|
127
155
|
|
128
156
|
# - `busy` - number of jobs that are currently being processed (active work)
|
@@ -130,10 +158,10 @@ module Karafka
|
|
130
158
|
#
|
131
159
|
# @return [Hash] hash with basic usage statistics of this queue.
|
132
160
|
def statistics
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
161
|
+
# Ensures there are no race conditions when returning this data
|
162
|
+
@mutex.synchronize do
|
163
|
+
@statistics.dup.freeze
|
164
|
+
end
|
137
165
|
end
|
138
166
|
|
139
167
|
private
|
@@ -4,19 +4,35 @@ module Karafka
|
|
4
4
|
module Processing
|
5
5
|
# FIFO scheduler for messages coming from various topics and partitions
|
6
6
|
class Scheduler
|
7
|
+
# @param queue [Karafka::Processing::JobsQueue] queue where we want to put the jobs
|
8
|
+
def initialize(queue)
|
9
|
+
@queue = queue
|
10
|
+
end
|
11
|
+
|
7
12
|
# Schedules jobs in the fifo order
|
8
13
|
#
|
9
|
-
# @param queue [Karafka::Processing::JobsQueue] queue where we want to put the jobs
|
10
14
|
# @param jobs_array [Array<Karafka::Processing::Jobs::Base>] jobs we want to schedule
|
11
|
-
def schedule_consumption(
|
15
|
+
def schedule_consumption(jobs_array)
|
12
16
|
jobs_array.each do |job|
|
13
|
-
queue << job
|
17
|
+
@queue << job
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
17
21
|
# Both revocation and shutdown jobs can also run in fifo by default
|
18
22
|
alias schedule_revocation schedule_consumption
|
19
23
|
alias schedule_shutdown schedule_consumption
|
24
|
+
|
25
|
+
# This scheduler does not have anything to manage as it is a pass through and has no state
|
26
|
+
def manage
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# This scheduler does not need to be cleared because it is stateless
|
31
|
+
#
|
32
|
+
# @param _group_id [String] Subscription group id
|
33
|
+
def clear(_group_id)
|
34
|
+
nil
|
35
|
+
end
|
20
36
|
end
|
21
37
|
end
|
22
38
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Processing
|
5
|
+
# Minimal queue with timeout for Ruby 3.1 and lower.
|
6
|
+
#
|
7
|
+
# It is needed because only since 3.2, Ruby has a timeout on `#pop`
|
8
|
+
class TimedQueue
|
9
|
+
include Karafka::Core::Helpers::Time
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@queue = Queue.new
|
13
|
+
@mutex = Thread::Mutex.new
|
14
|
+
@resource = Thread::ConditionVariable.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Adds element to the queue
|
18
|
+
#
|
19
|
+
# @param obj [Object] pushes an element onto the queue
|
20
|
+
def push(obj)
|
21
|
+
@mutex.synchronize do
|
22
|
+
@queue << obj
|
23
|
+
@resource.broadcast
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
alias << push
|
28
|
+
|
29
|
+
# No timeout means waiting up to 31 years
|
30
|
+
#
|
31
|
+
# @param timeout [Integer] max number of seconds to wait on the pop
|
32
|
+
# @return [Object] element inserted on the array or `nil` on timeout
|
33
|
+
#
|
34
|
+
# @note We use timeout in seconds because this is how Ruby 3.2+ works and we want to have
|
35
|
+
# the same API for newer and older Ruby versions
|
36
|
+
def pop(timeout: 10_000_000_000)
|
37
|
+
deadline = monotonic_now + timeout * 1000
|
38
|
+
|
39
|
+
@mutex.synchronize do
|
40
|
+
loop do
|
41
|
+
return @queue.pop unless @queue.empty?
|
42
|
+
return @queue.pop if @queue.closed?
|
43
|
+
|
44
|
+
to_wait = (deadline - monotonic_now) / 1_000.0
|
45
|
+
|
46
|
+
return nil if to_wait <= 0
|
47
|
+
|
48
|
+
@resource.wait(@mutex, to_wait)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Closes the internal queue and releases the lock
|
54
|
+
def close
|
55
|
+
@mutex.synchronize do
|
56
|
+
@queue.close
|
57
|
+
@resource.broadcast
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -3,20 +3,25 @@
|
|
3
3
|
module Karafka
|
4
4
|
module Routing
|
5
5
|
# Builder used as a DSL layer for building consumers and telling them which topics to consume
|
6
|
+
#
|
7
|
+
# @note We lock the access just in case this is used in patterns. The locks here do not have
|
8
|
+
# any impact on routing usage unless being expanded, so no race conditions risks.
|
9
|
+
#
|
6
10
|
# @example Build a simple (most common) route
|
7
11
|
# consumers do
|
8
12
|
# topic :new_videos do
|
9
13
|
# consumer NewVideosConsumer
|
10
14
|
# end
|
11
15
|
# end
|
12
|
-
class Builder <
|
16
|
+
class Builder < Array
|
13
17
|
# Empty default per-topic config
|
14
18
|
EMPTY_DEFAULTS = ->(_) {}.freeze
|
15
19
|
|
16
20
|
private_constant :EMPTY_DEFAULTS
|
17
21
|
|
18
22
|
def initialize
|
19
|
-
@
|
23
|
+
@mutex = Mutex.new
|
24
|
+
@draws = []
|
20
25
|
@defaults = EMPTY_DEFAULTS
|
21
26
|
super
|
22
27
|
end
|
@@ -34,21 +39,23 @@ module Karafka
|
|
34
39
|
# end
|
35
40
|
# end
|
36
41
|
def draw(&block)
|
37
|
-
@
|
42
|
+
@mutex.synchronize do
|
43
|
+
@draws << block
|
38
44
|
|
39
|
-
|
45
|
+
instance_eval(&block)
|
40
46
|
|
41
|
-
|
42
|
-
|
43
|
-
|
47
|
+
each do |consumer_group|
|
48
|
+
# Validate consumer group settings
|
49
|
+
Contracts::ConsumerGroup.new.validate!(consumer_group.to_h)
|
44
50
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
51
|
+
# and then its topics settings
|
52
|
+
consumer_group.topics.each do |topic|
|
53
|
+
Contracts::Topic.new.validate!(topic.to_h)
|
54
|
+
end
|
49
55
|
|
50
|
-
|
51
|
-
|
56
|
+
# Initialize subscription groups after all the routing is done
|
57
|
+
consumer_group.subscription_groups
|
58
|
+
end
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
@@ -61,9 +68,11 @@ module Karafka
|
|
61
68
|
|
62
69
|
# Clears the builder and the draws memory
|
63
70
|
def clear
|
64
|
-
@
|
65
|
-
|
66
|
-
|
71
|
+
@mutex.synchronize do
|
72
|
+
@defaults = EMPTY_DEFAULTS
|
73
|
+
@draws.clear
|
74
|
+
super
|
75
|
+
end
|
67
76
|
end
|
68
77
|
|
69
78
|
# @param block [Proc] block with per-topic evaluated defaults
|
@@ -71,7 +80,13 @@ module Karafka
|
|
71
80
|
def defaults(&block)
|
72
81
|
return @defaults unless block
|
73
82
|
|
74
|
-
@
|
83
|
+
if @mutex.owned?
|
84
|
+
@defaults = block
|
85
|
+
else
|
86
|
+
@mutex.synchronize do
|
87
|
+
@defaults = block
|
88
|
+
end
|
89
|
+
end
|
75
90
|
end
|
76
91
|
|
77
92
|
private
|
@@ -10,19 +10,24 @@ module Karafka
|
|
10
10
|
class SubscriptionGroup
|
11
11
|
attr_reader :id, :name, :topics, :kafka, :consumer_group
|
12
12
|
|
13
|
-
#
|
14
|
-
|
13
|
+
# Lock for generating new ids safely
|
14
|
+
ID_MUTEX = Mutex.new
|
15
15
|
|
16
|
-
private_constant :
|
16
|
+
private_constant :ID_MUTEX
|
17
17
|
|
18
18
|
class << self
|
19
19
|
# Generates new subscription group id that will be used in case of anonymous subscription
|
20
20
|
# groups
|
21
21
|
# @return [String] hex(6) compatible reproducible id
|
22
22
|
def id
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
ID_MUTEX.synchronize do
|
24
|
+
@group_counter ||= 0
|
25
|
+
@group_counter += 1
|
26
|
+
|
27
|
+
::Digest::MD5.hexdigest(
|
28
|
+
@group_counter.to_s
|
29
|
+
)[0..11]
|
30
|
+
end
|
26
31
|
end
|
27
32
|
end
|
28
33
|
|
data/lib/karafka/runner.rb
CHANGED
@@ -8,7 +8,7 @@ module Karafka
|
|
8
8
|
def call
|
9
9
|
# Despite possibility of having several independent listeners, we aim to have one queue for
|
10
10
|
# jobs across and one workers poll for that
|
11
|
-
jobs_queue =
|
11
|
+
jobs_queue = App.config.internal.processing.jobs_queue_class.new
|
12
12
|
|
13
13
|
workers = Processing::WorkersBatch.new(jobs_queue)
|
14
14
|
listeners = Connection::ListenersBatch.new(jobs_queue)
|
data/lib/karafka/setup/config.rb
CHANGED
@@ -152,6 +152,17 @@ module Karafka
|
|
152
152
|
# instances
|
153
153
|
setting :process, default: Process.new
|
154
154
|
|
155
|
+
# Interval of "ticking". This is used to define the maximum time between consecutive
|
156
|
+
# polling of the main rdkafka queue. It should match also the `statistics.interval.ms`
|
157
|
+
# smallest value defined in any of the per-kafka settings, so metrics are published with
|
158
|
+
# the desired frequency. It is set to 5 seconds because `statistics.interval.ms` is also
|
159
|
+
# set to five seconds.
|
160
|
+
#
|
161
|
+
# It is NOT allowed to set it to a value less than 1 seconds because it could cause polling
|
162
|
+
# not to have enough time to run. This (not directly) defines also a single poll
|
163
|
+
# max timeout as to allow for frequent enough events polling
|
164
|
+
setting :tick_interval, default: 5_000
|
165
|
+
|
155
166
|
# Namespace for CLI related settings
|
156
167
|
setting :cli do
|
157
168
|
# option contract [Object] cli setup validation contract (in the context of options and
|
@@ -198,8 +209,9 @@ module Karafka
|
|
198
209
|
end
|
199
210
|
|
200
211
|
setting :processing do
|
212
|
+
setting :jobs_queue_class, default: Processing::JobsQueue
|
201
213
|
# option scheduler [Object] scheduler we will be using
|
202
|
-
setting :
|
214
|
+
setting :scheduler_class, default: Processing::Scheduler
|
203
215
|
# option jobs_builder [Object] jobs builder we want to use
|
204
216
|
setting :jobs_builder, default: Processing::JobsBuilder.new
|
205
217
|
# option coordinator [Class] work coordinator we want to user for processing coordination
|
data/lib/karafka/version.rb
CHANGED
data/lib/karafka.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|