waterdrop 2.6.14 → 2.7.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24dc1ffc8d6298980ec8f0c302141a643196acbf62da017cf03ec6675552532c
4
- data.tar.gz: '08b66f7abb7f2e04fb9a9b5da566f5cefb5a00769ef14c14898f86c07003f81e'
3
+ metadata.gz: 89426650b3e24dded080b6a0ebb1b9db7f23fd289ced679fcba3527f8c06a06b
4
+ data.tar.gz: 8163dc40307dae99c2409047a237d58bf1b83923d9d2739de3db4aa7839e5c86
5
5
  SHA512:
6
- metadata.gz: 65d1a0b2ce58fa07edfa96dffaa97773424a53787ee05ba981ca1ed9e4edb91d7f9b25e4dbc5e054ac68abf288dd5e1a879cb09ee13fcacc9422d9a341f4bd81
7
- data.tar.gz: 2c7ce204ab9c9af43c5e916143aa5375e45157f1fd9c34acf69dc3191a646c3d41c230af82f6d1b8047836f2b7d4437d9a2cf3c173cd8beb9a7c1948c652cd54
6
+ metadata.gz: 716da4bb23487cf445dd2f43d3b7a6a69046e87d22c39beb5612f2667130efaed2e8a7b5c49a98f85df3c4cc7673065c87023e0d0b90618f869863dc6b015818
7
+ data.tar.gz: 1c9f6a97e3675570db5f4e2c258e82286d5ec2417ad172f9bc9d1698a2b88564446f2d5c412cee2205b2e91ca9b08144d210a7d0014e7609aa9b38002cff47ae
checksums.yaml.gz.sig CHANGED
Binary file
@@ -22,7 +22,6 @@ jobs:
22
22
  - '3.2'
23
23
  - '3.1'
24
24
  - '3.0'
25
- - '2.7'
26
25
  include:
27
26
  - ruby: '3.3'
28
27
  coverage: 'true'
@@ -49,25 +48,15 @@ jobs:
49
48
 
50
49
  - name: Install latest bundler
51
50
  run: |
52
- if [[ "$(ruby -v | awk '{print $2}')" == 2.7.8* ]]; then
53
- gem install bundler -v 2.4.22 --no-document
54
- gem update --system 3.4.22 --no-document
55
- else
56
- gem install bundler --no-document
57
- gem update --system --no-document
58
- fi
51
+ gem install bundler --no-document
52
+ gem update --system --no-document
59
53
 
60
54
  bundle config set without 'tools benchmarks docs'
61
55
 
62
56
  - name: Bundle install
63
57
  run: |
64
58
  bundle config set without development
65
-
66
- if [[ "$(ruby -v | awk '{print $2}')" == 2.7.8* ]]; then
67
- BUNDLER_VERSION=2.4.22 bundle install --jobs 4 --retry 3
68
- else
69
- bundle install --jobs 4 --retry 3
70
- fi
59
+ bundle install --jobs 4 --retry 3
71
60
 
72
61
  - name: Run all tests
73
62
  env:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,85 @@
1
1
  # WaterDrop changelog
2
2
 
