karafka 2.0.23 → 2.0.26

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +24 -3
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +52 -1
  6. data/Gemfile.lock +14 -12
  7. data/README.md +6 -4
  8. data/bin/integrations +8 -0
  9. data/bin/verify_license_integrity +35 -0
  10. data/config/{errors.yml → locales/errors.yml} +2 -1
  11. data/config/locales/pro_errors.yml +18 -0
  12. data/docker-compose.yml +3 -0
  13. data/karafka.gemspec +3 -3
  14. data/lib/karafka/active_job/job_options_contract.rb +1 -1
  15. data/lib/karafka/admin.rb +16 -14
  16. data/lib/karafka/app.rb +16 -4
  17. data/lib/karafka/base_consumer.rb +37 -7
  18. data/lib/karafka/connection/client.rb +21 -0
  19. data/lib/karafka/connection/consumer_group_coordinator.rb +7 -1
  20. data/lib/karafka/connection/listener.rb +5 -4
  21. data/lib/karafka/connection/listeners_batch.rb +6 -0
  22. data/lib/karafka/contracts/config.rb +1 -1
  23. data/lib/karafka/contracts/consumer_group.rb +1 -1
  24. data/lib/karafka/contracts/server_cli_options.rb +2 -1
  25. data/lib/karafka/contracts/topic.rb +13 -2
  26. data/lib/karafka/instrumentation/logger_listener.rb +50 -2
  27. data/lib/karafka/instrumentation/notifications.rb +17 -7
  28. data/lib/karafka/instrumentation/proctitle_listener.rb +7 -16
  29. data/lib/karafka/instrumentation/vendors/datadog/listener.rb +2 -2
  30. data/lib/karafka/messages/message.rb +14 -2
  31. data/lib/karafka/messages/parser.rb +14 -0
  32. data/lib/karafka/pro/active_job/job_options_contract.rb +1 -1
  33. data/lib/karafka/pro/encryption/cipher.rb +58 -0
  34. data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
  35. data/lib/karafka/pro/encryption/errors.rb +24 -0
  36. data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
  37. data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
  38. data/lib/karafka/pro/encryption/setup/config.rb +48 -0
  39. data/lib/karafka/pro/encryption.rb +47 -0
  40. data/lib/karafka/pro/loader.rb +22 -1
  41. data/lib/karafka/pro/processing/strategies/aj_dlq_mom.rb +1 -1
  42. data/lib/karafka/pro/processing/strategies/aj_lrj_mom_vp.rb +6 -1
  43. data/lib/karafka/pro/processing/strategies/aj_mom_vp.rb +1 -1
  44. data/lib/karafka/pro/processing/strategies/default.rb +7 -1
  45. data/lib/karafka/pro/processing/strategies/dlq.rb +1 -1
  46. data/lib/karafka/pro/processing/strategies/dlq_lrj.rb +1 -1
  47. data/lib/karafka/pro/processing/strategies/dlq_lrj_mom.rb +1 -1
  48. data/lib/karafka/pro/processing/strategies/dlq_mom.rb +1 -1
  49. data/lib/karafka/pro/processing/strategies/lrj.rb +6 -1
  50. data/lib/karafka/pro/processing/strategies/lrj_mom.rb +6 -1
  51. data/lib/karafka/pro/processing/strategies/mom.rb +1 -1
  52. data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +2 -2
  53. data/lib/karafka/pro/routing/features/long_running_job/contract.rb +2 -2
  54. data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +2 -2
  55. data/lib/karafka/process.rb +3 -1
  56. data/lib/karafka/processing/executor.rb +1 -1
  57. data/lib/karafka/processing/jobs_queue.rb +2 -2
  58. data/lib/karafka/processing/strategies/aj_dlq_mom.rb +1 -1
  59. data/lib/karafka/processing/strategies/base.rb +5 -0
  60. data/lib/karafka/processing/strategies/default.rb +15 -1
  61. data/lib/karafka/processing/strategies/dlq.rb +1 -1
  62. data/lib/karafka/processing/strategies/dlq_mom.rb +1 -1
  63. data/lib/karafka/processing/strategies/mom.rb +1 -1
  64. data/lib/karafka/processing/worker.rb +3 -1
  65. data/lib/karafka/railtie.rb +3 -0
  66. data/lib/karafka/routing/builder.rb +1 -1
  67. data/lib/karafka/routing/consumer_group.rb +3 -3
  68. data/lib/karafka/routing/consumer_mapper.rb +0 -10
  69. data/lib/karafka/routing/features/active_job/contract.rb +1 -1
  70. data/lib/karafka/routing/features/dead_letter_queue/contract.rb +1 -1
  71. data/lib/karafka/routing/features/manual_offset_management/contract.rb +1 -1
  72. data/lib/karafka/routing/router.rb +12 -2
  73. data/lib/karafka/routing/subscription_group.rb +18 -1
  74. data/lib/karafka/routing/topic.rb +11 -0
  75. data/lib/karafka/runner.rb +1 -0
  76. data/lib/karafka/server.rb +27 -18
  77. data/lib/karafka/setup/config.rb +15 -2
  78. data/lib/karafka/status.rb +33 -9
  79. data/lib/karafka/templates/karafka.rb.erb +1 -2
  80. data/lib/karafka/time_trackers/base.rb +1 -6
  81. data/lib/karafka/time_trackers/pause.rb +5 -3
  82. data/lib/karafka/time_trackers/poll.rb +2 -2
  83. data/lib/karafka/version.rb +1 -1
  84. data/lib/karafka.rb +2 -0
  85. data.tar.gz.sig +0 -0
  86. metadata +18 -8
  87. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84d8130c528081b283889f9f1ebe89b8829b800a2c5d1f4ca99f6d6ce9b4c9df
