racecar 2.9.0 → 2.12.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/workflows/ci.yml +17 -8
- data/.rspec +1 -0
- data/CHANGELOG.md +26 -2
- data/Dockerfile +1 -1
- data/Gemfile +1 -3
- data/Gemfile.lock +26 -14
- data/README.md +59 -20
- data/docker-compose.yml +3 -5
- data/lib/racecar/cli.rb +5 -4
- data/lib/racecar/config.rb +12 -4
- data/lib/racecar/consumer.rb +9 -5
- data/lib/racecar/consumer_set.rb +31 -6
- data/lib/racecar/ctl.rb +12 -2
- data/lib/racecar/datadog.rb +14 -1
- data/lib/racecar/producer.rb +1 -0
- data/lib/racecar/rails_config_file_loader.rb +5 -1
- data/lib/racecar/rebalance_listener.rb +48 -12
- data/lib/racecar/runner.rb +9 -14
- data/lib/racecar/version.rb +1 -1
- data/racecar.gemspec +2 -2
- metadata +12 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4e6b0c4d52637bd8c6bb2c5e3d69013889053973195a5d73ac5d5645047f985
|
|
4
|
+
data.tar.gz: 23425d552932665cafa03cf245d3d4ef879e053e9e9c60d1eb172455f405f3f3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62d52be41cb3821a5d6534fea43deea8d02dd8a8f03d3449249be0fcdcbf5c842cbb2be8aac7065b97fb515b16370ab93f114ab44dd85dce6a22091d9ac04de4
|
|
7
|
+
data.tar.gz: 006efc7649ddb4257a80e1f2400a62e9e9cba0fa5573d3947f47293d4d77b9b701d66ebf6d465cf91fbf209771e5699ddc9cd7df238ecddf7f54ffbab4eb0c15
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -11,17 +11,18 @@ jobs:
|
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
|
|
13
13
|
strategy:
|
|
14
|
+
fail-fast: false
|
|
14
15
|
matrix:
|
|
15
16
|
ruby-version:
|
|
16
|
-
- "2.7"
|
|
17
|
-
- "3.0"
|
|
18
17
|
- "3.1"
|
|
19
18
|
- "3.2"
|
|
19
|
+
- "3.3"
|
|
20
|
+
- "3.4"
|
|
20
21
|
|
|
21
22
|
steps:
|
|
22
|
-
- uses:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
23
24
|
- name: Set up Ruby
|
|
24
|
-
uses:
|
|
25
|
+
uses: ruby/setup-ruby@v1
|
|
25
26
|
with:
|
|
26
27
|
ruby-version: ${{ matrix.ruby-version }}
|
|
27
28
|
bundler-cache: true
|
|
@@ -30,15 +31,23 @@ jobs:
|
|
|
30
31
|
|
|
31
32
|
integration-specs:
|
|
32
33
|
runs-on: ubuntu-latest
|
|
34
|
+
strategy:
|
|
35
|
+
fail-fast: false
|
|
36
|
+
matrix:
|
|
37
|
+
ruby-version:
|
|
38
|
+
- "3.1"
|
|
39
|
+
- "3.2"
|
|
40
|
+
- "3.3"
|
|
41
|
+
- "3.4"
|
|
33
42
|
steps:
|
|
34
|
-
- uses:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
35
44
|
- name: Set up Ruby
|
|
36
|
-
uses:
|
|
45
|
+
uses: ruby/setup-ruby@v1
|
|
37
46
|
with:
|
|
38
|
-
ruby-version:
|
|
47
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
39
48
|
bundler-cache: true
|
|
40
49
|
- name: Bring up docker-compose stack
|
|
41
|
-
run: docker
|
|
50
|
+
run: docker compose up -d
|
|
42
51
|
- name: Build and test with RSpec
|
|
43
52
|
env:
|
|
44
53
|
RACECAR_BROKERS: localhost:9092
|
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 2.12.0
|
|
6
|
+
|
|
7
|
+
* Add tests against Ruby 3.4
|
|
8
|
+
* Drop support for Ruby 3.0
|
|
9
|
+
* Allow rdkafka gem versions newer than 0.15.0
|
|
10
|
+
|
|
11
|
+
## 2.11.0
|
|
12
|
+
|
|
13
|
+
* Allow the liveness probe command to skip loading config files
|
|
14
|
+
* Add unix domain socket support for Datadog StatsD metrics
|
|
15
|
+
* Bump minimum rdkafka gem version to 0.15.0
|
|
16
|
+
* Bump minimum Ruby version to 3.0
|
|
17
|
+
* Configurable strategy for consuming multiple topics
|
|
18
|
+
|
|
19
|
+
## 2.10.0
|
|
20
|
+
|
|
21
|
+
* Bump rdkafka gem version to 0.13.0
|
|
22
|
+
* Support cooperative-sticky
|
|
23
|
+
* Instrument produce delivery errors
|
|
24
|
+
* Resolve Rails 7.1 logger incompatibility
|
|
25
|
+
* Don't load rails env for liveness probe
|
|
26
|
+
* Fix config load for liveness probe
|
|
27
|
+
* Send exceptions to `process_batch` instrumenter
|
|
28
|
+
* Docker test fixes
|
|
29
|
+
* Test in CI with Ruby 3.2
|
|
6
30
|
|
|
7
31
|
## v2.9.0, v2.9.0.beta1
|
|
8
32
|
|
|
@@ -12,7 +36,7 @@
|
|
|
12
36
|
* Add built-in liveness probe for Kubernetes deployments.
|
|
13
37
|
|
|
14
38
|
## v2.8.2
|
|
15
|
-
* Handles ErroneousStateError, in previous versions the consumer would do several
|
|
39
|
+
* Handles ErroneousStateError, in previous versions the consumer would do several unnecessary group leave/joins. The log level is also changed to WARN instead of ERROR. ([#295](https://github.com/zendesk/racecar/pull/295))
|
|
16
40
|
|
|
17
41
|
## v2.8.1
|
|
18
42
|
* Adds new ErroneousStateError to racecar in order to give more information on this new possible exception.
|
data/Dockerfile
CHANGED
data/Gemfile
CHANGED
|
@@ -5,6 +5,4 @@ source 'https://rubygems.org'
|
|
|
5
5
|
# Specify your gem's dependencies in racecar.gemspec
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
# on Ruby 2.6, which activesupport 7.0 does not support.
|
|
10
|
-
gem 'activesupport', '~> 6.1.0'
|
|
8
|
+
gem 'activesupport', '~> 7.2.0'
|
data/Gemfile.lock
CHANGED
|
@@ -1,31 +1,43 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
racecar (2.
|
|
4
|
+
racecar (2.12.0)
|
|
5
5
|
king_konf (~> 1.0.0)
|
|
6
|
-
rdkafka (
|
|
6
|
+
rdkafka (>= 0.15.0)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
activesupport (
|
|
12
|
-
|
|
11
|
+
activesupport (7.2.2.1)
|
|
12
|
+
base64
|
|
13
|
+
benchmark (>= 0.3)
|
|
14
|
+
bigdecimal
|
|
15
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
16
|
+
connection_pool (>= 2.2.5)
|
|
17
|
+
drb
|
|
13
18
|
i18n (>= 1.6, < 2)
|
|
19
|
+
logger (>= 1.4.2)
|
|
14
20
|
minitest (>= 5.1)
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
securerandom (>= 0.3)
|
|
22
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
23
|
+
base64 (0.2.0)
|
|
24
|
+
benchmark (0.4.0)
|
|
25
|
+
bigdecimal (3.1.9)
|
|
17
26
|
byebug (11.1.3)
|
|
18
27
|
coderay (1.1.3)
|
|
19
|
-
concurrent-ruby (1.
|
|
28
|
+
concurrent-ruby (1.3.4)
|
|
29
|
+
connection_pool (2.4.1)
|
|
20
30
|
diff-lcs (1.5.0)
|
|
21
31
|
dogstatsd-ruby (5.5.0)
|
|
22
|
-
|
|
32
|
+
drb (2.2.1)
|
|
33
|
+
ffi (1.16.3)
|
|
23
34
|
i18n (1.12.0)
|
|
24
35
|
concurrent-ruby (~> 1.0)
|
|
25
36
|
king_konf (1.0.1)
|
|
37
|
+
logger (1.6.4)
|
|
26
38
|
method_source (1.0.0)
|
|
27
|
-
mini_portile2 (2.8.
|
|
28
|
-
minitest (5.
|
|
39
|
+
mini_portile2 (2.8.5)
|
|
40
|
+
minitest (5.25.4)
|
|
29
41
|
pry (0.14.2)
|
|
30
42
|
coderay (~> 1.1)
|
|
31
43
|
method_source (~> 1.0)
|
|
@@ -33,7 +45,7 @@ GEM
|
|
|
33
45
|
byebug (~> 11.0)
|
|
34
46
|
pry (>= 0.13, < 0.15)
|
|
35
47
|
rake (13.0.6)
|
|
36
|
-
rdkafka (0.
|
|
48
|
+
rdkafka (0.18.0)
|
|
37
49
|
ffi (~> 1.15)
|
|
38
50
|
mini_portile2 (~> 2.6)
|
|
39
51
|
rake (> 12)
|
|
@@ -50,16 +62,16 @@ GEM
|
|
|
50
62
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
51
63
|
rspec-support (~> 3.12.0)
|
|
52
64
|
rspec-support (3.12.0)
|
|
65
|
+
securerandom (0.4.1)
|
|
53
66
|
timecop (0.9.6)
|
|
54
67
|
tzinfo (2.0.6)
|
|
55
68
|
concurrent-ruby (~> 1.0)
|
|
56
|
-
zeitwerk (2.6.7)
|
|
57
69
|
|
|
58
70
|
PLATFORMS
|
|
59
71
|
ruby
|
|
60
72
|
|
|
61
73
|
DEPENDENCIES
|
|
62
|
-
activesupport (~>
|
|
74
|
+
activesupport (~> 7.2.0)
|
|
63
75
|
bundler (>= 1.13, < 3)
|
|
64
76
|
dogstatsd-ruby (>= 4.0.0, < 6.0.0)
|
|
65
77
|
pry-byebug
|
|
@@ -69,4 +81,4 @@ DEPENDENCIES
|
|
|
69
81
|
timecop
|
|
70
82
|
|
|
71
83
|
BUNDLED WITH
|
|
72
|
-
2.
|
|
84
|
+
2.6.2
|
data/README.md
CHANGED
|
@@ -414,6 +414,7 @@ Racecar supports [Datadog](https://www.datadoghq.com/) monitoring integration. I
|
|
|
414
414
|
- `datadog_enabled` – Whether Datadog monitoring is enabled (defaults to `false`).
|
|
415
415
|
- `datadog_host` – The host running the Datadog agent.
|
|
416
416
|
- `datadog_port` – The port of the Datadog agent.
|
|
417
|
+
- `datadog_socket_path` – The unix domain socket of the Datadog agent (when set takes precedence over host/port).
|
|
417
418
|
- `datadog_namespace` – The namespace to use for Datadog metrics.
|
|
418
419
|
- `datadog_tags` – Tags that should always be set on Datadog metrics.
|
|
419
420
|
|
|
@@ -476,6 +477,8 @@ With Foreman, you can easily run these processes locally by executing `foreman r
|
|
|
476
477
|
|
|
477
478
|
If you run your applications in Kubernetes, use the following [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) spec as a starting point:
|
|
478
479
|
|
|
480
|
+
##### Recreate Strategy
|
|
481
|
+
|
|
479
482
|
```yaml
|
|
480
483
|
apiVersion: apps/v1
|
|
481
484
|
kind: Deployment
|
|
@@ -483,8 +486,8 @@ metadata:
|
|
|
483
486
|
name: my-racecar-deployment
|
|
484
487
|
labels:
|
|
485
488
|
app: my-racecar
|
|
486
|
-
spec
|
|
487
|
-
replicas:
|
|
489
|
+
spec
|
|
490
|
+
replicas: 4 # <-- this is a good value if you have a multliple of 4 partitions
|
|
488
491
|
selector:
|
|
489
492
|
matchLabels:
|
|
490
493
|
app: my-racecar
|
|
@@ -506,24 +509,47 @@ spec:
|
|
|
506
509
|
value: 5
|
|
507
510
|
```
|
|
508
511
|
|
|
509
|
-
|
|
512
|
+
This configuration uses the recreate strategy which completely terminates all consumers before starting new ones.
|
|
513
|
+
It's simple and easy to understand but can result in significant 'downtime' where no messages are processed.
|
|
514
|
+
|
|
515
|
+
##### Rolling Updates and 'sticky-cooperative' Assignment
|
|
510
516
|
|
|
511
|
-
|
|
517
|
+
A newer alternative is to use the consumer's "cooperative-sticky" assignment strategy which allows healthy consumers to keep processing their partitions while others are terminated.
|
|
518
|
+
This can be combined with a restricted rolling update to minimize processing downtime.
|
|
519
|
+
|
|
520
|
+
Add to your Racecar config:
|
|
521
|
+
```ruby
|
|
522
|
+
Racecar.configure do |c|
|
|
523
|
+
c.partition_assignment_strategy = "cooperative-sticky"
|
|
524
|
+
end
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Replace the Kubernetes deployment strategy with:
|
|
528
|
+
```yaml
|
|
529
|
+
strategy:
|
|
530
|
+
type: RollingUpdate
|
|
531
|
+
rollingUpdate:
|
|
532
|
+
maxSurge: 0 # <- Never boot an excess consumer
|
|
533
|
+
maxUnavailable: 1 # <- The deploy 'rolls' one consumer at a time
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
These two configurations should be deployed together.
|
|
537
|
+
|
|
538
|
+
While `maxSurge` should always be 0, `maxUnavailable` can be increased to reduce deployment times in exchange for longer pauses in message processing.
|
|
512
539
|
|
|
513
540
|
#### Liveness Probe
|
|
514
541
|
|
|
515
542
|
Racecar comes with a built-in liveness probe, primarily for use with Kubernetes, but useful for any deployment environment where you can periodically run a process to check the health of your consumer.
|
|
516
543
|
|
|
517
544
|
To use this feature:
|
|
518
|
-
|
|
519
|
-
|
|
545
|
+
1. Set the `liveness_probe_enabled` config option to true.
|
|
546
|
+
2. Configure your Kubernetes deployment liveness probe to run this command `$ racecarctl liveness_probe`
|
|
520
547
|
|
|
521
|
-
|
|
522
|
-
When enabled (see config) Racecar will touch the file at `liveness_probe_file_path` each time it finishes polling Kafka and processing the messages in the batch (if any).
|
|
548
|
+
When enabled (see config) Racecar will touch the file at the specified path each time it finishes polling Kafka and processing the messages in the batch (if any).
|
|
523
549
|
|
|
524
550
|
The modified time of this file can be observed to determine when the consumer last exhibited 'liveness'.
|
|
525
551
|
|
|
526
|
-
Running `racecarctl liveness_probe` will return a successful exit status if the last 'liveness' event happened within an acceptable time, `liveness_probe_max_interval`.
|
|
552
|
+
Running `racecarctl liveness_probe` will return a successful exit status if the last 'liveness' event happened within an acceptable time, which you can set as `liveness_probe_max_interval`.
|
|
527
553
|
|
|
528
554
|
`liveness_probe_max_interval` should be long enough to account for both the Kafka polling time of `max_wait_time` and the processing time of a full message batch.
|
|
529
555
|
|
|
@@ -531,9 +557,15 @@ On receiving `SIGTERM`, Racecar will gracefully shut down and delete this file,
|
|
|
531
557
|
|
|
532
558
|
You may wish to tolerate more than one failed probe run to accommodate for environmental variance and clock changes.
|
|
533
559
|
|
|
534
|
-
|
|
560
|
+
The [Configuration section](https://github.com/zendesk/racecar#configuration) for the various ways the liveness probe can be configured. (We recommend environment variables).
|
|
561
|
+
|
|
562
|
+
##### Slow racecar.rb / racecar.yml? Skip config files!
|
|
535
563
|
|
|
536
|
-
|
|
564
|
+
If your config files need to do something expensive, such as load Rails, you can enable `RACECAR_LIVENESS_PROBE_SKIP_CONFIG_FILES`. The liveness probe command will then skip loading your configuration and execute quickly.
|
|
565
|
+
|
|
566
|
+
Most other configuration values can be set via the environment, we recommend you do this for liveness probe settings.
|
|
567
|
+
|
|
568
|
+
##### Example Kubernetes Configuration
|
|
537
569
|
|
|
538
570
|
```yaml
|
|
539
571
|
apiVersion: apps/v1
|
|
@@ -549,8 +581,15 @@ spec:
|
|
|
549
581
|
- SomeConsumer
|
|
550
582
|
|
|
551
583
|
env:
|
|
584
|
+
# Skip config loading to run fast, only the following values are needed
|
|
585
|
+
- name: RACECAR_LIVENESS_PROBE_SKIP_CONFIG_FILES
|
|
586
|
+
value: "true"
|
|
552
587
|
- name: RACECAR_LIVENESS_PROBE_ENABLED
|
|
553
588
|
value: "true"
|
|
589
|
+
- name: RACECAR_LIVENESS_PROBE_FILE_PATH
|
|
590
|
+
value: "/tmp/racecar-liveness"
|
|
591
|
+
- name: RACECAR_LIVENESS_PROBE_MAX_INTERVAL
|
|
592
|
+
value: "5"
|
|
554
593
|
|
|
555
594
|
livenessProbe:
|
|
556
595
|
exec:
|
|
@@ -558,8 +597,8 @@ spec:
|
|
|
558
597
|
- racecarctl
|
|
559
598
|
- liveness_probe
|
|
560
599
|
|
|
561
|
-
# Allow up to
|
|
562
|
-
failureThreshold:
|
|
600
|
+
# Allow up to 3 consecutive failures before terminating Pod:
|
|
601
|
+
failureThreshold: 3
|
|
563
602
|
|
|
564
603
|
# Wait 30 seconds before starting the probes:
|
|
565
604
|
initialDelaySeconds: 30
|
|
@@ -663,13 +702,13 @@ In order to introspect the configuration of a consumer process, send it the `SIG
|
|
|
663
702
|
|
|
664
703
|
### Upgrading from v1 to v2
|
|
665
704
|
|
|
666
|
-
In order to safely upgrade from Racecar v1 to v2, you need to completely shut down your consumer group before starting it up again with the v2 Racecar dependency.
|
|
705
|
+
In order to safely upgrade from Racecar v1 to v2, you need to completely shut down your consumer group before starting it up again with the v2 Racecar dependency.
|
|
667
706
|
|
|
668
707
|
### Compression
|
|
669
708
|
|
|
670
709
|
Racecar v2 requires a C library (zlib) to compress the messages before producing to the topic. If not already installed on you consumer docker container, please install using following command in Dockerfile of consumer
|
|
671
710
|
|
|
672
|
-
```
|
|
711
|
+
```
|
|
673
712
|
apt-get update && apt-get install -y libzstd-dev
|
|
674
713
|
```
|
|
675
714
|
|
|
@@ -677,16 +716,16 @@ apt-get update && apt-get install -y libzstd-dev
|
|
|
677
716
|
|
|
678
717
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
679
718
|
|
|
680
|
-
The integration tests run against a Kafka instance that is not automatically started from within `rspec`. You can set one up using the provided `docker-compose.yml` by running `docker
|
|
719
|
+
The integration tests run against a Kafka instance that is not automatically started from within `rspec`. You can set one up using the provided `docker-compose.yml` by running `docker compose up`.
|
|
681
720
|
|
|
682
721
|
### Running RSpec within Docker
|
|
683
722
|
|
|
684
|
-
There can be behavioural inconsistencies between running the specs on your machine, and in the CI pipeline. Due to this, there is now a Dockerfile included in the project, which is based on the CircleCI ruby 2.7.
|
|
723
|
+
There can be behavioural inconsistencies between running the specs on your machine, and in the CI pipeline. Due to this, there is now a Dockerfile included in the project, which is based on the CircleCI ruby 2.7.8 image. This could easily be extended with more Dockerfiles to cover different Ruby versions if desired. In order to run the specs via Docker:
|
|
685
724
|
|
|
686
725
|
- Uncomment the `tests` service from the docker-compose.yml
|
|
687
|
-
- Bring up the stack with `docker
|
|
688
|
-
- Execute the entire suite with `docker
|
|
689
|
-
- Execute a single spec or directory with `docker
|
|
726
|
+
- Bring up the stack with `docker compose up -d`
|
|
727
|
+
- Execute the entire suite with `docker compose run --rm tests bundle exec rspec`
|
|
728
|
+
- Execute a single spec or directory with `docker compose run --rm tests bundle exec rspec spec/integration/consumer_spec.rb`
|
|
690
729
|
|
|
691
730
|
Please note - your code directory is mounted as a volume, so you can make code changes without needing to rebuild
|
|
692
731
|
|
data/docker-compose.yml
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
version: '2.1'
|
|
2
|
-
|
|
3
1
|
services:
|
|
4
2
|
zookeeper:
|
|
5
|
-
image: confluentinc/cp-zookeeper:
|
|
3
|
+
image: confluentinc/cp-zookeeper:7.8.1
|
|
6
4
|
ports:
|
|
7
5
|
- "2181:2181"
|
|
8
6
|
environment:
|
|
@@ -13,7 +11,7 @@ services:
|
|
|
13
11
|
test: echo ruok | nc 127.0.0.1 2181 | grep imok
|
|
14
12
|
|
|
15
13
|
broker:
|
|
16
|
-
image: confluentinc/cp-kafka:
|
|
14
|
+
image: confluentinc/cp-kafka:7.8.1
|
|
17
15
|
depends_on:
|
|
18
16
|
- zookeeper
|
|
19
17
|
ports:
|
|
@@ -57,7 +55,7 @@ services:
|
|
|
57
55
|
# RACECAR_BROKERS: broker:29092
|
|
58
56
|
# DOCKER_SUDO: 'true'
|
|
59
57
|
# # When bringing up the stack, we just let the container exit. For running the
|
|
60
|
-
# # specs, we'll use commands like `docker
|
|
58
|
+
# # specs, we'll use commands like `docker compose run tests rspec`
|
|
61
59
|
# command: ["echo", "ready"]
|
|
62
60
|
# volumes:
|
|
63
61
|
# # The line below allows us to run docker commands from the container itself
|
data/lib/racecar/cli.rb
CHANGED
|
@@ -157,10 +157,11 @@ module Racecar
|
|
|
157
157
|
require_relative './datadog'
|
|
158
158
|
|
|
159
159
|
Datadog.configure do |datadog|
|
|
160
|
-
datadog.host
|
|
161
|
-
datadog.port
|
|
162
|
-
datadog.
|
|
163
|
-
datadog.
|
|
160
|
+
datadog.host = config.datadog_host unless config.datadog_host.nil?
|
|
161
|
+
datadog.port = config.datadog_port unless config.datadog_port.nil?
|
|
162
|
+
datadog.socket_path = config.datadog_socket_path unless config.datadog_socket_path.nil?
|
|
163
|
+
datadog.namespace = config.datadog_namespace unless config.datadog_namespace.nil?
|
|
164
|
+
datadog.tags = config.datadog_tags unless config.datadog_tags.nil?
|
|
164
165
|
end
|
|
165
166
|
end
|
|
166
167
|
end
|
data/lib/racecar/config.rb
CHANGED
|
@@ -29,6 +29,9 @@ module Racecar
|
|
|
29
29
|
desc "The minimum number of messages in the local consumer queue"
|
|
30
30
|
integer :min_message_queue_size, default: 2000
|
|
31
31
|
|
|
32
|
+
desc "Which partition assignment strategy to use, range, roundrobin or cooperative-sticky. -- https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md"
|
|
33
|
+
string :partition_assignment_strategy, default: "range,roundrobin"
|
|
34
|
+
|
|
32
35
|
desc "Kafka consumer configuration options, separated with '=' -- https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md"
|
|
33
36
|
list :consumer, default: []
|
|
34
37
|
|
|
@@ -155,6 +158,9 @@ module Racecar
|
|
|
155
158
|
desc "The port of the Datadog agent"
|
|
156
159
|
integer :datadog_port
|
|
157
160
|
|
|
161
|
+
desc "The unix domain socket of the Datadog agent (when set takes precedence over host/port)"
|
|
162
|
+
string :datadog_socket_path
|
|
163
|
+
|
|
158
164
|
desc "The namespace to use for Datadog metrics"
|
|
159
165
|
string :datadog_namespace
|
|
160
166
|
|
|
@@ -182,6 +188,12 @@ module Racecar
|
|
|
182
188
|
desc "Used only by the liveness probe: Max time (in seconds) between liveness events before the process is considered not healthy"
|
|
183
189
|
integer :liveness_probe_max_interval, default: 5
|
|
184
190
|
|
|
191
|
+
desc "Allows the liveness probe command to skip loading config files. When enabled, configure liveness probe values via environmental variables. Defaults still apply. Only applies to the liveness probe command."
|
|
192
|
+
boolean :liveness_probe_skip_config_files, default: false
|
|
193
|
+
|
|
194
|
+
desc "Strategy for switching topics when there are multiple subscriptions. `exhaust-topic` will only switch when the consumer poll returns no messages. `round-robin` will switch after each poll regardless.\nWarning: `round-robin` will be the default in Racecar 3.x"
|
|
195
|
+
string :multi_subscription_strategy, allowed_values: %w(round-robin exhaust-topic), default: "exhaust-topic"
|
|
196
|
+
|
|
185
197
|
# The error handler must be set directly on the object.
|
|
186
198
|
attr_reader :error_handler
|
|
187
199
|
|
|
@@ -296,10 +308,6 @@ module Racecar
|
|
|
296
308
|
)
|
|
297
309
|
end
|
|
298
310
|
|
|
299
|
-
def rebalance_listener
|
|
300
|
-
RebalanceListener.new(self)
|
|
301
|
-
end
|
|
302
|
-
|
|
303
311
|
private
|
|
304
312
|
|
|
305
313
|
def rdkafka_security_config
|
data/lib/racecar/consumer.rb
CHANGED
|
@@ -37,6 +37,10 @@ module Racecar
|
|
|
37
37
|
subscriptions << Subscription.new(topic, start_from_beginning, max_bytes_per_partition, additional_config)
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
|
+
|
|
41
|
+
# Rebalance hooks for subclasses to override
|
|
42
|
+
def on_partitions_assigned(rebalance_event); end
|
|
43
|
+
def on_partitions_revoked(rebalance_event); end
|
|
40
44
|
end
|
|
41
45
|
|
|
42
46
|
def configure(producer:, consumer:, instrumenter: NullInstrumenter, config: Racecar.config)
|
|
@@ -64,16 +68,16 @@ module Racecar
|
|
|
64
68
|
@instrumenter.instrument('deliver_messages', instrumentation_payload) do
|
|
65
69
|
@delivery_handles.each do |handle|
|
|
66
70
|
begin
|
|
67
|
-
# rdkafka-ruby checks
|
|
68
|
-
# successfully delivered, up to max_wait_timeout seconds
|
|
69
|
-
# Rdkafka::AbstractHandle::WaitTimeoutError. librdkafka will
|
|
70
|
-
# deliver all messages in the background, until "config.message_timeout"
|
|
71
|
+
# rdkafka-ruby checks with exponential backoff starting at 0 seconds wait
|
|
72
|
+
# if the message was successfully delivered, up to max_wait_timeout seconds
|
|
73
|
+
# before raising Rdkafka::AbstractHandle::WaitTimeoutError. librdkafka will
|
|
74
|
+
# (re)try to deliver all messages in the background, until "config.message_timeout"
|
|
71
75
|
# (message.timeout.ms) is exceeded. Phrased differently, rdkafka-ruby's
|
|
72
76
|
# WaitTimeoutError is just informative.
|
|
73
77
|
# The raising can be avoided if max_wait_timeout below is greater than
|
|
74
78
|
# config.message_timeout, but config is not available here (without
|
|
75
79
|
# changing the interface).
|
|
76
|
-
handle.wait(max_wait_timeout: 60
|
|
80
|
+
handle.wait(max_wait_timeout: 60)
|
|
77
81
|
rescue Rdkafka::AbstractHandle::WaitTimeoutError => e
|
|
78
82
|
partition = MessageDeliveryError.partition_from_delivery_handle(handle)
|
|
79
83
|
# ideally we could use the logger passed to the Runner, but it is not
|
data/lib/racecar/consumer_set.rb
CHANGED
|
@@ -15,6 +15,7 @@ module Racecar
|
|
|
15
15
|
@previous_retries = 0
|
|
16
16
|
|
|
17
17
|
@last_poll_read_nil_message = false
|
|
18
|
+
@paused_tpls = Hash.new { |h, k| h[k] = {} }
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def poll(max_wait_time_ms = @config.max_wait_time_ms)
|
|
@@ -65,14 +66,17 @@ module Racecar
|
|
|
65
66
|
|
|
66
67
|
def close
|
|
67
68
|
each_subscribed(&:close)
|
|
69
|
+
@paused_tpls.clear
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
def current
|
|
71
73
|
@consumers[@consumer_id_iterator.peek] ||= begin
|
|
72
74
|
consumer_config = Rdkafka::Config.new(rdkafka_config(current_subscription))
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
listener = RebalanceListener.new(@config.consumer_class, @instrumenter)
|
|
76
|
+
consumer_config.consumer_rebalance_listener = listener
|
|
75
77
|
consumer = consumer_config.consumer
|
|
78
|
+
listener.rdkafka_consumer = consumer
|
|
79
|
+
|
|
76
80
|
@instrumenter.instrument('join_group') do
|
|
77
81
|
consumer.subscribe current_subscription.topic
|
|
78
82
|
end
|
|
@@ -98,16 +102,25 @@ module Racecar
|
|
|
98
102
|
consumer.pause(filtered_tpl)
|
|
99
103
|
fake_msg = OpenStruct.new(topic: topic, partition: partition, offset: offset)
|
|
100
104
|
consumer.seek(fake_msg)
|
|
105
|
+
|
|
106
|
+
@paused_tpls[topic][partition] = [consumer, filtered_tpl]
|
|
101
107
|
end
|
|
102
108
|
|
|
103
109
|
def resume(topic, partition)
|
|
104
110
|
consumer, filtered_tpl = find_consumer_by(topic, partition)
|
|
111
|
+
|
|
112
|
+
if !consumer && @paused_tpls[topic][partition]
|
|
113
|
+
consumer, filtered_tpl = @paused_tpls[topic][partition]
|
|
114
|
+
end
|
|
115
|
+
|
|
105
116
|
if !consumer
|
|
106
117
|
@logger.info "Attempted to resume #{topic}/#{partition}, but we're not subscribed to it"
|
|
107
118
|
return
|
|
108
119
|
end
|
|
109
120
|
|
|
110
121
|
consumer.resume(filtered_tpl)
|
|
122
|
+
@paused_tpls[topic].delete(partition)
|
|
123
|
+
@paused_tpls.delete(topic) if @paused_tpls[topic].empty?
|
|
111
124
|
end
|
|
112
125
|
|
|
113
126
|
alias :each :each_subscribed
|
|
@@ -162,6 +175,7 @@ module Racecar
|
|
|
162
175
|
|
|
163
176
|
# polls a message for the current consumer, handling any API edge cases.
|
|
164
177
|
def poll_current_consumer(max_wait_time_ms)
|
|
178
|
+
@last_poll_read_nil_message = false
|
|
165
179
|
msg = current.poll(max_wait_time_ms)
|
|
166
180
|
rescue Rdkafka::RdkafkaError => e
|
|
167
181
|
case e.code
|
|
@@ -199,9 +213,14 @@ module Racecar
|
|
|
199
213
|
end
|
|
200
214
|
|
|
201
215
|
def maybe_select_next_consumer
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
216
|
+
case @config.multi_subscription_strategy
|
|
217
|
+
when "round-robin"
|
|
218
|
+
select_next_consumer
|
|
219
|
+
else # "exhaust-topic"
|
|
220
|
+
if @last_poll_read_nil_message
|
|
221
|
+
select_next_consumer
|
|
222
|
+
end
|
|
223
|
+
end
|
|
205
224
|
end
|
|
206
225
|
|
|
207
226
|
def select_next_consumer
|
|
@@ -218,6 +237,11 @@ module Racecar
|
|
|
218
237
|
def rdkafka_config(subscription)
|
|
219
238
|
# https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md
|
|
220
239
|
config = {
|
|
240
|
+
# Manually store offset after messages have been processed successfully
|
|
241
|
+
# to avoid marking failed messages as committed. The call just updates
|
|
242
|
+
# a value within librdkafka and is asynchronously written to proper
|
|
243
|
+
# storage through auto commits.
|
|
244
|
+
"enable.auto.offset.store" => false,
|
|
221
245
|
"auto.commit.interval.ms" => @config.offset_commit_interval * 1000,
|
|
222
246
|
"auto.offset.reset" => subscription.start_from_beginning ? "earliest" : "largest",
|
|
223
247
|
"bootstrap.servers" => @config.brokers.join(","),
|
|
@@ -233,7 +257,8 @@ module Racecar
|
|
|
233
257
|
"queued.min.messages" => @config.min_message_queue_size,
|
|
234
258
|
"session.timeout.ms" => @config.session_timeout * 1000,
|
|
235
259
|
"socket.timeout.ms" => @config.socket_timeout * 1000,
|
|
236
|
-
"statistics.interval.ms" => @config.statistics_interval_ms
|
|
260
|
+
"statistics.interval.ms" => @config.statistics_interval_ms,
|
|
261
|
+
"partition.assignment.strategy" => @config.partition_assignment_strategy,
|
|
237
262
|
}
|
|
238
263
|
config.merge! @config.rdkafka_consumer
|
|
239
264
|
config.merge! subscription.additional_config
|
data/lib/racecar/ctl.rb
CHANGED
|
@@ -36,8 +36,14 @@ module Racecar
|
|
|
36
36
|
require "racecar/liveness_probe"
|
|
37
37
|
parse_options!(args)
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
unless config.liveness_probe_skip_config_files
|
|
40
|
+
if File.exist?("config/racecar.rb")
|
|
41
|
+
require "./config/racecar"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if ENV["RAILS_ENV"] && File.exist?("config/racecar.yml")
|
|
45
|
+
Racecar.config.load_file("config/racecar.yml", ENV["RAILS_ENV"])
|
|
46
|
+
end
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
Racecar.config.liveness_probe.check_liveness_within_interval!
|
|
@@ -136,5 +142,9 @@ module Racecar
|
|
|
136
142
|
|
|
137
143
|
parser.parse!(args)
|
|
138
144
|
end
|
|
145
|
+
|
|
146
|
+
def config
|
|
147
|
+
Racecar.config
|
|
148
|
+
end
|
|
139
149
|
end
|
|
140
150
|
end
|
data/lib/racecar/datadog.rb
CHANGED
|
@@ -19,7 +19,11 @@ module Racecar
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def statsd
|
|
22
|
-
@statsd ||=
|
|
22
|
+
@statsd ||= if socket_path
|
|
23
|
+
::Datadog::Statsd.new(socket_path: socket_path, namespace: namespace, tags: tags)
|
|
24
|
+
else
|
|
25
|
+
::Datadog::Statsd.new(host, port, namespace: namespace, tags: tags)
|
|
26
|
+
end
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
def statsd=(statsd)
|
|
@@ -45,6 +49,15 @@ module Racecar
|
|
|
45
49
|
clear
|
|
46
50
|
end
|
|
47
51
|
|
|
52
|
+
def socket_path
|
|
53
|
+
@socket_path
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def socket_path=(socket_path)
|
|
57
|
+
@socket_path = socket_path
|
|
58
|
+
clear
|
|
59
|
+
end
|
|
60
|
+
|
|
48
61
|
def namespace
|
|
49
62
|
@namespace ||= STATSD_NAMESPACE
|
|
50
63
|
end
|
data/lib/racecar/producer.rb
CHANGED
|
@@ -40,6 +40,7 @@ module Racecar
|
|
|
40
40
|
"client.id" => config.client_id,
|
|
41
41
|
"statistics.interval.ms" => config.statistics_interval_ms,
|
|
42
42
|
"message.timeout.ms" => config.message_timeout * 1000,
|
|
43
|
+
"partitioner" => config.partitioner.to_s
|
|
43
44
|
}
|
|
44
45
|
producer_config["compression.codec"] = config.producer_compression_codec.to_s unless config.producer_compression_codec.nil?
|
|
45
46
|
producer_config.merge!(config.rdkafka_producer)
|
|
@@ -26,7 +26,11 @@ module Racecar
|
|
|
26
26
|
console = ActiveSupport::Logger.new($stdout)
|
|
27
27
|
console.formatter = Rails.logger.formatter
|
|
28
28
|
console.level = Rails.logger.level
|
|
29
|
-
Rails
|
|
29
|
+
if ::Rails::VERSION::STRING < "7.1"
|
|
30
|
+
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
|
|
31
|
+
else
|
|
32
|
+
Rails.logger = ActiveSupport::BroadcastLogger.new(Rails.logger, console)
|
|
33
|
+
end
|
|
30
34
|
end
|
|
31
35
|
end
|
|
32
36
|
end
|
|
@@ -1,22 +1,58 @@
|
|
|
1
1
|
module Racecar
|
|
2
2
|
class RebalanceListener
|
|
3
|
-
def initialize(
|
|
4
|
-
@
|
|
5
|
-
@
|
|
3
|
+
def initialize(consumer_class, instrumenter)
|
|
4
|
+
@consumer_class = consumer_class
|
|
5
|
+
@instrumenter = instrumenter
|
|
6
|
+
@rdkafka_consumer = nil
|
|
6
7
|
end
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
attr_writer :rdkafka_consumer
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
attr_reader :consumer_class, :instrumenter, :rdkafka_consumer
|
|
12
|
+
private :consumer_class, :instrumenter, :rdkafka_consumer
|
|
13
|
+
|
|
14
|
+
def on_partitions_assigned(rdkafka_topic_partition_list)
|
|
15
|
+
event = Event.new(rdkafka_consumer: rdkafka_consumer, rdkafka_topic_partition_list: rdkafka_topic_partition_list)
|
|
16
|
+
|
|
17
|
+
instrument("partitions_assigned", partitions: event.partition_numbers) do
|
|
18
|
+
consumer_class.on_partitions_assigned(event)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def on_partitions_revoked(rdkafka_topic_partition_list)
|
|
23
|
+
event = Event.new(rdkafka_consumer: rdkafka_consumer, rdkafka_topic_partition_list: rdkafka_topic_partition_list)
|
|
24
|
+
|
|
25
|
+
instrument("partitions_revoked", partitions: event.partition_numbers) do
|
|
26
|
+
consumer_class.on_partitions_revoked(event)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def instrument(event, payload, &block)
|
|
33
|
+
instrumenter.instrument(event, payload, &block)
|
|
14
34
|
end
|
|
15
35
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
36
|
+
class Event
|
|
37
|
+
def initialize(rdkafka_topic_partition_list:, rdkafka_consumer:)
|
|
38
|
+
@__rdkafka_topic_partition_list = rdkafka_topic_partition_list
|
|
39
|
+
@__rdkafka_consumer = rdkafka_consumer
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def topic_name
|
|
43
|
+
__rdkafka_topic_partition_list.to_h.keys.first
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def partition_numbers
|
|
47
|
+
__rdkafka_topic_partition_list.to_h.values.flatten.map(&:partition)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def empty?
|
|
51
|
+
__rdkafka_topic_partition_list.empty?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# API private and not guaranteed stable
|
|
55
|
+
attr_reader :__rdkafka_topic_partition_list, :__rdkafka_consumer
|
|
20
56
|
end
|
|
21
57
|
end
|
|
22
58
|
end
|
data/lib/racecar/runner.rb
CHANGED
|
@@ -96,7 +96,7 @@ module Racecar
|
|
|
96
96
|
end
|
|
97
97
|
ensure
|
|
98
98
|
producer.close
|
|
99
|
-
Racecar::Datadog.close if
|
|
99
|
+
Racecar::Datadog.close if config.datadog_enabled
|
|
100
100
|
@instrumenter.instrument("shut_down", instrumentation_payload || {})
|
|
101
101
|
end
|
|
102
102
|
|
|
@@ -131,11 +131,6 @@ module Racecar
|
|
|
131
131
|
|
|
132
132
|
def consumer
|
|
133
133
|
@consumer ||= begin
|
|
134
|
-
# Manually store offset after messages have been processed successfully
|
|
135
|
-
# to avoid marking failed messages as committed. The call just updates
|
|
136
|
-
# a value within librdkafka and is asynchronously written to proper
|
|
137
|
-
# storage through auto commits.
|
|
138
|
-
config.consumer << "enable.auto.offset.store=false"
|
|
139
134
|
ConsumerSet.new(config, logger, @instrumenter)
|
|
140
135
|
end
|
|
141
136
|
end
|
|
@@ -213,21 +208,21 @@ module Racecar
|
|
|
213
208
|
}
|
|
214
209
|
|
|
215
210
|
@instrumenter.instrument("start_process_batch", instrumentation_payload)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
211
|
+
with_pause(first.topic, first.partition, first.offset..last.offset) do |pause|
|
|
212
|
+
begin
|
|
213
|
+
@instrumenter.instrument("process_batch", instrumentation_payload) do
|
|
219
214
|
racecar_messages = messages.map do |message|
|
|
220
215
|
Racecar::Message.new(message, retries_count: pause.pauses_count)
|
|
221
216
|
end
|
|
222
217
|
processor.process_batch(racecar_messages)
|
|
223
218
|
processor.deliver!
|
|
224
219
|
consumer.store_offset(messages.last)
|
|
225
|
-
rescue => e
|
|
226
|
-
instrumentation_payload[:unrecoverable_delivery_error] = reset_producer_on_unrecoverable_delivery_errors(e)
|
|
227
|
-
instrumentation_payload[:retries_count] = pause.pauses_count
|
|
228
|
-
config.error_handler.call(e, instrumentation_payload)
|
|
229
|
-
raise e
|
|
230
220
|
end
|
|
221
|
+
rescue => e
|
|
222
|
+
instrumentation_payload[:unrecoverable_delivery_error] = reset_producer_on_unrecoverable_delivery_errors(e)
|
|
223
|
+
instrumentation_payload[:retries_count] = pause.pauses_count
|
|
224
|
+
config.error_handler.call(e, instrumentation_payload)
|
|
225
|
+
raise e
|
|
231
226
|
end
|
|
232
227
|
end
|
|
233
228
|
end
|
data/lib/racecar/version.rb
CHANGED
data/racecar.gemspec
CHANGED
|
@@ -20,10 +20,10 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
21
21
|
spec.require_paths = ["lib"]
|
|
22
22
|
|
|
23
|
-
spec.required_ruby_version = '>=
|
|
23
|
+
spec.required_ruby_version = '>= 3.1'
|
|
24
24
|
|
|
25
25
|
spec.add_runtime_dependency "king_konf", "~> 1.0.0"
|
|
26
|
-
spec.add_runtime_dependency "rdkafka", "
|
|
26
|
+
spec.add_runtime_dependency "rdkafka", ">= 0.15.0"
|
|
27
27
|
|
|
28
28
|
spec.add_development_dependency "bundler", [">= 1.13", "< 3"]
|
|
29
29
|
spec.add_development_dependency "pry-byebug"
|
metadata
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: racecar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Schierbeck
|
|
8
8
|
- Benjamin Quorning
|
|
9
|
-
autorequire:
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2025-02-27 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: king_konf
|
|
@@ -29,16 +29,16 @@ dependencies:
|
|
|
29
29
|
name: rdkafka
|
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
|
31
31
|
requirements:
|
|
32
|
-
- - "
|
|
32
|
+
- - ">="
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: 0.
|
|
34
|
+
version: 0.15.0
|
|
35
35
|
type: :runtime
|
|
36
36
|
prerelease: false
|
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
|
38
38
|
requirements:
|
|
39
|
-
- - "
|
|
39
|
+
- - ">="
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: 0.
|
|
41
|
+
version: 0.15.0
|
|
42
42
|
- !ruby/object:Gem::Dependency
|
|
43
43
|
name: bundler
|
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -149,7 +149,7 @@ dependencies:
|
|
|
149
149
|
- - ">="
|
|
150
150
|
- !ruby/object:Gem::Version
|
|
151
151
|
version: '0'
|
|
152
|
-
description:
|
|
152
|
+
description:
|
|
153
153
|
email:
|
|
154
154
|
- dschierbeck@zendesk.com
|
|
155
155
|
- bquorning@zendesk.com
|
|
@@ -214,7 +214,7 @@ homepage: https://github.com/zendesk/racecar
|
|
|
214
214
|
licenses:
|
|
215
215
|
- Apache License Version 2.0
|
|
216
216
|
metadata: {}
|
|
217
|
-
post_install_message:
|
|
217
|
+
post_install_message:
|
|
218
218
|
rdoc_options: []
|
|
219
219
|
require_paths:
|
|
220
220
|
- lib
|
|
@@ -222,15 +222,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
222
222
|
requirements:
|
|
223
223
|
- - ">="
|
|
224
224
|
- !ruby/object:Gem::Version
|
|
225
|
-
version: '
|
|
225
|
+
version: '3.1'
|
|
226
226
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
227
227
|
requirements:
|
|
228
228
|
- - ">="
|
|
229
229
|
- !ruby/object:Gem::Version
|
|
230
230
|
version: '0'
|
|
231
231
|
requirements: []
|
|
232
|
-
rubygems_version: 3.
|
|
233
|
-
signing_key:
|
|
232
|
+
rubygems_version: 3.5.22
|
|
233
|
+
signing_key:
|
|
234
234
|
specification_version: 4
|
|
235
235
|
summary: A framework for running Kafka consumers
|
|
236
236
|
test_files: []
|