3
+ ## 2.7.0 (Unreleased)
4
+
5
+ This release contains **BREAKING** changes. Make sure to read and apply upgrade notes.
6
+
7
+ - **[Breaking]** Drop Ruby `2.7` support.
8
+ - **[Breaking]** Change default timeouts so final delivery `message.timeout.ms` is less that `max_wait_time` so we do not end up with not final verdict.
9
+ - **[Breaking]** Update all the time related configuration settings to be in `ms` and not mixed.
10
+ - **[Breaking]** Remove no longer needed `wait_timeout` configuration option.
11
+ - [Enhancement] Introduce `instrument_on_wait_queue_full` flag (defaults to `true`) to be able to configure whether non critical (retryable) queue full errors should be instrumented in the error pipeline. Useful when building high-performance pipes with WaterDrop queue retry backoff as a throttler.
12
+
13
+ ### Upgrade Notes
14
+
15
+ **PLEASE MAKE SURE TO READ AND APPLY THEM!**
16
+
17
+ #### Time Settings Format Alignment
18
+
19
+ **All** time-related values are now configured in milliseconds instead of some being in seconds and some in milliseconds.
20
+
21
+ The values that were changed from seconds to milliseconds are:
22
+
23
+ - `max_wait_timeout`
24
+ - `wait_timeout`
25
+ - `wait_backoff_on_queue_full`
26
+ - `wait_timeout_on_queue_full`
27
+ - `wait_backoff_on_transaction_command, default`
28
+
29
+ If you have configured any of those yourself, please replace the seconds representation with milliseconds:
30
+
31
+ ```ruby
32
+ producer = WaterDrop::Producer.new
33
+
34
+ producer.setup do |config|
35
+ config.deliver = true
36
+
37
+ # Replace this:
38
+ config.wait_timeout = 30
39
+
40
+ # With
41
+ config.wait_timeout = 30_000
42
+ # ...
43
+ end
44
+ ```
45
+
46
+ #### Defaults Alignment
47
+
48
+ In this release, we've updated our default settings to address a crucial issue: previous defaults could lead to inconclusive outcomes in synchronous operations due to wait timeout errors. Users often mistakenly believed that a message dispatch was halted because of these errors when, in fact, the timeout was related to awaiting the final dispatch verdict, not the dispatch action itself.
49
+
50
+ The new defaults in WaterDrop 2.7.0 eliminate this confusion by ensuring synchronous operation results are always transparent and conclusive. This change aims to provide a straightforward understanding of wait timeout errors, reinforcing that they reflect the wait state, not the dispatch success.
51
+
52
+ Below, you can find a table with what has changed, the new defaults, and the current ones in case you want to retain the previous behavior:
53
+
54
+ <table>
55
+ <thead>
56
+ <tr>
57
+ <th>Config</th>
58
+ <th>Previous Default</th>
59
+ <th>New Default</th>
60
+ </tr>
61
+ </thead>
62
+ <tbody>
63
+ <tr>
64
+ <td>root <code>max_wait_timeout</code></td>
65
+ <td>5000 ms (5 seconds)</td>
66
+ <td>60000 ms (60 seconds)</td>
67
+ </tr>
68
+ <tr>
69
+ <td>kafka <code>message.timeout.ms</code></td>
70
+ <td>300000 ms (5 minutes)</td>
71
+ <td>55000 ms (55 seconds)</td>
72
+ </tr>
73
+ <tr>
74
+ <td>kafka <code>transaction.timeout.ms</code></td>
75
+ <td>60000 ms (1 minute)</td>
76
+ <td>45000 ms (45 seconds)</td>
77
+ </tr>
78
+ </tbody>
79
+ </table>
80
+
81
+ This alignment ensures that when using sync operations or invoking `#wait`, any exception you get should give you a conclusive and final delivery verdict.
82
+
3
83
  ## 2.6.14 (2024-02-06)
4
84
  - [Enhancement] Instrument `producer.connected` and `producer.closing` lifecycle events.
5
85
 
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- waterdrop (2.6.14)
5
- karafka-core (>= 2.2.3, < 3.0.0)
4
+ waterdrop (2.7.0.alpha2)
5
+ karafka-core (>= 2.4.0.alpha1, < 3.0.0)
6
6
  zeitwerk (~> 2.3)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activesupport (7.1.3)
11
+ activesupport (7.1.3.2)
12
12
  base64
13
13
  bigdecimal
14
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -25,22 +25,20 @@ GEM
25
25
  connection_pool (2.4.1)
26
26
  diff-lcs (1.5.1)
27
27
  docile (1.4.0)
28
- drb (2.2.0)
29
- ruby2_keywords
30
- factory_bot (6.4.5)
28
+ drb (2.2.1)
29
+ factory_bot (6.4.6)
31
30
  activesupport (>= 5.0.0)
32
31
  ffi (1.16.3)
33
- i18n (1.14.1)
32
+ i18n (1.14.4)
34
33
  concurrent-ruby (~> 1.0)
35
- karafka-core (2.2.7)
36
- concurrent-ruby (>= 1.1)
37
- karafka-rdkafka (>= 0.13.9, < 0.15.0)
38
- karafka-rdkafka (0.14.7)
34
+ karafka-core (2.4.0.alpha1)
35
+ karafka-rdkafka (>= 0.15.0.alpha1, < 0.16.0)
36
+ karafka-rdkafka (0.15.0.alpha1)
39
37
  ffi (~> 1.15)