4
- data.tar.gz: 3cc30f65586226bcb6d8ed4fdac912a72c1a4a7eb5691862b378629d9de1347f
3
+ metadata.gz: e1e0356b40b0812dc321a825731c6fcd7413f9d1d30a23288597d2f832c6b5d3
4
+ data.tar.gz: f887c3bc93f945329fa4bba4bcc81a8f95a9d5153fae64aa5b0936935502909d
5
5
  SHA512:
6
- metadata.gz: 8e1f3fc0a3c73035fdb38093eb0594c999d0a715752f41ed95079b1d8f2e89a5a0fa0abdf054272891e89ffa4dd8d010dd2225540d4480067d8bc0359bf2b7b8
7
- data.tar.gz: 7c1f3f958ef52682e46efb11e8a3ddcbe641ba85c62956fefb84ec6eacadbe9d28ee509bdfd61fcd73ea8f52b1ab867de4909990503024cc5c603788931264ca
6
+ metadata.gz: 32b9c06212b90bb49232931a06e9ae445b76c2c8780c0774923945458dd425233c37167825c5d1f39118cdebb8352f6a014a8e547947367fa712e46fe07c96d5
7
+ data.tar.gz: 2047da1a400328dd13465f9c94b033a9841675b6e7274fc9d94a1677866d0feb9611858a02296a20e6e7b8ea589c0b903ae35e572c6c6ad6c8a3040300692703
checksums.yaml.gz.sig CHANGED
Binary file
@@ -25,7 +25,7 @@ jobs:
25
25
  - name: Set up Ruby
26
26
  uses: ruby/setup-ruby@v1
27
27
  with:
28
- ruby-version: 3.1
28
+ ruby-version: 3.2
29
29
  bundler-cache: true
30
30
 
31
31
  - name: Install Diffend plugin
@@ -34,6 +34,22 @@ jobs:
34
34
  - name: Bundle Secure
35
35
  run: bundle secure
36
36
 
37
+ karafka-checksum:
38
+ runs-on: ubuntu-latest
39
+ strategy:
40
+ fail-fast: false
41
+ steps:
42
+ - uses: actions/checkout@v3
43
+ with:
44
+ fetch-depth: 0
45
+ - name: Run Karafka license checksum verification
46
+ env:
47
+ KARAFKA_PRO_USERNAME: ${{ secrets.KARAFKA_PRO_USERNAME }}
48
+ KARAFKA_PRO_PASSWORD: ${{ secrets.KARAFKA_PRO_PASSWORD }}
49
+ KARAFKA_PRO_VERSION: ${{ secrets.KARAFKA_PRO_VERSION }}
50
+ KARAFKA_PRO_LICENSE_CHECKSUM: ${{ secrets.KARAFKA_PRO_LICENSE_CHECKSUM }}
51
+ run: bin/verify_license_integrity
52
+
37
53
  coditsu:
38
54
  runs-on: ubuntu-latest
39
55
  strategy:
@@ -46,12 +62,14 @@ jobs:
46
62
  run: \curl -sSL https://api.coditsu.io/run/ci | bash
47
63
 
48
64
  specs:
65
+ timeout-minutes: 30
49
66
  runs-on: ubuntu-latest
50
67
  needs: diffend
51
68
  strategy:
52
69
  fail-fast: false
53
70
  matrix:
54
71
  ruby:
72
+ - '3.2'
55
73
  # We run it against the oldest and the newest of a given major to make sure, that there
56
74
  # are no syntax-sugars that we would use that were introduced down the road
57
75
  - '3.1'
@@ -61,7 +79,7 @@ jobs:
61
79
  - '2.7'
62
80
  - '2.7.0'
63
81
  include:
64
- - ruby: '3.1'
82
+ - ruby: '3.2'
65
83
  coverage: 'true'
