racecar 2.12.0 → 2.13.0
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/CODEOWNERS +4 -0
- data/.github/workflows/ci.yml +2 -2
- data/.github/workflows/publish.yml +21 -7
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +2 -2
- data/README.md +18 -1
- data/Rakefile +1 -3
- data/lib/racecar/config.rb +3 -0
- data/lib/racecar/consumer_set.rb +4 -2
- data/lib/racecar/runner.rb +43 -3
- data/lib/racecar/version.rb +1 -1
- metadata +4 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 994ab85c49335ccb0a2aee9d7f0c43f1bdc4ee7cd48e19cc01f2eba884f8ec1d
|
|
4
|
+
data.tar.gz: 680ccd2f1adaa3cfa46ca66086234667959b916da2e1c4f8748d2a47b82454f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1be971cb7d6cb591e0b3961e680057c49768dcf10dded68d4ec293e07ba32ad432d2ba2d3e4a57012a7f37831be655422fcad330efe5394e1a58bc56929fca59
|
|
7
|
+
data.tar.gz: 5a2b04c4cf2783f60b52acf38f0ed8e06a80a6b057d58d10d3419c84281edc719435fe6f420587bf358012adb917aa5e970b71d2d39645194bb1d024f0f39267
|
data/.github/CODEOWNERS
ADDED
data/.github/workflows/ci.yml
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
name: Publish
|
|
1
|
+
name: Publish to RubyGems.org
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
|
|
5
|
+
branches: main
|
|
6
|
+
paths: lib/racecar/version.rb
|
|
7
|
+
workflow_dispatch:
|
|
6
8
|
|
|
7
9
|
jobs:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
publish:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment: rubygems-publish
|
|
13
|
+
if: github.repository_owner == 'zendesk'
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write
|
|
16
|
+
contents: write
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- name: Set up Ruby
|
|
20
|
+
uses: ruby/setup-ruby@v1
|
|
21
|
+
with:
|
|
22
|
+
bundler-cache: false
|
|
23
|
+
ruby-version: "3.4"
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: bundle install
|
|
26
|
+
- uses: rubygems/release-gem@v1
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 2.13.0
|
|
6
|
+
|
|
7
|
+
* Fix `ConsumerSet#pause` raising `NameError` when `OpenStruct` is not loaded by
|
|
8
|
+
replacing the seek message with a `Struct`.
|
|
9
|
+
* Commit stored offsets on a timer while idle to refresh their retention, so an
|
|
10
|
+
idle consumer group doesn't lose its position in the topics after
|
|
11
|
+
`offsets.retention.ms` expires. Enabled by default; set
|
|
12
|
+
`offset_commit_on_idle=false` to disable.
|
|
13
|
+
|
|
5
14
|
## 2.12.0
|
|
6
15
|
|
|
7
16
|
* Add tests against Ruby 3.4
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
racecar (2.
|
|
4
|
+
racecar (2.13.0)
|
|
5
5
|
king_konf (~> 1.0.0)
|
|
6
6
|
rdkafka (>= 0.15.0)
|
|
7
7
|
|
|
@@ -45,7 +45,7 @@ GEM
|
|
|
45
45
|
byebug (~> 11.0)
|
|
46
46
|
pry (>= 0.13, < 0.15)
|
|
47
47
|
rake (13.0.6)
|
|
48
|
-
rdkafka (0.
|
|
48
|
+
rdkafka (0.21.0)
|
|
49
49
|
ffi (~> 1.15)
|
|
50
50
|
mini_portile2 (~> 2.6)
|
|
51
51
|
rake (> 12)
|
data/README.md
CHANGED
|
@@ -349,6 +349,7 @@ end
|
|
|
349
349
|
The consumers will checkpoint their positions from time to time in order to be able to recover from failures. This is called _committing offsets_, since it's done by tracking the offset reached in each partition being processed, and committing those offset numbers to the Kafka offset storage API. If you can tolerate more double-processing after a failure, you can increase the interval between commits in order to better performance. You can also do the opposite if you prefer less chance of double-processing.
|
|
350
350
|
|
|
351
351
|
- `offset_commit_interval` – How often to save the consumer's position in Kafka. Default is every 10 seconds.
|
|
352
|
+
- `offset_commit_on_idle` – Whether to keep committing the stored position on a timer even when no messages are being processed. Kafka retains committed offsets for only a finite period (`offsets.retention.ms`); without these keep-alive commits, an idle consumer group can lose its position and fall back to `auto.offset.reset` on the next restart, causing reprocessing or skipped messages. Default is `true`.
|
|
352
353
|
|
|
353
354
|
#### Timeouts & intervals
|
|
354
355
|
|
|
@@ -418,7 +419,7 @@ Racecar supports [Datadog](https://www.datadoghq.com/) monitoring integration. I
|
|
|
418
419
|
- `datadog_namespace` – The namespace to use for Datadog metrics.
|
|
419
420
|
- `datadog_tags` – Tags that should always be set on Datadog metrics.
|
|
420
421
|
|
|
421
|
-
Furthermore, there's a [standard Datadog dashboard configuration file](https://raw.githubusercontent.com/zendesk/racecar/
|
|
422
|
+
Furthermore, there's a [standard Datadog dashboard configuration file](https://raw.githubusercontent.com/zendesk/racecar/main/extra/datadog-dashboard.json) that you can import to get started with a Racecar dashboard for all of your consumers.
|
|
422
423
|
|
|
423
424
|
#### Consumers Without Rails
|
|
424
425
|
|
|
@@ -729,6 +730,22 @@ There can be behavioural inconsistencies between running the specs on your machi
|
|
|
729
730
|
|
|
730
731
|
Please note - your code directory is mounted as a volume, so you can make code changes without needing to rebuild
|
|
731
732
|
|
|
733
|
+
### Releasing a new version
|
|
734
|
+
|
|
735
|
+
A new version is published to RubyGems.org every time a change to `version.rb` is pushed to the `main` branch.
|
|
736
|
+
In short, follow these steps:
|
|
737
|
+
1. Update `version.rb`,
|
|
738
|
+
2. run `bundle lock` to update `Gemfile.lock`,
|
|
739
|
+
3. merge this change into `main`, and
|
|
740
|
+
4. look at [the action](https://github.com/zendesk/racecar/actions/workflows/publish.yml) for output.
|
|
741
|
+
|
|
742
|
+
To create a pre-release from a non-main branch:
|
|
743
|
+
1. change the version in `version.rb` to something like `2.13.0.pre.1` or `3.0.0.beta.2`,
|
|
744
|
+
2. push this change to your branch,
|
|
745
|
+
3. go to [Actions → “Publish to RubyGems.org” on GitHub](https://github.com/zendesk/racecar/actions/workflows/publish.yml),
|
|
746
|
+
4. click the “Run workflow” button,
|
|
747
|
+
5. pick your branch from a dropdown.
|
|
748
|
+
|
|
732
749
|
## Contributing
|
|
733
750
|
|
|
734
751
|
Bug reports and pull requests are welcome on [GitHub](https://github.com/zendesk/racecar). Feel free to [join our Slack team](https://ruby-kafka-slack.herokuapp.com/) and ask how best to contribute!
|
data/Rakefile
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "bundler/setup"
|
|
3
4
|
require "bundler/gem_tasks"
|
|
4
5
|
require "rspec/core/rake_task"
|
|
5
6
|
|
|
6
|
-
# Pushing to rubygems is handled by a github workflow
|
|
7
|
-
ENV["gem_push"] = "false"
|
|
8
|
-
|
|
9
7
|
RSpec::Core::RakeTask.new(:spec)
|
|
10
8
|
|
|
11
9
|
task :default => :spec
|
data/lib/racecar/config.rb
CHANGED
|
@@ -23,6 +23,9 @@ module Racecar
|
|
|
23
23
|
desc "How frequently to commit offset positions"
|
|
24
24
|
float :offset_commit_interval, default: 10
|
|
25
25
|
|
|
26
|
+
desc "Commit stored offsets on a timer even when no messages have been processed, refreshing their retention so an idle consumer group doesn't lose its position"
|
|
27
|
+
boolean :offset_commit_on_idle, default: true
|
|
28
|
+
|
|
26
29
|
desc "How often to send a heartbeat message to Kafka"
|
|
27
30
|
float :heartbeat_interval, default: 10
|
|
28
31
|
|
data/lib/racecar/consumer_set.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Racecar
|
|
4
4
|
class ConsumerSet
|
|
5
5
|
MAX_POLL_TRIES = 10
|
|
6
|
+
SeekMessage = Struct.new(:topic, :partition, :offset, keyword_init: true)
|
|
7
|
+
private_constant :SeekMessage
|
|
6
8
|
|
|
7
9
|
def initialize(config, logger, instrumenter = NullInstrumenter)
|
|
8
10
|
@config, @logger = config, logger
|
|
@@ -100,8 +102,8 @@ module Racecar
|
|
|
100
102
|
end
|
|
101
103
|
|
|
102
104
|
consumer.pause(filtered_tpl)
|
|
103
|
-
|
|
104
|
-
consumer.seek(
|
|
105
|
+
seek_message = SeekMessage.new(topic: topic, partition: partition, offset: offset)
|
|
106
|
+
consumer.seek(seek_message)
|
|
105
107
|
|
|
106
108
|
@paused_tpls[topic][partition] = [consumer, filtered_tpl]
|
|
107
109
|
end
|
data/lib/racecar/runner.rb
CHANGED
|
@@ -49,6 +49,7 @@ module Racecar
|
|
|
49
49
|
def run
|
|
50
50
|
install_signal_handlers
|
|
51
51
|
@stop_requested = false
|
|
52
|
+
@last_keep_alive_commit_at = monotonic_time
|
|
52
53
|
|
|
53
54
|
# Configure the consumer with a producer so it can produce messages and
|
|
54
55
|
# with a consumer so that it can support advanced use-cases.
|
|
@@ -68,18 +69,22 @@ module Racecar
|
|
|
68
69
|
loop do
|
|
69
70
|
break if @stop_requested
|
|
70
71
|
resume_paused_partitions
|
|
72
|
+
@offset_advanced_this_loop = false
|
|
71
73
|
|
|
72
74
|
@instrumenter.instrument("start_main_loop", instrumentation_payload)
|
|
73
75
|
@instrumenter.instrument("main_loop", instrumentation_payload) do
|
|
74
76
|
case process_method
|
|
75
77
|
when :batch then
|
|
76
|
-
|
|
77
|
-
msg_per_part
|
|
78
|
-
|
|
78
|
+
messages = consumer.batch_poll(config.max_wait_time_ms)
|
|
79
|
+
msg_per_part = messages.group_by(&:partition)
|
|
80
|
+
msg_per_part.each_value do |batch|
|
|
81
|
+
process_batch(batch)
|
|
79
82
|
end
|
|
83
|
+
maybe_keep_alive_commit(!@offset_advanced_this_loop)
|
|
80
84
|
when :single then
|
|
81
85
|
message = consumer.poll(config.max_wait_time_ms)
|
|
82
86
|
process(message) if message
|
|
87
|
+
maybe_keep_alive_commit(!@offset_advanced_this_loop)
|
|
83
88
|
end
|
|
84
89
|
end
|
|
85
90
|
end
|
|
@@ -185,6 +190,7 @@ module Racecar
|
|
|
185
190
|
processor.process(Racecar::Message.new(message, retries_count: pause.pauses_count))
|
|
186
191
|
processor.deliver!
|
|
187
192
|
consumer.store_offset(message)
|
|
193
|
+
@offset_advanced_this_loop = true
|
|
188
194
|
end
|
|
189
195
|
rescue => e
|
|
190
196
|
instrumentation_payload[:unrecoverable_delivery_error] = reset_producer_on_unrecoverable_delivery_errors(e)
|
|
@@ -217,6 +223,7 @@ module Racecar
|
|
|
217
223
|
processor.process_batch(racecar_messages)
|
|
218
224
|
processor.deliver!
|
|
219
225
|
consumer.store_offset(messages.last)
|
|
226
|
+
@offset_advanced_this_loop = true
|
|
220
227
|
end
|
|
221
228
|
rescue => e
|
|
222
229
|
instrumentation_payload[:unrecoverable_delivery_error] = reset_producer_on_unrecoverable_delivery_errors(e)
|
|
@@ -289,5 +296,38 @@ module Racecar
|
|
|
289
296
|
end
|
|
290
297
|
end
|
|
291
298
|
end
|
|
299
|
+
|
|
300
|
+
# librdkafka's auto-commit only writes an OffsetCommit when the stored
|
|
301
|
+
# offset has changed, so a consumer that never advances its stored offset
|
|
302
|
+
# -- whether idle or stuck on failing messages -- never commits. Kafka
|
|
303
|
+
# retains committed offsets for only a finite period (offsets.retention.ms,
|
|
304
|
+
# default 7 days); once they expire the group loses its position and a
|
|
305
|
+
# restart falls back to auto.offset.reset, causing reprocessing or skipped
|
|
306
|
+
# messages. Re-committing the already stored position on a timer refreshes
|
|
307
|
+
# its retention. This only fires when the stored offset wasn't advanced
|
|
308
|
+
# this iteration, so it never duplicates the normal commit path, and it
|
|
309
|
+
# never advances past a stored offset, so at-least-once semantics are
|
|
310
|
+
# unchanged.
|
|
311
|
+
def maybe_keep_alive_commit(offset_stale)
|
|
312
|
+
return unless offset_stale
|
|
313
|
+
return unless config.offset_commit_on_idle
|
|
314
|
+
return if monotonic_time - @last_keep_alive_commit_at < config.offset_commit_interval
|
|
315
|
+
|
|
316
|
+
consumer.commit
|
|
317
|
+
@last_keep_alive_commit_at = monotonic_time
|
|
318
|
+
rescue Rdkafka::RdkafkaError => e
|
|
319
|
+
# Retry on the next idle iteration (the timer isn't advanced on failure),
|
|
320
|
+
# but throttle the warning so a persistent failure doesn't spam the log
|
|
321
|
+
# every iteration.
|
|
322
|
+
now = monotonic_time
|
|
323
|
+
unless @last_keep_alive_commit_error_at && now - @last_keep_alive_commit_error_at < config.offset_commit_interval
|
|
324
|
+
@last_keep_alive_commit_error_at = now
|
|
325
|
+
logger.warn "Keep-alive offset commit failed: #{e}"
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def monotonic_time
|
|
330
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
331
|
+
end
|
|
292
332
|
end
|
|
293
333
|
end
|
data/lib/racecar/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: racecar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Schierbeck
|
|
8
8
|
- Benjamin Quorning
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: exe
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
14
|
name: king_konf
|
|
@@ -149,7 +148,6 @@ dependencies:
|
|
|
149
148
|
- - ">="
|
|
150
149
|
- !ruby/object:Gem::Version
|
|
151
150
|
version: '0'
|
|
152
|
-
description:
|
|
153
151
|
email:
|
|
154
152
|
- dschierbeck@zendesk.com
|
|
155
153
|
- bquorning@zendesk.com
|
|
@@ -159,6 +157,7 @@ executables:
|
|
|
159
157
|
extensions: []
|
|
160
158
|
extra_rdoc_files: []
|
|
161
159
|
files:
|
|
160
|
+
- ".github/CODEOWNERS"
|
|
162
161
|
- ".github/dependabot.yml"
|
|
163
162
|
- ".github/workflows/ci.yml"
|
|
164
163
|
- ".github/workflows/publish.yml"
|
|
@@ -214,7 +213,6 @@ homepage: https://github.com/zendesk/racecar
|
|
|
214
213
|
licenses:
|
|
215
214
|
- Apache License Version 2.0
|
|
216
215
|
metadata: {}
|
|
217
|
-
post_install_message:
|
|
218
216
|
rdoc_options: []
|
|
219
217
|
require_paths:
|
|
220
218
|
- lib
|
|
@@ -229,8 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
229
227
|
- !ruby/object:Gem::Version
|
|
230
228
|
version: '0'
|
|
231
229
|
requirements: []
|
|
232
|
-
rubygems_version: 3.
|
|
233
|
-
signing_key:
|
|
230
|
+
rubygems_version: 3.6.9
|
|
234
231
|
specification_version: 4
|
|
235
232
|
summary: A framework for running Kafka consumers
|
|
236
233
|
test_files: []
|