40
38
  mini_portile2 (~> 2.6)
41
39
  rake (> 12)
42
40
  mini_portile2 (2.8.5)
43
- minitest (5.21.2)
41
+ minitest (5.22.2)
44
42
  mutex_m (0.2.0)
45
43
  rake (13.1.0)
46
44
  rspec (3.13.0)
@@ -55,8 +53,7 @@ GEM
55
53
  rspec-mocks (3.13.0)
56
54
  diff-lcs (>= 1.2.0, < 2.0)
57
55
  rspec-support (~> 3.13.0)
58
- rspec-support (3.13.0)
59
- ruby2_keywords (0.0.5)
56
+ rspec-support (3.13.1)
60
57
  simplecov (0.22.0)
61
58
  docile (~> 1.1)
62
59
  simplecov-html (~> 0.11)
@@ -65,10 +62,10 @@ GEM
65
62
  simplecov_json_formatter (0.1.4)
66
63
  tzinfo (2.0.6)
67
64
  concurrent-ruby (~> 1.0)
68
- zeitwerk (2.6.12)
65
+ zeitwerk (2.6.13)
69
66
 
70
67
  PLATFORMS
71
- ruby
68
+ arm64-darwin-22
72
69
  x86_64-linux
73
70
 
74
71
  DEPENDENCIES
@@ -6,7 +6,6 @@ en:
6
6
  deliver_format: must be boolean
7
7
  id_format: must be a non-empty string
8
8
  max_payload_size_format: must be an integer that is equal or bigger than 1
9
- wait_timeout_format: must be a numeric that is bigger than 0
10
9
  max_wait_timeout_format: must be an integer that is equal or bigger than 0
11
10
  kafka_format: must be a hash with symbol based keys
12
11
  kafka_key_must_be_a_symbol: All keys under the kafka settings scope need to be symbols
data/docker-compose.yml CHANGED
@@ -3,7 +3,7 @@ version: '2'
3
3
  services:
4
4
  kafka:
5
5
  container_name: kafka
6
- image: confluentinc/cp-kafka:7.5.3
6
+ image: confluentinc/cp-kafka:7.6.0
7
7
 
8
8
  ports:
9
9
  - 9092:9092
@@ -12,7 +12,12 @@ module WaterDrop
12
12
  'client.id': 'waterdrop',
13
13
  # emit librdkafka statistics every five seconds. This is used in instrumentation.
14
14
  # When disabled, part of metrics will not be published and available.
15
- 'statistics.interval.ms': 5_000
15
+ 'statistics.interval.ms': 5_000,
16
+ # We set it to a value that is lower than `max_wait_time` to have a final verdict upon sync
17
+ # delivery
18
+ 'message.timeout.ms': 55_000,
19
+ # Must be less or equal to `message.timeout.ms` defaults
20
+ 'transaction.timeout.ms': 45_000
16
21
  }.freeze
17
22
 
18
23
  private_constant :KAFKA_DEFAULTS
@@ -44,12 +49,8 @@ module WaterDrop
44
49
  # option [Integer] max payload size allowed for delivery to Kafka
45
50
  setting :max_payload_size, default: 1_000_012
46
51
  # option [Integer] Wait that long for the delivery report or raise an error if this takes
47
- # longer than the timeout.
48
- setting :max_wait_timeout, default: 5
49
- # option [Numeric] how long should we wait between re-checks on the availability of the
50
- # delivery report. In a really robust systems, this describes the min-delivery time
51
- # for a single sync message when produced in isolation
52
- setting :wait_timeout, default: 0.005 # 5 milliseconds
52
+ # longer than the timeout ms.
53
+ setting :max_wait_timeout, default: 60_000
53
54
  # option [Boolean] should we upon detecting full librdkafka queue backoff and retry or should
54
55
  # we raise an exception.
55
56
  # When this is set to `true`, upon full queue, we won't raise an error. There will be error
@@ -60,12 +61,14 @@ module WaterDrop
60
61
  # option [Integer] how long (in seconds) should we backoff before a retry when queue is full
61
62
  # The retry will happen with the same message and backoff should give us some time to
62
63
  # dispatch previously buffered messages.