66
84
  steps:
67
85
  - uses: actions/checkout@v3
@@ -84,17 +102,19 @@ jobs:
84
102
  run: bin/rspecs
85
103
 
86
104
  integrations:
105
+ timeout-minutes: 30
87
106
  runs-on: ubuntu-latest
88
107
  needs: diffend
89
108
  strategy:
90
109
  fail-fast: false
91
110
  matrix:
92
111
  ruby:
112
+ - '3.2'
93
113
  - '3.1'
94
114
  - '3.0'
95
115
  - '2.7'
96
116
  include:
97
- - ruby: '3.1'
117
+ - ruby: '3.2'
98
118
  coverage: 'true'
99
119
  steps:
100
120
  - uses: actions/checkout@v3
@@ -130,5 +150,6 @@ jobs:
130
150
  KARAFKA_PRO_USERNAME: ${{ secrets.KARAFKA_PRO_USERNAME }}
131
151
  KARAFKA_PRO_PASSWORD: ${{ secrets.KARAFKA_PRO_PASSWORD }}
132
152
  KARAFKA_PRO_VERSION: ${{ secrets.KARAFKA_PRO_VERSION }}
153
+ KARAFKA_PRO_LICENSE_CHECKSUM: ${{ secrets.KARAFKA_PRO_LICENSE_CHECKSUM }}
133
154
  GITHUB_COVERAGE: ${{matrix.coverage}}
134
155
  run: bin/integrations
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.3
1
+ 3.2.0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # Karafka framework changelog
2
2
 
3
+ ## 2.0.26 (2023-01-10)
4
+ - **[Feature]** Allow for disabling given topics by setting `active` to false. It will exclude them from consumption but will allow to have their definitions for using admin APIs, etc.
5
+ - [Improvement] Early terminate on `read_topic` when reaching the last offset available on the request time.
6
+ - [Improvement] Introduce a `quiet` state that indicates that Karafka is not only moving to quiet mode but actually that it reached it and no work will happen anymore in any of the consumer groups.
7
+ - [Improvement] Use Karafka defined routes topics when possible for `read_topic` admin API.
8
+ - [Improvement] Introduce `client.pause` and `client.resume` instrumentation hooks for tracking client topic partition pausing and resuming. This is alongside of `consumer.consuming.pause` that can be used to track both manual and automatic pausing with more granular consumer related details. The `client.*` should be used for low level tracking.
9
+ - [Improvement] Replace `LoggerListener` pause notification with one based on `client.pause` instead of `consumer.consuming.pause`.
10
+ - [Improvement] Expand `LoggerListener` with `client.resume` notification.
11
+ - [Improvement] Replace random anonymous subscription groups ids with stable once.
12
+ - [Improvement] Add `consumer.consume`, `consumer.revoke` and `consumer.shutting_down` notification events and move the revocation logic calling to strategies.
13
+ - [Change] Rename job queue statistics `processing` key to `busy`. No changes needed because naming in the DataDog listener stays the same.
14
+ - [Fix] Fix proctitle listener state changes reporting on new states.
15
+ - [Fix] Make sure all files descriptors are closed in the integration specs.
16
+ - [Fix] Fix a case where empty subscription groups could leak into the execution flow.
17
+ - [Fix] Fix `LoggerListener` reporting so it does not end with `.`.
18
+ - [Fix] Run previously defined (if any) signal traps created prior to Karafka signals traps.
19
+
20
+ ## 2.0.25 (2023-01-10)
21
+ - Release yanked due to accidental release with local changes.
22
+
23
+ ## 2.0.24 (2022-12-19)
24
+ - **[Feature]** Provide out of the box encryption support for Pro.
25
+ - [Improvement] Add instrumentation upon `#pause`.
26
+ - [Improvement] Add instrumentation upon retries.
27
+ - [Improvement] Assign `#id` to consumers similar to other entities for ease of debugging.
28
+ - [Improvement] Add retries and pausing to the default `LoggerListener`.
29
+ - [Improvement] Introduce a new final `terminated` state that will kick in prior to exit but after all the instrumentation and other things are done.
30
+ - [Improvement] Ensure that state transitions are thread-safe and ensure state transitions can occur in one direction.
31
+ - [Improvement] Optimize status methods proxying to `Karafka::App`.
32
+ - [Improvement] Allow for easier state usage by introducing explicit `#to_s` for reporting.
33
+ - [Improvement] Change auto-generated id from `SecureRandom#uuid` to `SecureRandom#hex(6)`
34
+ - [Improvement] Emit statistic every 5 seconds by default.
35
+ - [Improvement] Introduce general messages parser that can be swapped when needed.
36
+ - [Fix] Do not trigger code reloading when `consumer_persistence` is enabled.
37
+ - [Fix] Shutdown producer after all the consumer components are down and the status is stopped. This will ensure, that any instrumentation related Kafka messaging can still operate.
38
+
39
+ ### Upgrade notes
40
+
41
+ If you want to disable `librdkafka` statistics because you do not use them at all, update the `kafka` `statistics.interval.ms` setting and set it to `0`:
42
+
43
+ ```ruby
44
+ class KarafkaApp < Karafka::App
45
+ setup do |config|
46
+ # Other settings...
47
+ config.kafka = {
48
+ 'statistics.interval.ms': 0
49
+ }
50
+ end
51
+ end
52
+ ```
53
+
3
54
  ## 2.0.23 (2022-12-07)
