karafka 2.4.3 → 2.4.5
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 +0 -0
- data/.github/workflows/ci.yml +5 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +15 -15
- data/docker-compose.yml +1 -1
- data/karafka.gemspec +1 -1
- data/lib/karafka/connection/client.rb +3 -0
- data/lib/karafka/embedded.rb +44 -2
- data/lib/karafka/helpers/async.rb +10 -1
- data/lib/karafka/instrumentation/logger_listener.rb +21 -4
- data/lib/karafka/pro/connection/manager.rb +4 -0
- data/lib/karafka/pro/processing/coordinators/filters_applier.rb +17 -0
- data/lib/karafka/pro/processing/filters/base.rb +12 -0
- data/lib/karafka/pro/processing/filters/delayer.rb +2 -2
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +2 -1
- data/lib/karafka/pro/processing/strategies/ftr/default.rb +13 -0
- data/lib/karafka/routing/subscription_group.rb +21 -1
- data/lib/karafka/server.rb +21 -1
- data/lib/karafka/setup/attributes_map.rb +2 -0
- data/lib/karafka/setup/config.rb +0 -3
- data/lib/karafka/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +4 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe76e139b2007e11b0cb9393bc94e2b5b9cad5dc40d69b5ad2bfa34a0555e2b8
|
4
|
+
data.tar.gz: e288c63648d17388f577a45b51267e30fceb4726def0a42c40a9133de27d93f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5e46439a216e66c728ceb74070832407d39b1ebe06d21c858f3e3a0e8e599df15b749318131aa82a5bc994e6dd7ed3094f44639e706a57231f031f0e52186b6
|
7
|
+
data.tar.gz: dc44b6a2f0e5d21bdcbe156f0cc94c324d586c74e9fb757454157ac014897fc3cbd683acb19e6397d668f67a9c68e97d683521f1ceecdbf338e24f3d497c688d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/.github/workflows/ci.yml
CHANGED
@@ -148,6 +148,11 @@ jobs:
|
|
148
148
|
|
149
149
|
bundle config set without 'tools benchmarks docs'
|
150
150
|
|
151
|
+
- name: Fix directory permissions for Bundler
|
152
|
+
run: |
|
153
|
+
chmod -R o-w /opt/hostedtoolcache/Ruby/3*/x64/lib/ruby/gems/3*/gems
|
154
|
+
chmod +t /opt/hostedtoolcache/Ruby/3*/x64/lib/ruby/gems/3*/gems
|
155
|
+
|
151
156
|
- name: Bundle install
|
152
157
|
run: |
|
153
158
|
bundle config set without development
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.3.
|
1
|
+
3.3.4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# Karafka framework changelog
|
2
2
|
|
3
|
+
## 2.4.5 (2024-07-18)
|
4
|
+
- [Change] Inject `client.id` when building subscription group and not during the initial setup.
|
5
|
+
- [Fix] Mitigate `confluentinc/librdkafka/issues/4783` by injecting dynamic client id when using `cooperative-sticky` strategy.
|
6
|
+
|
7
|
+
### Change Note
|
8
|
+
|
9
|
+
`client.id` is technically a low-importance value that should not (aside from this error) impact operations. This is why it is not considered a breaking change. This change may be reverted when the original issue is fixed in librdkafka.
|
10
|
+
|
11
|
+
## 2.4.4 (2024-07-04)
|
12
|
+
- [Enhancement] Allow for offset storing from the Filtering API.
|
13
|
+
- [Enhancement] Print more extensive error info on forceful shutdown.
|
14
|
+
- [Enhancement] Include `original_key` in the DLQ dispatch headers.
|
15
|
+
- [Enhancement] Support embedding mode control management from the trap context.
|
16
|
+
- [Enhancement] Make sure, that the listener thread is stopped before restarting.
|
17
|
+
- [Fix] Do not block on hanging listener shutdown when invoking forceful shutdown.
|
18
|
+
- [Fix] Static membership fencing error is not propagated explicitly enough.
|
19
|
+
- [Fix] Make sure DLQ dispatches raw headers and not deserialized headers (same as payload).
|
20
|
+
- [Fix] Fix a typo where `ms` in logger listener would not have space before it.
|
21
|
+
- [Maintenance] Require `karafka-core` `>=` `2.4.3`.
|
22
|
+
- [Maintenance] Allow for usage of `karafka-rdkafka` `~` `0.16` to support librdkafka `2.4.0`.
|
23
|
+
- [Maintenance] Lower the precision reporting to 100 microseconds in the logger listener.
|
24
|
+
|
3
25
|
## 2.4.3 (2024-06-12)
|
4
26
|
- [Enhancement] Allow for customization of Virtual Partitions reducer for enhanced parallelization.
|
5
27
|
- [Enhancement] Add more error codes to early report on polling issues (kidlab)
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
karafka (2.4.
|
4
|
+
karafka (2.4.5)
|
5
5
|
base64 (~> 0.2)
|
6
|
-
karafka-core (>= 2.4.
|
6
|
+
karafka-core (>= 2.4.3, < 2.5.0)
|
7
7
|
waterdrop (>= 2.7.3, < 3.0.0)
|
8
8
|
zeitwerk (~> 2.3)
|
9
9
|
|
@@ -26,12 +26,12 @@ GEM
|
|
26
26
|
base64 (0.2.0)
|
27
27
|
bigdecimal (3.1.8)
|
28
28
|
byebug (11.1.3)
|
29
|
-
concurrent-ruby (1.3.
|
29
|
+
concurrent-ruby (1.3.3)
|
30
30
|
connection_pool (2.4.1)
|
31
31
|
diff-lcs (1.5.1)
|
32
32
|
docile (1.4.0)
|
33
33
|
drb (2.2.1)
|
34
|
-
erubi (1.
|
34
|
+
erubi (1.13.0)
|
35
35
|
factory_bot (6.4.6)
|
36
36
|
activesupport (>= 5.0.0)
|
37
37
|
ffi (1.17.0)
|
@@ -39,13 +39,13 @@ GEM
|
|
39
39
|
activesupport (>= 6.1)
|
40
40
|
i18n (1.14.5)
|
41
41
|
concurrent-ruby (~> 1.0)
|
42
|
-
karafka-core (2.4.
|
43
|
-
karafka-rdkafka (>= 0.15.0, < 0.
|
44
|
-
karafka-rdkafka (0.
|
42
|
+
karafka-core (2.4.3)
|
43
|
+
karafka-rdkafka (>= 0.15.0, < 0.17.0)
|
44
|
+
karafka-rdkafka (0.16.0)
|
45
45
|
ffi (~> 1.15)
|
46
46
|
mini_portile2 (~> 2.6)
|
47
47
|
rake (> 12)
|
48
|
-
karafka-testing (2.4.
|
48
|
+
karafka-testing (2.4.4)
|
49
49
|
karafka (>= 2.4.0, < 2.5.0)
|
50
50
|
waterdrop (>= 2.7.0)
|
51
51
|
karafka-web (0.9.1)
|
@@ -55,12 +55,12 @@ GEM
|
|
55
55
|
roda (~> 3.68, >= 3.69)
|
56
56
|
tilt (~> 2.0)
|
57
57
|
mini_portile2 (2.8.7)
|
58
|
-
minitest (5.
|
58
|
+
minitest (5.24.0)
|
59
59
|
mutex_m (0.2.0)
|
60
60
|
ostruct (0.6.0)
|
61
|
-
rack (3.
|
61
|
+
rack (3.1.4)
|
62
62
|
rake (13.2.1)
|
63
|
-
roda (3.
|
63
|
+
roda (3.81.0)
|
64
64
|
rack
|
65
65
|
rspec (3.13.0)
|
66
66
|
rspec-core (~> 3.13.0)
|
@@ -68,7 +68,7 @@ GEM
|
|
68
68
|
rspec-mocks (~> 3.13.0)
|
69
69
|
rspec-core (3.13.0)
|
70
70
|
rspec-support (~> 3.13.0)
|
71
|
-
rspec-expectations (3.13.
|
71
|
+
rspec-expectations (3.13.1)
|
72
72
|
diff-lcs (>= 1.2.0, < 2.0)
|
73
73
|
rspec-support (~> 3.13.0)
|
74
74
|
rspec-mocks (3.13.1)
|
@@ -81,14 +81,14 @@ GEM
|
|
81
81
|
simplecov_json_formatter (~> 0.1)
|
82
82
|
simplecov-html (0.12.3)
|
83
83
|
simplecov_json_formatter (0.1.4)
|
84
|
-
tilt (2.
|
84
|
+
tilt (2.4.0)
|
85
85
|
tzinfo (2.0.6)
|
86
86
|
concurrent-ruby (~> 1.0)
|
87
87
|
waterdrop (2.7.3)
|
88
88
|
karafka-core (>= 2.4.0, < 3.0.0)
|
89
89
|
karafka-rdkafka (>= 0.15.1)
|
90
90
|
zeitwerk (~> 2.3)
|
91
|
-
zeitwerk (2.6.
|
91
|
+
zeitwerk (2.6.16)
|
92
92
|
|
93
93
|
PLATFORMS
|
94
94
|
ruby
|
@@ -106,4 +106,4 @@ DEPENDENCIES
|
|
106
106
|
simplecov
|
107
107
|
|
108
108
|
BUNDLED WITH
|
109
|
-
2.5.
|
109
|
+
2.5.14
|
data/docker-compose.yml
CHANGED
data/karafka.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
DESC
|
23
23
|
|
24
24
|
spec.add_dependency 'base64', '~> 0.2'
|
25
|
-
spec.add_dependency 'karafka-core', '>= 2.4.
|
25
|
+
spec.add_dependency 'karafka-core', '>= 2.4.3', '< 2.5.0'
|
26
26
|
spec.add_dependency 'waterdrop', '>= 2.7.3', '< 3.0.0'
|
27
27
|
spec.add_dependency 'zeitwerk', '~> 2.3'
|
28
28
|
|
@@ -559,6 +559,9 @@ module Karafka
|
|
559
559
|
early_report = true
|
560
560
|
when :cluster_authorization_failed # 31
|
561
561
|
early_report = true
|
562
|
+
# This can happen for many reasons, including issues with static membership being fenced
|
563
|
+
when :fatal # -150
|
564
|
+
early_report = true
|
562
565
|
# @see
|
563
566
|
# https://github.com/confluentinc/confluent-kafka-dotnet/issues/1366#issuecomment-821842990
|
564
567
|
# This will be raised each time poll detects a non-existing topic. When auto creation is
|
data/lib/karafka/embedded.rb
CHANGED
@@ -2,11 +2,28 @@
|
|
2
2
|
|
3
3
|
module Karafka
|
4
4
|
# Allows to start and stop Karafka as part of a different process
|
5
|
+
# Following limitations and restrictions apply:
|
6
|
+
#
|
7
|
+
# - `#start` cannot be called from a trap context - non blocking
|
8
|
+
# - `#quiet` - can be called from a trap context - non blocking
|
9
|
+
# - `#stop` - can be called from a trap context - blocking
|
5
10
|
module Embedded
|
6
11
|
class << self
|
12
|
+
# Lock for ensuring we do not control embedding in parallel
|
13
|
+
MUTEX = Mutex.new
|
14
|
+
|
15
|
+
private_constant :MUTEX
|
16
|
+
|
7
17
|
# Starts Karafka without supervision and without ownership of signals in a background thread
|
8
18
|
# so it won't interrupt other things running
|
9
19
|
def start
|
20
|
+
MUTEX.synchronize do
|
21
|
+
# Prevent from double-starting
|
22
|
+
return if @started
|
23
|
+
|
24
|
+
@started = true
|
25
|
+
end
|
26
|
+
|
10
27
|
Thread.new do
|
11
28
|
Thread.current.name = 'karafka.embedded'
|
12
29
|
|
@@ -19,9 +36,32 @@ module Karafka
|
|
19
36
|
#
|
20
37
|
# @note This method is blocking because we want to wait until Karafka is stopped with final
|
21
38
|
# process shutdown
|
39
|
+
#
|
40
|
+
# @note This method **is** safe to run from a trap context.
|
22
41
|
def stop
|
23
|
-
#
|
24
|
-
|
42
|
+
# Prevent from double stopping
|
43
|
+
unless @stopping
|
44
|
+
Thread.new do
|
45
|
+
Thread.current.name = 'karafka.embedded.stopping'
|
46
|
+
|
47
|
+
stop = false
|
48
|
+
|
49
|
+
# We spawn a new thread because `#stop` may be called from a trap context
|
50
|
+
MUTEX.synchronize do
|
51
|
+
break if @stopping
|
52
|
+
|
53
|
+
@stopping = true
|
54
|
+
stop = true
|
55
|
+
end
|
56
|
+
|
57
|
+
next unless stop
|
58
|
+
|
59
|
+
Karafka::Server.stop
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Since we want to have this blocking, we wait for the background thread
|
64
|
+
sleep(0.1) until Karafka::App.terminated?
|
25
65
|
end
|
26
66
|
|
27
67
|
# Quiets Karafka upon any event
|
@@ -29,6 +69,8 @@ module Karafka
|
|
29
69
|
# @note This method is not blocking and will not wait for Karafka to fully quiet.
|
30
70
|
# It will trigger the quiet procedure but won't wait.
|
31
71
|
#
|
72
|
+
# @note This method **can** be called from a trap context.
|
73
|
+
#
|
32
74
|
# @note Please keep in mind you need to `#stop` to actually stop the server anyhow.
|
33
75
|
def quiet
|
34
76
|
Karafka::Server.quiet
|
@@ -22,7 +22,16 @@ module Karafka
|
|
22
22
|
def included(base)
|
23
23
|
base.extend ::Forwardable
|
24
24
|
|
25
|
-
base.def_delegators :@thread, :join, :terminate, :
|
25
|
+
base.def_delegators :@thread, :join, :terminate, :name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] true if thread is present and is running, false otherwise
|
30
|
+
def alive?
|
31
|
+
MUTEX.synchronize do
|
32
|
+
return false unless @thread
|
33
|
+
|
34
|
+
@thread.alive?
|
26
35
|
end
|
27
36
|
end
|
28
37
|
|
@@ -41,7 +41,7 @@ module Karafka
|
|
41
41
|
return unless log_polling?
|
42
42
|
|
43
43
|
listener = event[:caller]
|
44
|
-
time = event[:time]
|
44
|
+
time = event[:time].round(2)
|
45
45
|
messages_count = event[:messages_buffer].size
|
46
46
|
|
47
47
|
message = "[#{listener.id}] Polled #{messages_count} messages in #{time}ms"
|
@@ -69,14 +69,14 @@ module Karafka
|
|
69
69
|
# @param event [Karafka::Core::Monitoring::Event] event details including payload
|
70
70
|
def on_worker_processed(event)
|
71
71
|
job = event[:job]
|
72
|
-
time = event[:time]
|
72
|
+
time = event[:time].round(2)
|
73
73
|
job_type = job.class.to_s.split('::').last
|
74
74
|
consumer = job.executor.topic.consumer
|
75
75
|
topic = job.executor.topic.name
|
76
76
|
partition = job.executor.partition
|
77
77
|
info <<~MSG.tr("\n", ' ').strip!
|
78
78
|
[#{job.id}] #{job_type} job for #{consumer}
|
79
|
-
on #{topic}/#{partition} finished in #{time}ms
|
79
|
+
on #{topic}/#{partition} finished in #{time} ms
|
80
80
|
MSG
|
81
81
|
end
|
82
82
|
|
@@ -306,7 +306,24 @@ module Karafka
|
|
306
306
|
fatal "Runner crashed due to an error: #{error}"
|
307
307
|
fatal details
|
308
308
|
when 'app.stopping.error'
|
309
|
-
|
309
|
+
# Counts number of workers and listeners that were still active when forcing the
|
310
|
+
# shutdown. Please note, that unless all listeners are closed, workers will not finalize
|
311
|
+
# their operations as well.
|
312
|
+
# We need to check if listeners and workers are assigned as during super early stages of
|
313
|
+
# boot they are not.
|
314
|
+
listeners = Server.listeners ? Server.listeners.count(&:active?) : 0
|
315
|
+
workers = Server.workers ? Server.workers.count(&:alive?) : 0
|
316
|
+
|
317
|
+
message = <<~MSG.tr("\n", ' ').strip!
|
318
|
+
Forceful Karafka server stop with:
|
319
|
+
#{workers} active workers and
|
320
|
+
#{listeners} active listeners
|
321
|
+
MSG
|
322
|
+
|
323
|
+
error message
|
324
|
+
when 'app.forceful_stopping.error'
|
325
|
+
error "Forceful shutdown error occurred: #{error}"
|
326
|
+
error details
|
310
327
|
when 'librdkafka.error'
|
311
328
|
error "librdkafka internal error occurred: #{error}"
|
312
329
|
error details
|
@@ -216,6 +216,10 @@ module Karafka
|
|
216
216
|
next unless multi_part_sgs_families.include?(sg_listener.subscription_group.name)
|
217
217
|
# Skip already active connections
|
218
218
|
next unless sg_listener.pending? || sg_listener.stopped?
|
219
|
+
# Ensure that the listener thread under which we operate is already stopped and
|
220
|
+
# is not dangling. While not likely to happen, this may protect against a
|
221
|
+
# case where a shutdown critical crash would case a restart of the same listener
|
222
|
+
next if sg_listener.alive?
|
219
223
|
|
220
224
|
touch(sg_listener.subscription_group.id)
|
221
225
|
sg_listener.start!
|
@@ -89,6 +89,23 @@ module Karafka
|
|
89
89
|
applied.map(&:cursor).compact.min_by(&:offset)
|
90
90
|
end
|
91
91
|
|
92
|
+
# @return [Boolean] did any of the filters requested offset storage during filter
|
93
|
+
# application
|
94
|
+
def mark_as_consumed?
|
95
|
+
# We can manage filtering offset only when user wanted that and there is a cursor
|
96
|
+
# to use
|
97
|
+
applied.any?(&:mark_as_consumed?) && cursor
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [Symbol] `:mark_as_consumed` or `:mark_as_consumed!`
|
101
|
+
def marking_method
|
102
|
+
candidates = applied.map(&:marking_method)
|
103
|
+
|
104
|
+
return :mark_as_consumed! if candidates.include?(:mark_as_consumed!)
|
105
|
+
|
106
|
+
:mark_as_consumed
|
107
|
+
end
|
108
|
+
|
92
109
|
private
|
93
110
|
|
94
111
|
# @return [Boolean] is filtering active
|
@@ -54,6 +54,18 @@ module Karafka
|
|
54
54
|
def timeout
|
55
55
|
0
|
56
56
|
end
|
57
|
+
|
58
|
+
# @return [Boolean] should we use the cursor value to mark as consumed. If any of the
|
59
|
+
# filters returns true, we return lowers applicable cursor value (if any)
|
60
|
+
def mark_as_consumed?
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Symbol] `:mark_as_consumed` or `:mark_as_consumed!`. Applicable only if
|
65
|
+
# marking is requested
|
66
|
+
def marking_method
|
67
|
+
:mark_as_consumed
|
68
|
+
end
|
57
69
|
end
|
58
70
|
end
|
59
71
|
end
|
@@ -33,7 +33,7 @@ module Karafka
|
|
33
33
|
|
34
34
|
# Time on message is in seconds with ms precision, so we need to convert the ttl that
|
35
35
|
# is in ms to this format
|
36
|
-
border = ::Time.now.utc - @delay / 1_000.
|
36
|
+
border = ::Time.now.utc - @delay / 1_000.0
|
37
37
|
|
38
38
|
messages.delete_if do |message|
|
39
39
|
too_young = message.timestamp > border
|
@@ -52,7 +52,7 @@ module Karafka
|
|
52
52
|
def timeout
|
53
53
|
return 0 unless @cursor
|
54
54
|
|
55
|
-
timeout = (@delay / 1_000.
|
55
|
+
timeout = (@delay / 1_000.0) - (::Time.now.utc - @cursor.timestamp)
|
56
56
|
|
57
57
|
timeout <= 0 ? 0 : timeout * 1_000
|
58
58
|
end
|
@@ -154,11 +154,12 @@ module Karafka
|
|
154
154
|
topic: topic.dead_letter_queue.topic,
|
155
155
|
key: original_partition,
|
156
156
|
payload: skippable_message.raw_payload,
|
157
|
-
headers: skippable_message.
|
157
|
+
headers: skippable_message.raw_headers.merge(
|
158
158
|
'original_topic' => topic.name,
|
159
159
|
'original_partition' => original_partition,
|
160
160
|
'original_offset' => skippable_message.offset.to_s,
|
161
161
|
'original_consumer_group' => topic.consumer_group.id,
|
162
|
+
'original_key' => skippable_message.raw_key.to_s,
|
162
163
|
'original_attempts' => attempt.to_s
|
163
164
|
)
|
164
165
|
}
|
@@ -66,6 +66,19 @@ module Karafka
|
|
66
66
|
# and this should not happen
|
67
67
|
throttle_timeout = filter.timeout
|
68
68
|
|
69
|
+
# If user requested marking when applying filter, we mark. We may be in the user
|
70
|
+
# flow but even then this is not a problem. Older offsets will be ignored since
|
71
|
+
# we do not force the offset update (expected) and newer are on the user to control.
|
72
|
+
# This can be primarily used when filtering large quantities of data to mark on the
|
73
|
+
# idle runs, so lag reporting is aware that those messages were not consumed but also
|
74
|
+
# are no longer relevant
|
75
|
+
if filter.mark_as_consumed?
|
76
|
+
send(
|
77
|
+
filter.marking_method,
|
78
|
+
filter.cursor
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
69
82
|
case filter.action
|
70
83
|
when :skip
|
71
84
|
nil
|
@@ -121,8 +121,8 @@ module Karafka
|
|
121
121
|
kafka = Setup::AttributesMap.consumer(@topics.first.kafka.dup)
|
122
122
|
|
123
123
|
inject_group_instance_id(kafka)
|
124
|
+
inject_client_id(kafka)
|
124
125
|
|
125
|
-
kafka[:'client.id'] ||= client_id
|
126
126
|
kafka[:'group.id'] ||= @consumer_group.id
|
127
127
|
kafka[:'auto.offset.reset'] ||= @topics.first.initial_offset
|
128
128
|
# Karafka manages the offsets based on the processing state, thus we do not rely on the
|
@@ -132,6 +132,26 @@ module Karafka
|
|
132
132
|
kafka
|
133
133
|
end
|
134
134
|
|
135
|
+
# Sets (if needed) the client.id attribute
|
136
|
+
#
|
137
|
+
# @param kafka [Hash] kafka level config
|
138
|
+
def inject_client_id(kafka)
|
139
|
+
# If client id is set directly on librdkafka level, we do nothing and just go with what
|
140
|
+
# end user has configured
|
141
|
+
return if kafka.key?(:'client.id')
|
142
|
+
|
143
|
+
# This mitigates an issue for multiplexing and potentially other cases when running
|
144
|
+
# multiple karafka processes on one machine, where librdkafka goes into an infinite
|
145
|
+
# loop when using cooperative-sticky and upscaling.
|
146
|
+
#
|
147
|
+
# @see https://github.com/confluentinc/librdkafka/issues/4783
|
148
|
+
kafka[:'client.id'] = if kafka[:'partition.assignment.strategy'] == 'cooperative-sticky'
|
149
|
+
"#{client_id}/#{Time.now.to_f}/#{SecureRandom.hex[0..9]}"
|
150
|
+
else
|
151
|
+
client_id
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
135
155
|
# If we use static group memberships, there can be a case, where same instance id would
|
136
156
|
# be set on many subscription groups as the group instance id from Karafka perspective is
|
137
157
|
# set per config. Each instance even if they are subscribed to different topics needs to
|
data/lib/karafka/server.rb
CHANGED
@@ -3,6 +3,12 @@
|
|
3
3
|
module Karafka
|
4
4
|
# Karafka consuming server class
|
5
5
|
class Server
|
6
|
+
# How long should we wait on the listeners forceful shutdown when they are stuck beyond the
|
7
|
+
# shutdown timeout before forcing a bypass
|
8
|
+
FORCEFUL_SHUTDOWN_WAIT = 5
|
9
|
+
|
10
|
+
private_constant :FORCEFUL_SHUTDOWN_WAIT
|
11
|
+
|
6
12
|
class << self
|
7
13
|
# Set of consuming threads. Each consumer thread contains a single consumer
|
8
14
|
attr_accessor :listeners
|
@@ -105,9 +111,23 @@ module Karafka
|
|
105
111
|
# We're done waiting, lets kill them!
|
106
112
|
workers.each(&:terminate)
|
107
113
|
listeners.active.each(&:terminate)
|
114
|
+
|
108
115
|
# We always need to shutdown clients to make sure we do not force the GC to close consumer.
|
109
116
|
# This can cause memory leaks and crashes.
|
110
|
-
|
117
|
+
# We run it in a separate thread in case this would hang and we ignore it after the time
|
118
|
+
# we assigned to it and force shutdown as we prefer to stop the process rather than wait
|
119
|
+
# indefinitely even with risk of VM crash as this is a last resort.
|
120
|
+
Thread.new do
|
121
|
+
listeners.each(&:shutdown)
|
122
|
+
rescue StandardError => e
|
123
|
+
# If anything wrong happened during shutdown, we also want to record it
|
124
|
+
Karafka.monitor.instrument(
|
125
|
+
'error.occurred',
|
126
|
+
caller: self,
|
127
|
+
error: e,
|
128
|
+
type: 'app.forceful_stopping.error'
|
129
|
+
)
|
130
|
+
end.join(FORCEFUL_SHUTDOWN_WAIT)
|
111
131
|
|
112
132
|
# We also do not forcefully terminate everything when running in the embedded mode,
|
113
133
|
# otherwise we would overwrite the shutdown process of the process that started Karafka
|
data/lib/karafka/setup/config.rb
CHANGED
@@ -385,9 +385,6 @@ module Karafka
|
|
385
385
|
config.kafka[key] = value
|
386
386
|
end
|
387
387
|
|
388
|
-
# Use Karafka client_id as kafka client id if not set
|
389
|
-
config.kafka[:'client.id'] ||= config.client_id
|
390
|
-
|
391
388
|
return if Karafka::App.env.production?
|
392
389
|
|
393
390
|
KAFKA_DEV_DEFAULTS.each do |key, value|
|
data/lib/karafka/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: karafka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.4.
|
4
|
+
version: 2.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maciej Mensfeld
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
AnG1dJU+yL2BK7vaVytLTstJME5mepSZ46qqIJXMuWob/YPDmVaBF39TDSG9e34s
|
36
36
|
msG3BiCqgOgHAnL23+CN3Rt8MsuRfEtoTKpJVcCfoEoNHOkc
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2024-
|
38
|
+
date: 2024-07-18 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: base64
|
@@ -57,7 +57,7 @@ dependencies:
|
|
57
57
|
requirements:
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: 2.4.
|
60
|
+
version: 2.4.3
|
61
61
|
- - "<"
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: 2.5.0
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
requirements:
|
68
68
|
- - ">="
|
69
69
|
- !ruby/object:Gem::Version
|
70
|
-
version: 2.4.
|
70
|
+
version: 2.4.3
|
71
71
|
- - "<"
|
72
72
|
- !ruby/object:Gem::Version
|
73
73
|
version: 2.5.0
|
metadata.gz.sig
CHANGED
Binary file
|