63
- setting :wait_backoff_on_queue_full, default: 0.1
64
- # option [Numeric] how many seconds should we wait with the backoff on queue having space for
64
+ setting :wait_backoff_on_queue_full, default: 100
65
+ # option [Numeric] how many ms should we wait with the backoff on queue having space for
65
66
  # more messages before re-raising the error.
66
- setting :wait_timeout_on_queue_full, default: 10
67
+ setting :wait_timeout_on_queue_full, default: 10_000
68
+ # option [Boolean] should we instrument non-critical, retryable queue full errors
69
+ setting :instrument_on_wait_queue_full, default: true
67
70
  # option [Numeric] How long to wait before retrying a retryable transaction related error
68
- setting :wait_backoff_on_transaction_command, default: 0.5
71
+ setting :wait_backoff_on_transaction_command, default: 500
69
72
  # option [Numeric] How many times to retry a retryable transaction related error before
70
73
  # giving up
71
74
  setting :max_attempts_on_transaction_command, default: 5
@@ -17,7 +17,6 @@ module WaterDrop
17
17
  required(:deliver) { |val| [true, false].include?(val) }
18
18
  required(:max_payload_size) { |val| val.is_a?(Integer) && val >= 1 }
19
19
  required(:max_wait_timeout) { |val| val.is_a?(Numeric) && val >= 0 }
20
- required(:wait_timeout) { |val| val.is_a?(Numeric) && val.positive? }
21
20
  required(:kafka) { |val| val.is_a?(Hash) && !val.empty? }
22
21
  required(:wait_on_queue_full) { |val| [true, false].include?(val) }
23
22
  required(:wait_backoff_on_queue_full) { |val| val.is_a?(Numeric) && val >= 0 }
@@ -52,8 +52,8 @@ module WaterDrop
52
52
  # @return [Array<Rdkafka::Producer::DeliveryReport>] delivery reports
53
53
  #
54
54
  # @raise [Rdkafka::RdkafkaError] When adding the messages to rdkafka's queue failed
55
- # @raise [Rdkafka::Producer::WaitTimeoutError] When the timeout has been reached and the
56
- # some handles are still pending
55
+ # @raise [Rdkafka::Producer::WaitTimeoutError] When the timeout has been reached and some
56
+ # handles are still pending
57
57
  # @raise [Errors::MessageInvalidError] When any of the provided messages details are invalid
58
58
  # and the message could not be sent to Kafka
59
59
  def produce_many_sync(messages)
@@ -132,8 +132,7 @@ module WaterDrop
132
132
  client.send_offsets_to_transaction(
133
133
  consumer,
134
134
  tpl,
135
- # This setting is at the moment in seconds and we require ms
136
- @config.max_wait_timeout * 1_000
135
+ @config.max_wait_timeout
137
136
  )
138
137
  end
139
138
  end
@@ -197,7 +196,7 @@ module WaterDrop
197
196
 
198
197
  if do_retry
199
198
  # Backoff more and more before retries
200
- sleep(config.wait_backoff_on_transaction_command * attempt)
199
+ sleep((config.wait_backoff_on_transaction_command / 1_000.0) * attempt)
201
200
 
202
201
  retry
203
202
  end
@@ -188,8 +188,7 @@ module WaterDrop
188
188
  # The linger.ms time will be ignored for the duration of the call,
189
189
  # queued messages will be sent to the broker as soon as possible.
190
190
  begin
191
- # `max_wait_timeout` is in seconds at the moment
192
- @client.flush(@config.max_wait_timeout * 1_000) unless @client.closed?
191
+ @client.flush(@config.max_wait_timeout) unless @client.closed?
193
192
  # We can safely ignore timeouts here because any left outstanding requests
194
193
  # will anyhow force wait on close if not forced.
195
194
  # If forced, we will purge the queue and just close
@@ -250,8 +249,8 @@ module WaterDrop
250
249
  # @param handler [Rdkafka::Producer::DeliveryHandle]
251
250
  def wait(handler)
252
251
  handler.wait(
253
- max_wait_timeout: @config.max_wait_timeout,
254
- wait_timeout: @config.wait_timeout
252
+ # rdkafka max_wait_timeout is in seconds and we use ms
253
+ max_wait_timeout: @config.max_wait_timeout / 1_000.0
255
254
  )
256
255
  end
257
256
 