4
55
  - [Maintenance] Align with `waterdrop` and `karafka-core`
5
56
  - [Improvement] Provide `Admin#read_topic` API to get topic data without subscribing.
@@ -425,7 +476,7 @@ There are several things in the plan already for 2.1 and beyond, including a web
425
476
  - Small integration specs refactoring + specs for pausing scenarios
426
477
 
427
478
  ## 2.0.0-alpha6 (2022-04-17)
428
- - Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
479
+ - Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
429
480
  - Fix an issue with parallel pristine specs colliding with each other during `bundle install` (#820)
430
481
  - Replace `consumer.consume` with `consumer.consumed` event to match the behaviour
431
482
  - Make sure, that offset committing happens before the `consumer.consumed` event is propagated
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.0.23)
5
- karafka-core (>= 2.0.6, < 3.0.0)
4
+ karafka (2.0.26)
5
+ karafka-core (>= 2.0.8, < 3.0.0)
6
6
  thor (>= 0.20)
7
- waterdrop (>= 2.4.3, < 3.0.0)
7
+ waterdrop (>= 2.4.7, < 3.0.0)
8
8
  zeitwerk (~> 2.3)
9
9
 
10
10
  GEM
@@ -29,11 +29,11 @@ GEM
29
29
  activesupport (>= 5.0)
30
30
  i18n (1.12.0)
31
31
  concurrent-ruby (~> 1.0)
32
- karafka-core (2.0.6)
32
+ karafka-core (2.0.8)
33
33
  concurrent-ruby (>= 1.1)
34
34
  rdkafka (>= 0.12)
35
- mini_portile2 (2.8.0)
36
- minitest (5.16.3)
35
+ mini_portile2 (2.8.1)
36
+ minitest (5.17.0)
37
37
  rake (13.0.6)
38
38
  rdkafka (0.12.0)
39
39
  ffi (~> 1.15)
@@ -45,14 +45,14 @@ GEM
45
45
  rspec-mocks (~> 3.12.0)
46
46
  rspec-core (3.12.0)
47
47
  rspec-support (~> 3.12.0)
48
- rspec-expectations (3.12.0)
48
+ rspec-expectations (3.12.1)
49
49
  diff-lcs (>= 1.2.0, < 2.0)
50
50
  rspec-support (~> 3.12.0)
51
- rspec-mocks (3.12.0)
51
+ rspec-mocks (3.12.1)
52
52
  diff-lcs (>= 1.2.0, < 2.0)
53
53
  rspec-support (~> 3.12.0)
54
54
  rspec-support (3.12.0)
55
- simplecov (0.21.2)
55
+ simplecov (0.22.0)
56
56
  docile (~> 1.1)
57
57
  simplecov-html (~> 0.11)
58
58
  simplecov_json_formatter (~> 0.1)
@@ -61,12 +61,14 @@ GEM
61
61
  thor (1.2.1)
62
62
  tzinfo (2.0.5)
63
63
  concurrent-ruby (~> 1.0)
64
- waterdrop (2.4.3)
65
- karafka-core (>= 2.0.6, < 3.0.0)
64
+ waterdrop (2.4.7)
65
+ karafka-core (>= 2.0.7, < 3.0.0)
66
66
  zeitwerk (~> 2.3)
67
67
  zeitwerk (2.6.6)
68
68
 
69
69
  PLATFORMS
70
+ arm64-darwin-21
71
+ x86_64-darwin-21
70
72
  x86_64-linux
71
73
 
72
74
  DEPENDENCIES
@@ -78,4 +80,4 @@ DEPENDENCIES
78
80
  simplecov
79
81
 
80
82
  BUNDLED WITH
