karafka 2.3.0.alpha1 → 2.3.0.alpha2
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/CHANGELOG.md +1 -0
- data/Gemfile.lock +5 -5
- data/bin/integrations +1 -1
- data/lib/karafka/errors.rb +4 -0
- data/lib/karafka/pro/connection/manager.rb +23 -54
- data/lib/karafka/pro/processing/strategies/default.rb +22 -2
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +6 -2
- data/lib/karafka/pro/processing/strategies/vp/default.rb +8 -2
- data/lib/karafka/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +2 -2
- 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: 07bebe70b6697a90d04154dcbfe5837f5bcfaf934073f91bbfc9e8939d9d1a6c
|
4
|
+
data.tar.gz: 13e41b276eee5142b55eb6908c8b9292bd8f802f470fb4e9bdc1f812dbd50189
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fef2cbded4409d951cf7e752f25b7db016052cc5f2a77c54ccf7e8778fbf44edba74cd1b04cf82caf341cf05beda552af5c4cbd994a510c54ad1d3c4e561fd17
|
7
|
+
data.tar.gz: 00bf893d7c6f29e559530585c40ba721ab3a9f3ce5906cd7c5b6948f79725d6d99423444d9ec7e6f8593f9780aec3375e400986ed8478dfab2cbbabb5807a85d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Karafka framework changelog
|
2
2
|
|
3
3
|
## 2.3.0 (Unreleased)
|
4
|
+
- **[Feature]** Introduce Exactly-Once Semantics within consumers `#transaction` block (Pro)
|
4
5
|
- **[Feature]** Provide ability to multiplex subscription groups (Pro)
|
5
6
|
- **[Feature]** Provide `Karafka::Admin::Acl` for Kafka ACL management via the Admin APIs.
|
6
7
|
- **[Feature]** Periodic Jobs (Pro)
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
karafka (2.3.0.
|
4
|
+
karafka (2.3.0.alpha2)
|
5
5
|
karafka-core (>= 2.3.0.alpha1, < 2.4.0)
|
6
6
|
waterdrop (>= 2.6.12, < 3.0.0)
|
7
7
|
zeitwerk (~> 2.3)
|
@@ -9,10 +9,10 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
activejob (7.1.
|
13
|
-
activesupport (= 7.1.
|
12
|
+
activejob (7.1.3)
|
13
|
+
activesupport (= 7.1.3)
|
14
14
|
globalid (>= 0.3.6)
|
15
|
-
activesupport (7.1.
|
15
|
+
activesupport (7.1.3)
|
16
16
|
base64
|
17
17
|
bigdecimal
|
18
18
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
@@ -25,7 +25,7 @@ GEM
|
|
25
25
|
base64 (0.2.0)
|
26
26
|
bigdecimal (3.1.5)
|
27
27
|
byebug (11.1.3)
|
28
|
-
concurrent-ruby (1.2.
|
28
|
+
concurrent-ruby (1.2.3)
|
29
29
|
connection_pool (2.4.1)
|
30
30
|
diff-lcs (1.5.0)
|
31
31
|
docile (1.4.0)
|
data/bin/integrations
CHANGED
@@ -28,7 +28,7 @@ ROOT_PATH = Pathname.new(File.expand_path(File.join(File.dirname(__FILE__), '../
|
|
28
28
|
CONCURRENCY = ENV.key?('CI') ? 5 : Etc.nprocessors * 3
|
29
29
|
|
30
30
|
# How may bytes do we want to keep from the stdout in the buffer for when we need to print it
|
31
|
-
MAX_BUFFER_OUTPUT =
|
31
|
+
MAX_BUFFER_OUTPUT = 102_400
|
32
32
|
|
33
33
|
# Abstraction around a single test scenario execution process
|
34
34
|
class Scenario
|
data/lib/karafka/errors.rb
CHANGED
@@ -76,5 +76,9 @@ module Karafka
|
|
76
76
|
|
77
77
|
# Raised when we want to un-pause listener that was not paused
|
78
78
|
InvalidListenerPauseError = Class.new(BaseError)
|
79
|
+
|
80
|
+
# Raised in transactions when we attempt to store offset for a partition that we have lost
|
81
|
+
# This does not affect producer only transactions, hence we raise it only on offset storage
|
82
|
+
AssignmentLostError = Class.new(BaseError)
|
79
83
|
end
|
80
84
|
end
|
@@ -24,18 +24,9 @@ module Karafka
|
|
24
24
|
# @note Manager operations relate to consumer groups and not subscription groups. Since
|
25
25
|
# cluster operations can cause consumer group wide effects, we always apply only one
|
26
26
|
# change on a consumer group.
|
27
|
-
#
|
28
|
-
# @note Since we collect statistical data from listeners and this happens in a background
|
29
|
-
# thread, we need to make sure we lock not to have race conditions with expired data
|
30
|
-
# eviction.
|
31
27
|
class Manager < Karafka::Connection::Manager
|
32
28
|
include Core::Helpers::Time
|
33
29
|
|
34
|
-
# How long should we keep stale stats before evicting them completely
|
35
|
-
EVICTION_DELAY = 5 * 60 * 1_000
|
36
|
-
|
37
|
-
private_constant :EVICTION_DELAY
|
38
|
-
|
39
30
|
# How long should we wait after a rebalance before doing anything on a consumer group
|
40
31
|
#
|
41
32
|
# @param scale_delay [Integer] How long should we wait before making any changes. Any
|
@@ -50,7 +41,6 @@ module Karafka
|
|
50
41
|
state: '',
|
51
42
|
join_state: '',
|
52
43
|
state_age: 0,
|
53
|
-
state_age_sync: monotonic_now,
|
54
44
|
changed_at: monotonic_now
|
55
45
|
}
|
56
46
|
end
|
@@ -65,6 +55,9 @@ module Karafka
|
|
65
55
|
def register(listeners)
|
66
56
|
@listeners = listeners
|
67
57
|
|
58
|
+
# Preload all the keys into the hash so we never add keys to changes but just change them
|
59
|
+
listeners.each { |listener| @changes[listener.subscription_group.id] }
|
60
|
+
|
68
61
|
in_sg_families do |first_subscription_group, sg_listeners|
|
69
62
|
multiplexing = first_subscription_group.multiplexing
|
70
63
|
|
@@ -86,25 +79,22 @@ module Karafka
|
|
86
79
|
# @note Please note that while we collect here per subscription group, we use those metrics
|
87
80
|
# collectively on a whole consumer group. This reduces the friction.
|
88
81
|
def notice(subscription_group_id, statistics)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
state_age_sync: monotonic_now
|
106
|
-
}
|
107
|
-
end
|
82
|
+
times = []
|
83
|
+
# stateage is in microseconds
|
84
|
+
# We monitor broker changes to make sure we do not introduce extra friction
|
85
|
+
times << statistics['brokers'].values.map { |stats| stats['stateage'] }.min / 1_000
|
86
|
+
times << statistics['cgrp']['rebalance_age']
|
87
|
+
times << statistics['cgrp']['stateage']
|
88
|
+
|
89
|
+
# Keep the previous change age for changes that were triggered by us
|
90
|
+
previous_changed_at = @changes[subscription_group_id][:changed_at]
|
91
|
+
|
92
|
+
@changes[subscription_group_id].merge!(
|
93
|
+
state_age: times.min,
|
94
|
+
changed_at: previous_changed_at,
|
95
|
+
join_state: statistics['cgrp']['join_state'],
|
96
|
+
state: statistics['cgrp']['state']
|
97
|
+
)
|
108
98
|
end
|
109
99
|
|
110
100
|
# Shuts down all the listeners when it is time (including moving to quiet) or rescales
|
@@ -158,8 +148,6 @@ module Karafka
|
|
158
148
|
#
|
159
149
|
# We always run scaling down and up because it may be applicable to different CGs
|
160
150
|
def rescale
|
161
|
-
evict
|
162
|
-
|
163
151
|
scale_down
|
164
152
|
scale_up
|
165
153
|
end
|
@@ -232,23 +220,11 @@ module Karafka
|
|
232
220
|
end
|
233
221
|
end
|
234
222
|
|
235
|
-
# Removes states that are no longer being reported for stopped/pending listeners
|
236
|
-
def evict
|
237
|
-
@mutex.synchronize do
|
238
|
-
@changes.delete_if do |_, details|
|
239
|
-
monotonic_now - details[:state_age_sync] >= EVICTION_DELAY
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
223
|
# Indicates, that something has changed on a subscription group. We consider every single
|
245
224
|
# change we make as a change to the setup as well.
|
246
225
|
# @param subscription_group_id [String]
|
247
226
|
def touch(subscription_group_id)
|
248
|
-
@
|
249
|
-
@changes[subscription_group_id][:changed_at] = 0
|
250
|
-
@changes[subscription_group_id][:state_age_sync] = monotonic_now
|
251
|
-
end
|
227
|
+
@changes[subscription_group_id][:changed_at] = 0
|
252
228
|
end
|
253
229
|
|
254
230
|
# @param sg_listeners [Array<Listener>] listeners from one multiplexed sg
|
@@ -257,17 +233,10 @@ module Karafka
|
|
257
233
|
# are also stable. This is a strong indicator that no rebalances or other operations are
|
258
234
|
# happening at a given moment.
|
259
235
|
def stable?(sg_listeners)
|
260
|
-
# If none of listeners has changes reported it means we did not yet start collecting
|
261
|
-
# metrics about any of them and at least one must be present. We do not consider it
|
262
|
-
# stable in such case as we still are waiting for metrics.
|
263
|
-
return false if sg_listeners.none? do |sg_listener|
|
264
|
-
@changes.key?(sg_listener.subscription_group.id)
|
265
|
-
end
|
266
|
-
|
267
236
|
sg_listeners.all? do |sg_listener|
|
268
|
-
#
|
269
|
-
#
|
270
|
-
next true unless
|
237
|
+
# If a listener is not active, we do not take it into consideration when looking at
|
238
|
+
# the stability data
|
239
|
+
next true unless sg_listener.active?
|
271
240
|
|
272
241
|
state = @changes[sg_listener.subscription_group.id]
|
273
242
|
|
@@ -27,6 +27,21 @@ module Karafka
|
|
27
27
|
# Apply strategy for a non-feature based flow
|
28
28
|
FEATURES = %i[].freeze
|
29
29
|
|
30
|
+
# Allows to set offset metadata that will be used with the upcoming marking as consumed
|
31
|
+
# as long as a different offset metadata was not used. After it was used either via
|
32
|
+
# `#mark_as_consumed` or `#mark_as_consumed!` it will be set back to `nil`. It is done
|
33
|
+
# that way to provide the end user with ability to influence metadata on the non-user
|
34
|
+
# initiated markings in complex flows.
|
35
|
+
#
|
36
|
+
# @param offset_metadata [String, nil] metadata we want to store with the upcoming
|
37
|
+
# marking as consumed
|
38
|
+
#
|
39
|
+
# @note Please be aware, that offset metadata set this way will be passed to any marking
|
40
|
+
# as consumed even if it was not user initiated. For example in the DLQ flow.
|
41
|
+
def store_offset_metadata(offset_metadata)
|
42
|
+
@_current_offset_metadata = offset_metadata
|
43
|
+
end
|
44
|
+
|
30
45
|
# Marks message as consumed in an async way.
|
31
46
|
#
|
32
47
|
# @param message [Messages::Message] last successfully processed message.
|
@@ -38,7 +53,7 @@ module Karafka
|
|
38
53
|
# processing another message. In case like this we do not pause on the message we've
|
39
54
|
# already processed but rather at the next one. This applies to both sync and async
|
40
55
|
# versions of this method.
|
41
|
-
def mark_as_consumed(message, offset_metadata =
|
56
|
+
def mark_as_consumed(message, offset_metadata = @_current_offset_metadata)
|
42
57
|
if @_in_transaction
|
43
58
|
mark_in_transaction(message, offset_metadata, true)
|
44
59
|
else
|
@@ -54,6 +69,8 @@ module Karafka
|
|
54
69
|
end
|
55
70
|
|
56
71
|
true
|
72
|
+
ensure
|
73
|
+
@_current_offset_metadata = nil
|
57
74
|
end
|
58
75
|
|
59
76
|
# Marks message as consumed in a sync way.
|
@@ -62,7 +79,7 @@ module Karafka
|
|
62
79
|
# @param offset_metadata [String, nil] offset metadata string or nil if nothing
|
63
80
|
# @return [Boolean] true if we were able to mark the offset, false otherwise.
|
64
81
|
# False indicates that we were not able and that we have lost the partition.
|
65
|
-
def mark_as_consumed!(message, offset_metadata =
|
82
|
+
def mark_as_consumed!(message, offset_metadata = @_current_offset_metadata)
|
66
83
|
if @_in_transaction
|
67
84
|
mark_in_transaction(message, offset_metadata, false)
|
68
85
|
else
|
@@ -79,6 +96,8 @@ module Karafka
|
|
79
96
|
end
|
80
97
|
|
81
98
|
true
|
99
|
+
ensure
|
100
|
+
@_current_offset_metadata = nil
|
82
101
|
end
|
83
102
|
|
84
103
|
# Starts producer transaction, saves the transaction context for transactional marking
|
@@ -134,6 +153,7 @@ module Karafka
|
|
134
153
|
# transaction state synchronization usage as within transaction it is always sync)
|
135
154
|
def mark_in_transaction(message, offset_metadata, async)
|
136
155
|
raise Errors::TransactionRequiredError unless @_in_transaction
|
156
|
+
raise Errors::AssignmentLostError if revoked?
|
137
157
|
|
138
158
|
producer.transaction_mark_as_consumed(
|
139
159
|
client,
|
@@ -35,7 +35,7 @@ module Karafka
|
|
35
35
|
# @see `Strategies::Default#mark_as_consumed` for more details
|
36
36
|
# @param message [Messages::Message]
|
37
37
|
# @param offset_metadata [String, nil]
|
38
|
-
def mark_as_consumed(message, offset_metadata =
|
38
|
+
def mark_as_consumed(message, offset_metadata = @_current_offset_metadata)
|
39
39
|
return super unless retrying?
|
40
40
|
return super unless topic.dead_letter_queue.independent?
|
41
41
|
return false unless super
|
@@ -43,6 +43,8 @@ module Karafka
|
|
43
43
|
coordinator.pause_tracker.reset
|
44
44
|
|
45
45
|
true
|
46
|
+
ensure
|
47
|
+
@_current_offset_metadata = nil
|
46
48
|
end
|
47
49
|
|
48
50
|
# Override of the standard `#mark_as_consumed!`. Resets the pause tracker count in case
|
@@ -51,7 +53,7 @@ module Karafka
|
|
51
53
|
# @see `Strategies::Default#mark_as_consumed!` for more details
|
52
54
|
# @param message [Messages::Message]
|
53
55
|
# @param offset_metadata [String, nil]
|
54
|
-
def mark_as_consumed!(message, offset_metadata =
|
56
|
+
def mark_as_consumed!(message, offset_metadata = @_current_offset_metadata)
|
55
57
|
return super unless retrying?
|
56
58
|
return super unless topic.dead_letter_queue.independent?
|
57
59
|
return false unless super
|
@@ -59,6 +61,8 @@ module Karafka
|
|
59
61
|
coordinator.pause_tracker.reset
|
60
62
|
|
61
63
|
true
|
64
|
+
ensure
|
65
|
+
@_current_offset_metadata = nil
|
62
66
|
end
|
63
67
|
|
64
68
|
# When we encounter non-recoverable message, we skip it and go on with our lives
|
@@ -33,7 +33,7 @@ module Karafka
|
|
33
33
|
# @note This virtual offset management uses a regular default marking API underneath.
|
34
34
|
# We do not alter the "real" marking API, as VPs are just one of many cases we want
|
35
35
|
# to support and we do not want to impact them with collective offsets management
|
36
|
-
def mark_as_consumed(message, offset_metadata =
|
36
|
+
def mark_as_consumed(message, offset_metadata = @_current_offset_metadata)
|
37
37
|
if @_in_transaction && !collapsed?
|
38
38
|
mark_in_transaction(message, offset_metadata, true)
|
39
39
|
elsif collapsed?
|
@@ -55,11 +55,13 @@ module Karafka
|
|
55
55
|
manager.markable? ? super(*manager.markable) : revoked?
|
56
56
|
end
|
57
57
|
end
|
58
|
+
ensure
|
59
|
+
@_current_offset_metadata = nil
|
58
60
|
end
|
59
61
|
|
60
62
|
# @param message [Karafka::Messages::Message] blocking marks message as consumed
|
61
63
|
# @param offset_metadata [String, nil]
|
62
|
-
def mark_as_consumed!(message, offset_metadata =
|
64
|
+
def mark_as_consumed!(message, offset_metadata = @_current_offset_metadata)
|
63
65
|
if @_in_transaction && !collapsed?
|
64
66
|
mark_in_transaction(message, offset_metadata, false)
|
65
67
|
elsif collapsed?
|
@@ -73,6 +75,8 @@ module Karafka
|
|
73
75
|
manager.markable? ? super(*manager.markable) : revoked?
|
74
76
|
end
|
75
77
|
end
|
78
|
+
ensure
|
79
|
+
@_current_offset_metadata = nil
|
76
80
|
end
|
77
81
|
|
78
82
|
# Stores the next offset for processing inside of the transaction when collapsed and
|
@@ -93,6 +97,8 @@ module Karafka
|
|
93
97
|
# transaction state synchronization usage as within transaction it is always sync)
|
94
98
|
def mark_in_transaction(message, offset_metadata, async)
|
95
99
|
raise Errors::TransactionRequiredError unless @_in_transaction
|
100
|
+
# Prevent from attempts of offset storage when we no longer own the assignment
|
101
|
+
raise Errors::AssignmentLostError if revoked?
|
96
102
|
|
97
103
|
return super if collapsed?
|
98
104
|
|
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.3.0.
|
4
|
+
version: 2.3.0.alpha2
|
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-01-
|
38
|
+
date: 2024-01-17 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: karafka-core
|
metadata.gz.sig
CHANGED
Binary file
|