@@ -286,7 +285,7 @@ module WaterDrop
286
285
  # If we're running for longer than the timeout, we need to re-raise the queue full.
287
286
  # This will prevent from situation where cluster is down forever and we just retry and retry
288
287
  # in an infinite loop, effectively hanging the processing
289
- raise unless monotonic_now - produce_time < @config.wait_timeout_on_queue_full * 1_000
288
+ raise unless monotonic_now - produce_time < @config.wait_timeout_on_queue_full
290
289
 
291
290
  label = caller_locations(2, 1)[0].label.split(' ').last
292
291
 
@@ -297,22 +296,28 @@ module WaterDrop
297
296
  begin
298
297
  raise Errors::ProduceError, e.inspect
299
298
  rescue Errors::ProduceError => e
300
- # We want to instrument on this event even when we restart it.
301
- # The reason is simple: instrumentation and visibility.
302
- # We can recover from this, but despite that we should be able to instrument this.
303
- # If this type of event happens too often, it may indicate that the buffer settings are not
304
- # well configured.
305
- @monitor.instrument(
306
- 'error.occurred',
307
- producer_id: id,
308
- message: message,
309
- error: e,
310
- type: "message.#{label}"
311
- )
299
+ # Users can configure this because in pipe-like flows with high throughput, queue full with
300
+ # retry may be used as a throttling system that will backoff and wait.
301
+ # In such scenarios this error notification can be removed and until queue full is
302
+ # retryable, it will not be raised as an error.
303
+ if @config.instrument_on_wait_queue_full
304
+ # We want to instrument on this event even when we restart it.
305
+ # The reason is simple: instrumentation and visibility.
306
+ # We can recover from this, but despite that we should be able to instrument this.
307
+ # If this type of event happens too often, it may indicate that the buffer settings are
308
+ # not well configured.
309
+ @monitor.instrument(
310
+ 'error.occurred',
311
+ producer_id: id,
312
+ message: message,
313
+ error: e,
314
+ type: "message.#{label}"
315
+ )
316
+ end
312
317
 
313
318
  # We do not poll the producer because polling happens in a background thread
314
319
  # It also should not be a frequent case (queue full), hence it's ok to just throttle.
315
- sleep @config.wait_backoff_on_queue_full
320
+ sleep @config.wait_backoff_on_queue_full / 1_000.0
316
321
  end
317
322
 
318
323
  @operations_in_progress.decrement
@@ -3,5 +3,5 @@
3
3
  # WaterDrop library
4
4
  module WaterDrop
5
5
  # Current WaterDrop version
6
- VERSION = '2.6.14'
6
+ VERSION = '2.7.0.alpha2'
7
7
  end
data/waterdrop.gemspec CHANGED
@@ -16,9 +16,11 @@ Gem::Specification.new do |spec|
16
16
  spec.description = spec.summary
17
17
  spec.license = 'MIT'
18
18
 
19
- spec.add_dependency 'karafka-core', '>= 2.2.3', '< 3.0.0'
19
+ spec.add_dependency 'karafka-core', '>= 2.4.0.alpha1', '< 3.0.0'
20
20
  spec.add_dependency 'zeitwerk', '~> 2.3'
21
21
 
22
+ spec.required_ruby_version = '>= 3.0.0'
23
+
22
24
  if $PROGRAM_NAME.end_with?('gem')
23
25
  spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
24
26
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waterdrop
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.14
4
+ version: 2.7.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-02-06 00:00:00.000000000 Z
38
+ date: 2024-03-17 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: karafka-core
@@ -43,7 +43,7 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 2.2.3
46
+ version: 2.4.0.alpha1
47
47
  - - "<"
48
48
  - !ruby/object:Gem::Version
49
49
  version: 3.0.0
@@ -53,7 +53,7 @@ dependencies:
53
53
  requirements:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: 2.2.3
56
+ version: 2.4.0.alpha1
57
57
  - - "<"
58
58
  - !ruby/object:Gem::Version
59
59
  version: 3.0.0
@@ -144,7 +144,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
144
  requirements:
145
145
  - - ">="
146
146
  - !ruby/object:Gem::Version
147
- version: '0'
147
+ version: 3.0.0
148
148
  required_rubygems_version: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - ">="
metadata.gz.sig CHANGED
Binary file