81
- 2.3.26
83
+ 2.4.2
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![Gem Version](https://badge.fury.io/rb/karafka.svg)](http://badge.fury.io/rb/karafka)
5
5
  [![Join the chat at https://slack.karafka.io](https://raw.githubusercontent.com/karafka/misc/master/slack.svg)](https://slack.karafka.io)
6
6
 
7
- **Note**: Upgrade notes for migration from Karafka `1.4` to Karafka `2.0` can be found [here](https://karafka.io/docs/Upgrades-2.0/).
7
+ **Note**: Upgrade instructions for migration from Karafka `1.4` to Karafka `2.0` can be found [here](https://karafka.io/docs/Upgrades-2.0/).
8
8
 
9
9
  ## About Karafka
10
10
 
@@ -40,6 +40,8 @@ Karafka **uses** threads to handle many messages simultaneously in the same proc
40
40
 
41
41
  ## Getting started
42
42
 
43
+ ![karafka web ui](https://raw.githubusercontent.com/karafka/misc/master/printscreens/web-ui.png)
44
+
43
45
  If you're entirely new to the subject, you can start with our "Kafka on Rails" articles series, which will get you up and running with the terminology and basic ideas behind using Kafka:
44
46
 
45
47
  - [Kafka on Rails: Using Kafka with Ruby on Rails – Part 1 – Kafka basics and its advantages](https://mensfeld.pl/2017/11/kafka-on-rails-using-kafka-with-ruby-on-rails-part-1-kafka-basics-and-its-advantages/)
@@ -72,10 +74,10 @@ Karafka.producer.produce_sync(topic: 'example', payload: { 'ping' => 'pong' }.to
72
74
  ```bash
73
75
  bundle exec karafka server
74
76
 
75
- [7616dc24-505a-417f-b87b-6bf8fc2d98c5] Polled 1 message in 1000ms
76
- [dcf3a8d8-0bd9-433a-8f63-b70a0cdb0732] Consume job for ExampleConsumer on example started
77
+ [86d47f0b92f7] Polled 1 message in 1000ms
78
+ [3732873c8a74] Consume job for ExampleConsumer on example started
77
79
  {"ping"=>"pong"}
78
- [dcf3a8d8-0bd9-433a-8f63-b70a0cdb0732] Consume job for ExampleConsumer on example finished in 0ms
80
+ [3732873c8a74] Consume job for ExampleConsumer on example finished in 0ms
79
81
  ```
80
82
 
81
83
  ## Want to Upgrade? LGPL is not for you? Want to help?
data/bin/integrations CHANGED
@@ -152,6 +152,13 @@ class Scenario
152
152
  end
153
153
  end
154
154
 
155
+ # Close all the files that are open, so they do not pile up
156
+ def close
157
+ @stdin.close
158
+ @stdout.close
159
+ @stderr.close
160
+ end
161
+
155
162
  private
156
163
 
157
164
  # Sets up a proper environment for a given spec to run and returns the run command
@@ -248,6 +255,7 @@ while finished_scenarios.size < scenarios.size
248
255
  active_scenarios.select(&:finished?).each do |exited|
249
256
  scenario = active_scenarios.delete(exited)
250
257
  scenario.report
258
+ scenario.close
251
259
  finished_scenarios << scenario
252
260
  end
253
261
 
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This script verifies integrity of the Pro license
4
+ # Run it before bundle install to ensure, that what you are fetching is what you expect
5
+ # Run it after bundle install to ensure that the local artefact was not compromised
6
+
7
+ #!/usr/bin/env bash
8
+
9
+ set -e
10
+
11
+ if [ "$MODE" != "after" ]; then
12
+ # Check the remote license prior to bundle installing
13
+ curl \
14
+ --fail \
15
+ -u $KARAFKA_PRO_USERNAME:$KARAFKA_PRO_PASSWORD \
16
+ https://gems.karafka.io/gems/karafka-license-$KARAFKA_PRO_VERSION.gem \
17
+ -o ./karafka-license.gem
18
+ else
19
+ # Check the local cached one after bundle install
20
+ cache_path=`ruby -e 'puts "#{Gem.dir}/cache/"'`
21
+ cp "$cache_path/karafka-license-$KARAFKA_PRO_VERSION.gem" ./karafka-license.gem
22
+ fi
23
+
24
+ detected=`sha256sum ./karafka-license.gem | awk '{ print $1 }'`
25
+
26
+ rm ./karafka-license.gem
27
+
28
+ echo -n "Karafka Pro license artifact checksum verification result: "
29
+
30
+ if [ "$detected" = "$KARAFKA_PRO_LICENSE_CHECKSUM" ]; then
31
+ echo "Success"
32
+ else
33
+ echo -e "\033[0;31mFailure!\033[0m"
34
+ exit 1
35
+ fi
@@ -44,6 +44,7 @@ en:
44
44
  dead_letter_queue.max_retries_format: needs to be equal or bigger than 0
45
45
  dead_letter_queue.topic_format: 'needs to be a string with a Kafka accepted format'
46
46
  dead_letter_queue.active_format: needs to be either true or false
47
+ active_format: needs to be either true or false
47
48
 
48
49
  consumer_group:
49
50
  missing: needs to be present
@@ -63,7 +64,7 @@ en:
63
64
 
64
65
  pro_topic:
65
66
  virtual_partitions.partitioner_respond_to_call: needs to be defined and needs to respond to `#call`
66
- virtual_partitions.max_partitions_format: needs to be equl or more than 1
67
+ virtual_partitions.max_partitions_format: needs to be equal or more than 1
67
68
  manual_offset_management_not_with_virtual_partitions: cannot be used together with Virtual Partitions
68
69
  long_running_job.active_format: needs to be either true or false
69
70
  dead_letter_queue_not_with_virtual_partitions: cannot be used together with Virtual Partitions
@@ -0,0 +1,18 @@
1
+ en:
2
+ validations:
3
+ topic:
4
+ virtual_partitions.partitioner_respond_to_call: needs to be defined and needs to respond to `#call`
5
+ virtual_partitions.max_partitions_format: needs to be equal or more than 1
6
+ manual_offset_management_not_with_virtual_partitions: cannot be used together with Virtual Partitions
7
+ long_running_job.active_format: needs to be either true or false
8
+ dead_letter_queue_not_with_virtual_partitions: cannot be used together with Virtual Partitions
9
+
10
+ config:
11
+ encryption.active_format: 'needs to be either true or false'
12
+ encryption.public_key_invalid: 'is not a valid public RSA key'
13
+ encryption.public_key_needs_to_be_public: 'is a private RSA key not a public one'
14
+ encryption.private_keys_format: 'needs to be a hash of version and private key value'
15
+ encryption.private_keys_need_to_be_private: 'all keys need to be private'
16
+ encryption.version_format: must be a non-empty string
17
+ encryption.public_key_format: 'is not a valid public RSA key'
18
+ encryption.private_keys_invalid: 'contains an invalid private RSA key string'
data/docker-compose.yml CHANGED
@@ -3,8 +3,10 @@ services:
3
3
  zookeeper:
4
4
  container_name: karafka_20_zookeeper
5
5
  image: wurstmeister/zookeeper
6
+ restart: on-failure
6
7
  ports:
7
8
  - '2181:2181'
9
+
8
10
  kafka:
9
11
  container_name: karafka_20_kafka
10
12
  image: wurstmeister/kafka
@@ -22,3 +24,4 @@ services:
22
24
  benchmarks_00_10:10:1"
23
25
  volumes:
24
26
  - /var/run/docker.sock:/var/run/docker.sock
27
+ restart: on-failure
data/karafka.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.authors = ['Maciej Mensfeld']
13
13
  spec.email = %w[contact@karafka.io]
14
14
  spec.homepage = 'https://karafka.io'
15
- spec.licenses = ['LGPL-3.0', 'Commercial']
15
+ spec.licenses = %w[LGPL-3.0 Commercial]
16
16
  spec.summary = 'Karafka is Ruby and Rails efficient Kafka processing framework.'
17
17
  spec.description = <<-DESC
18
18
  Karafka is Ruby and Rails efficient Kafka processing framework.
@@ -21,9 +21,9 @@ Gem::Specification.new do |spec|
21
21
  without having to focus on things that are not your business domain.
22
22
  DESC
23
23
 
24
- spec.add_dependency 'karafka-core', '>= 2.0.6', '< 3.0.0'
24
+ spec.add_dependency 'karafka-core', '>= 2.0.8', '< 3.0.0'
25
25
  spec.add_dependency 'thor', '>= 0.20'
26
- spec.add_dependency 'waterdrop', '>= 2.4.3', '< 3.0.0'
26
+ spec.add_dependency 'waterdrop', '>= 2.4.7', '< 3.0.0'
27
27
  spec.add_dependency 'zeitwerk', '~> 2.3'
28
28
 
29
29
  spec.required_ruby_version = '>= 2.7.0'
@@ -10,7 +10,7 @@ module Karafka
10
10
  configure do |config|
11
11
  config.error_messages = YAML.safe_load(
12
12
  File.read(
13
- File.join(Karafka.gem_root, 'config', 'errors.yml')
13
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
14
14
  )
15
15
  ).fetch('en').fetch('validations').fetch('job_options')
16
16
  end
data/lib/karafka/admin.rb CHANGED
@@ -30,29 +30,30 @@ module Karafka
30
30
  # @param name [String, Symbol] topic name
31
31
  # @param partition [Integer] partition
32
32
  # @param count [Integer] how many messages we want to get at most
33
- # @param offset [Integer] offset from which we should start. If -1 is provided (default) we
34
- # will start from the latest offset
33
+ # @param start_offset [Integer] offset from which we should start. If -1 is provided
34
+ # (default) we will start from the latest offset
35
35
  #
36
36
  # @return [Array<Karafka::Messages::Message>] array with messages
37
- def read_topic(name, partition, count, offset = -1)
37
+ def read_topic(name, partition, count, start_offset = -1)
38
38
  messages = []
39
39
  tpl = Rdkafka::Consumer::TopicPartitionList.new
40
40
 
41
41
  with_consumer do |consumer|
42
- if offset.negative?
43
- offsets = consumer.query_watermark_offsets(name, partition)
44
- offset = offsets.last - count
45
- end
42
+ offsets = consumer.query_watermark_offsets(name, partition)
43
+ end_offset = offsets.last
46
44
 
47
- offset = offset.negative? ? 0 : offset
45
+ start_offset = [0, offsets.last - count].max if start_offset.negative?
48
46
 
49
- tpl.add_topic_and_partitions_with_offsets(name, partition => offset)
47
+ tpl.add_topic_and_partitions_with_offsets(name, partition => start_offset)
50
48
  consumer.assign(tpl)
51
49
 
52
50
  # We should poll as long as we don't have all the messages that we need or as long as
53
51
  # we do not read all the messages from the topic
54
52
  loop do
53
+ # If we've got as many messages as we've wanted stop
55
54
  break if messages.size >= count
55
+ # If we've reached end of the topic messages, don't process more
56
+ break if !messages.empty? && end_offset <= messages.last.offset
56
57
 
57
58
  message = consumer.poll(200)
58
59
  messages << message if message
@@ -67,7 +68,10 @@ module Karafka
67
68
  messages.map do |message|
68
69
  Messages::Builders::Message.call(
69
70
  message,
70
- Topic.new(name, Karafka::App.config.deserializer),
71
+ # Use topic from routes if we can match it or create a dummy one
72
+ # Dummy one is used in case we cannot match the topic with routes. This can happen
73
+ # when admin API is used to read topics that are not part of the routing
74
+ Routing::Router.find_by(name: name) || Topic.new(name, App.config.deserializer),
71
75
  Time.now
72
76
  )
73
77
  end
@@ -116,8 +120,7 @@ module Karafka
116
120
  # Creates admin instance and yields it. After usage it closes the admin instance
117
121
  def with_admin
118
122
  admin = config(:producer).admin
119
- result = yield(admin)
120
- result
123
+ yield(admin)
121
124
  ensure
122
125
  admin&.close
123
126
  end
@@ -125,8 +128,7 @@ module Karafka
125
128
  # Creates consumer instance and yields it. After usage it closes the consumer instance
126
129
  def with_consumer
127
130
  consumer = config(:consumer).consumer
128
- result = yield(consumer)
129
- result
131
+ yield(consumer)
130
132
  ensure
131
133
  consumer&.close
132
134
  end
data/lib/karafka/app.rb CHANGED
@@ -29,16 +29,28 @@ module Karafka
29
29
  .delete_if { |_, sgs| sgs.empty? }
30
30
  .each { |_, sgs| sgs.each { |sg| sg.topics.delete_if { |top| !top.active? } } }
31
31
  .each { |_, sgs| sgs.delete_if { |sg| sg.topics.empty? } }
32
+ .reject { |cg, _| cg.subscription_groups.empty? }
32
33
  .to_h
33
34
  end
34
35
 
35
36
  # Just a nicer name for the consumer groups
36
37
  alias routes consumer_groups
37
38
 
38
- Status.instance_methods(false).each do |delegated|
39
- define_method(delegated) do
40
- App.config.internal.status.send(delegated)
41
- end
39
+ # Allow for easier status management via `Karafka::App` by aliasing status methods here
40
+ Status::STATES.each do |state, transition|
41
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
42
+ def #{state}
43
+ App.config.internal.status.#{state}
44
+ end
45
+
46
+ def #{state}?
47
+ App.config.internal.status.#{state}?
48
+ end
49
+
50
+ def #{transition}
51
+ App.config.internal.status.#{transition}
52
+ end
53
+ RUBY
42
54
  end
43
55
 
44
56
  # Methods that should be delegated to Karafka module
@@ -4,6 +4,8 @@
4
4
  module Karafka
5
5
  # Base consumer from which all Karafka consumers should inherit
6
6
  class BaseConsumer
7
+ # @return [String] id of the current consumer
8
+ attr_reader :id
7
9
  # @return [Karafka::Routing::Topic] topic to which a given consumer is subscribed
8
10
  attr_accessor :topic
9
11
  # @return [Karafka::Messages::Messages] current messages batch
@@ -15,6 +17,11 @@ module Karafka
15
17
  # @return [Waterdrop::Producer] producer instance
16
18
  attr_accessor :producer
17
19
 
20
+ # Creates new consumer and assigns it an id
21
+ def initialize
22
+ @id = SecureRandom.hex(6)
23
+ end
24
+
18
25
  # Can be used to run preparation code prior to the job being enqueued
19
26
  #
20
27
  # @private
@@ -92,10 +99,6 @@ module Karafka
92
99
  # @private
93
100
  def on_revoked
94
101
  handle_revoked
95
-
96
- Karafka.monitor.instrument('consumer.revoked', caller: self) do
97
- revoked
98
- end
99
102
  rescue StandardError => e
100
103
  Karafka.monitor.instrument(
101
104
  'error.occurred',
@@ -109,9 +112,7 @@ module Karafka
109
112
  #
110
113
  # @private
111
114
  def on_shutdown
112
- Karafka.monitor.instrument('consumer.shutdown', caller: self) do
113
- shutdown
114
- end
115
+ handle_shutdown
115
116
  rescue StandardError => e
116
117
  Karafka.monitor.instrument(
117
118
  'error.occurred',
@@ -204,6 +205,17 @@ module Karafka
204
205
 
205
206
  # Indicate, that user took a manual action of pausing
206
207
  coordinator.manual_pause if manual_pause
208
+
209
+ Karafka.monitor.instrument(
210
+ 'consumer.consuming.pause',
211
+ caller: self,
212
+ manual: manual_pause,
213
+ topic: messages.metadata.topic,
214
+ partition: messages.metadata.partition,
215
+ offset: offset,
216
+ timeout: coordinator.pause_tracker.current_timeout,
217
+ attempt: coordinator.pause_tracker.attempt
218
+ )
207
219
  end
208
220
 
209
221
  # Resumes processing of the current topic partition
@@ -232,5 +244,23 @@ module Karafka
232
244
  def revoked?
233
245
  coordinator.revoked?
234
246
  end
247
+
248
+ # Pauses the processing from the last offset to retry on given message
249
+ # @private
250
+ def retry_after_pause
251
+ pause(coordinator.seek_offset, nil, false)
252
+
253
+ # Instrumentation needs to run **after** `#pause` invocation because we rely on the states
254
+ # set by `#pause`
255
+ Karafka.monitor.instrument(
256
+ 'consumer.consuming.retry',
257
+ caller: self,
258
+ topic: messages.metadata.topic,
259
+ partition: messages.metadata.partition,
260
+ offset: coordinator.seek_offset,
261
+ timeout: coordinator.pause_tracker.current_timeout,
262
+ attempt: coordinator.pause_tracker.attempt
263
+ )
264
+ end
235
265
  end
236
266
  end
@@ -14,6 +14,9 @@ module Karafka
14
14
  # @note Consumer name may change in case we regenerate it
15
15
  attr_reader :name
16
16
 
17
+ # @return [String] id of the client
18
+ attr_reader :id
19
+
17
20
  # How many times should we retry polling in case of a failure
18
21
  MAX_POLL_RETRIES = 20
19
22
 
@@ -29,6 +32,7 @@ module Karafka
29
32
  # with all the configuration details needed for us to create a client
30
33
  # @return [Karafka::Connection::Rdk::Consumer]
31
34
  def initialize(subscription_group)
35
+ @id = SecureRandom.hex(6)
32
36
  # Name is set when we build consumer
33
37
  @name = ''
34
38
  @mutex = Mutex.new
@@ -165,6 +169,15 @@ module Karafka
165
169
 
166
170
  return unless tpl
167
171
 
172
+ Karafka.monitor.instrument(
173
+ 'client.pause',
174
+ caller: self,
175
+ subscription_group: @subscription_group,
176
+ topic: topic,
177
+ partition: partition,
178
+ offset: offset
179
+ )
180
+
168
181
  @paused_tpls[topic][partition] = tpl
169
182
 
170
183
  @kafka.pause(tpl)
@@ -195,6 +208,14 @@ module Karafka
195
208
  # happen in the first place
196
209
  return unless @paused_tpls[topic].delete(partition)
197
210
 
211
+ Karafka.monitor.instrument(
212
+ 'client.resume',
213
+ caller: self,
214
+ subscription_group: @subscription_group,
215
+ topic: topic,
216
+ partition: partition
217
+ )
218
+
198
219
  @kafka.resume(tpl)
199
220
  ensure
200
221
  @mutex.unlock
@@ -21,10 +21,16 @@ module Karafka
21
21
  @finished = Set.new
22
22
  end
23
23
 
24
+ # @return [Boolean] true if all the subscription groups from a given consumer group are
25
+ # finished
26
+ def finished?
27
+ @finished.size == @group_size
28
+ end
29
+
24
30
  # @return [Boolean] can we start shutdown on a given listener
25
31
  # @note If true, will also obtain a lock so no-one else will be closing the same time we do
26
32
  def shutdown?
27
- @finished.size == @group_size && @shutdown_lock.try_lock
33
+ finished? && @shutdown_lock.try_lock
28
34
  end
29
35
 
30
36
  # Unlocks the shutdown lock