karafka 2.5.0.beta2 → 2.5.0.rc2

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +5 -5
  3. data/.github/workflows/push.yml +2 -3
  4. data/CHANGELOG.md +16 -1
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +20 -20
  7. data/bin/integrations +1 -0
  8. data/config/locales/errors.yml +2 -1
  9. data/docker-compose.yml +1 -1
  10. data/examples/payloads/avro/.gitkeep +0 -0
  11. data/karafka.gemspec +2 -2
  12. data/lib/karafka/active_job/job_extensions.rb +4 -1
  13. data/lib/karafka/admin.rb +27 -15
  14. data/lib/karafka/connection/client.rb +9 -0
  15. data/lib/karafka/connection/proxy.rb +1 -1
  16. data/lib/karafka/contracts/base.rb +3 -2
  17. data/lib/karafka/contracts/config.rb +2 -1
  18. data/lib/karafka/helpers/interval_runner.rb +8 -0
  19. data/lib/karafka/instrumentation/logger_listener.rb +11 -11
  20. data/lib/karafka/instrumentation/vendors/kubernetes/base_listener.rb +17 -2
  21. data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +29 -6
  22. data/lib/karafka/instrumentation/vendors/kubernetes/swarm_liveness_listener.rb +9 -0
  23. data/lib/karafka/pro/encryption.rb +4 -1
  24. data/lib/karafka/pro/processing/coordinators/errors_tracker.rb +5 -0
  25. data/lib/karafka/pro/processing/strategies/dlq/default.rb +4 -3
  26. data/lib/karafka/pro/recurring_tasks.rb +8 -2
  27. data/lib/karafka/pro/routing/features/swarm/contracts/routing.rb +3 -2
  28. data/lib/karafka/pro/routing/features/swarm.rb +4 -1
  29. data/lib/karafka/pro/scheduled_messages/consumer.rb +45 -8
  30. data/lib/karafka/pro/scheduled_messages/dispatcher.rb +2 -1
  31. data/lib/karafka/pro/scheduled_messages/proxy.rb +15 -3
  32. data/lib/karafka/pro/scheduled_messages/serializer.rb +2 -4
  33. data/lib/karafka/pro/scheduled_messages/state.rb +20 -23
  34. data/lib/karafka/pro/scheduled_messages/tracker.rb +34 -8
  35. data/lib/karafka/pro/scheduled_messages.rb +4 -1
  36. data/lib/karafka/routing/builder.rb +12 -3
  37. data/lib/karafka/routing/features/base/expander.rb +8 -2
  38. data/lib/karafka/server.rb +4 -1
  39. data/lib/karafka/setup/config.rb +17 -5
  40. data/lib/karafka/swarm/supervisor.rb +5 -2
  41. data/lib/karafka/version.rb +1 -1
  42. metadata +10 -9
  43. /data/examples/payloads/json/{enrollment_event.json → sample_set_01/enrollment_event.json} +0 -0
  44. /data/examples/payloads/json/{ingestion_event.json → sample_set_01/ingestion_event.json} +0 -0
  45. /data/examples/payloads/json/{transaction_event.json → sample_set_01/transaction_event.json} +0 -0
  46. /data/examples/payloads/json/{user_event.json → sample_set_01/user_event.json} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbcaf396d7f2eff35ec3b59c96ffe7dd880f1f07294aa28bb623e95ce328e3e9
4
- data.tar.gz: 410b037a79abbbc82fbd4b540dcb26378dca396e408f68280325e354ae2e275b
3
+ metadata.gz: 45fa2a9d2f2e68cdf611a2610d339d1c33bfdfa42570dccf817950279c1678bc
4
+ data.tar.gz: 32abcc95bda1e13d6d6dc76b25f278867ed055a39f1af675a28efd5cd79d83f6
5
5
  SHA512:
6
- metadata.gz: dcd79f3bda653b74d95938d440ccfe93d8d2cdc214e02de31d9ad56f738b554699bd1991d83dd1c9c7090e1c3a63669f8837677fef77efc22df4804bc16f25a0
7
- data.tar.gz: dbca53f433a0e13ec7582f9ee1c6da15bf38678a1019a3a36e361e7bbbae2c5e5777bf44e61330692b5f38c4fa011dd9e3950027925767908f617df02e1e8219
6
+ metadata.gz: 54c9455fc69ae7f0ab4593be64f0c337680aa3894484a846d07dd9c6aa7e797db901c2a907d11b25088d62e8411e973ae27d39ebb627d5e2cd2f8bbc0f061d73
7
+ data.tar.gz: 2649ac38264e56c88c6608eb36c7dadcf3d6b0020f7ae5ce9ed27ff6c74fcbe95e3f9051fb3c1968508e8d21784d7a54007756f42a153c108ed5fb009f35f58c
@@ -1,4 +1,4 @@
1
- name: ci
1
+ name: CI
2
2
 
3
3
  concurrency:
4
4
  group: ${{ github.workflow }}-${{ github.ref }}
@@ -31,7 +31,7 @@ jobs:
31
31
  fetch-depth: 0
32
32
 
33
33
  - name: Set up Ruby
34
- uses: ruby/setup-ruby@bb0f760b6c925183520ee0bcc9c4a432a7c8c3c6 # v1.241.0
34
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
35
35
  with:
36
36
  ruby-version: 3.4
37
37
  bundler-cache: true
@@ -118,7 +118,7 @@ jobs:
118
118
  run: rm -f Gemfile.lock
119
119
 
120
120
  - name: Set up Ruby
121
- uses: ruby/setup-ruby@bb0f760b6c925183520ee0bcc9c4a432a7c8c3c6 # v1.241.0
121
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
122
122
  with:
123
123
  ruby-version: ${{matrix.ruby}}
124
124
  bundler-cache: true
@@ -164,7 +164,7 @@ jobs:
164
164
  docker compose up -d || (sleep 5 && docker compose up -d)
165
165
 
166
166
  - name: Set up Ruby
167
- uses: ruby/setup-ruby@bb0f760b6c925183520ee0bcc9c4a432a7c8c3c6 # v1.241.0
167
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
168
168
  with:
169
169
  # Do not use cache here as we run bundle install also later in some of the integration
170
170
  # tests and we need to be able to run it without cache
@@ -228,7 +228,7 @@ jobs:
228
228
  docker compose up -d || (sleep 5 && docker compose up -d)
229
229
 
230
230
  - name: Set up Ruby
231
- uses: ruby/setup-ruby@bb0f760b6c925183520ee0bcc9c4a432a7c8c3c6 # v1.241.0
231
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
232
232
  with:
233
233
  ruby-version: ${{matrix.ruby}}
234
234
  bundler: 'latest'
@@ -24,7 +24,7 @@ jobs:
24
24
  fetch-depth: 0
25
25
 
26
26
  - name: Set up Ruby
27
- uses: ruby/setup-ruby@bb0f760b6c925183520ee0bcc9c4a432a7c8c3c6 # v1.241.0
27
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
28
28
  with:
29
29
  bundler-cache: false
30
30
 
@@ -32,5 +32,4 @@ jobs:
32
32
  run: |
33
33
  bundle install --jobs 4 --retry 3
34
34
 
35
- # Release
36
- - uses: rubygems/release-gem@9e85cb11501bebc2ae661c1500176316d3987059 # v1
35
+ - uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1
data/CHANGELOG.md CHANGED
@@ -1,9 +1,14 @@
1
1
  # Karafka Framework Changelog
2
2
 
3
3
  ## 2.5.0 (Unreleased)
4
+ - **[Breaking]** Change how consistency of DLQ dispatches works in Pro (`partition_key` vs. direct partition id mapping).
5
+ - **[Breaking]** Remove the headers `source_key` from the Pro DLQ dispatched messages as the original key is now fully preserved.
4
6
  - **[Breaking]** Use DLQ and Piping prefix `source_` instead of `original_` to align with naming convention of Kafka Streams and Apache Flink for future usage.
5
7
  - **[Breaking]** Rename scheduled jobs topics names in their config (Pro).
8
+ - **[Breaking]** Change K8s listener response from `204` to `200` and include JSON body with reasons.
9
+ - **[Breaking]** Replace admin config `max_attempts` with `max_retries_duration` and
6
10
  - **[Feature]** Parallel Segments for concurrent processing of the same partition with more than partition count of processes (Pro).
11
+ - [Enhancement] Normalize topic + partition logs format.
7
12
  - [Enhancement] Support KIP-82 (header values of arrays).
8
13
  - [Enhancement] Enhance errors tracker with `#counts` that contains per-error class specific counters for granular flow handling.
9
14
  - [Enhancement] Provide explicit `Karafka::Admin.copy_consumer_group` API.
@@ -35,12 +40,19 @@
35
40
  - [Enhancement] Execute the help CLI command when no command provided (similar to Rails) to improve DX.
36
41
  - [Enhancement] Remove backtrace from the CLI error for incorrect commands (similar to Rails) to improve DX.
37
42
  - [Enhancement] Provide `karafka topics help` sub-help due to nesting of Declarative Topics actions.
43
+ - [Enhancement] Use independent keys for different states of reporting in scheduled messages.
44
+ - [Enhancement] Enrich scheduled messages state reporter with debug data.
45
+ - [Enhancement] Introduce a new state called `stopped` to the scheduled messages.
46
+ - [Enhancement] Do not overwrite the `key` in the Pro DLQ dispatched messages for routing reasons.
47
+ - [Enhancement] Introduce `errors_tracker.trace_id` for distributed error details correlation with the Web UI.
48
+ - [Enhancement] Improve contracts validations reporting.
49
+ - [Enhancement] Optimize topic creation and repartitioning admin operations for topics with hundreds of partitions.
38
50
  - [Refactor] Introduce a `bin/verify_kafka_warnings` script to clean Kafka from temporary test-suite topics.
39
51
  - [Refactor] Introduce a `bin/verify_topics_naming` script to ensure proper test topics naming convention.
40
52
  - [Refactor] Make sure all temporary topics have a `it-` prefix in their name.
41
53
  - [Refactor] Improve CI specs parallelization.
42
54
  - [Maintenance] Lower the `Karafka::Admin` `poll_timeout` to 50 ms to improve responsiveness of admin operations.
43
- - [Maintenance] Require `karafka-rdkafka` `>=` `0.19.2` due to usage of `#rd_kafka_global_init`, KIP-82 and the new producer caching engine.
55
+ - [Maintenance] Require `karafka-rdkafka` `>=` `0.19.5` due to usage of `#rd_kafka_global_init`, KIP-82, new producer caching engine and improvements to the `partition_key` assignments.
44
56
  - [Maintenance] Add Deimos routing patch into integration suite not to break it in the future.
45
57
  - [Maintenance] Remove Rails `7.0` specs due to upcoming EOL.
46
58
  - [Fix] Fix Recurring Tasks and Scheduled Messages not working with Swarm (using closed producer).
@@ -58,6 +70,9 @@
58
70
  - [Fix] `karafka` cannot be required without Bundler.
59
71
  - [Fix] Scheduled Messages re-seek moves to `latest` on inheritance of initial offset when `0` offset is compacted.
60
72
  - [Fix] Seek to `:latest` without `topic_partition_position` (-1) will not seek at all.
73
+ - [Fix] Extremely high turn over of scheduled messages can cause them not to reach EOF/Loaded state.
74
+ - [Fix] Fix incorrectly passed `max_wait_time` to rdkafka (ms instead of seconds) causing too long wait.
75
+ - [Fix] Remove aggresive requerying of the Kafka cluster on topic creation/removal/altering.
61
76
  - [Change] Move to trusted-publishers and remove signing since no longer needed.
62
77
 
63
78
  ## 2.4.18 (2025-04-09)
data/Gemfile CHANGED
@@ -18,7 +18,7 @@ end
18
18
  group :integrations do
19
19
  gem 'activejob', require: false
20
20
  gem 'karafka-testing', '>= 2.5.0', require: false
21
- gem 'karafka-web', '>= 0.11.0.beta1', require: false
21
+ gem 'karafka-web', '>= 0.11.0.rc2', require: false
22
22
  end
23
23
 
24
24
  group :test do
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.5.0.beta2)
4
+ karafka (2.5.0.rc2)
5
5
  base64 (~> 0.2)
6
- karafka-core (>= 2.5.0, < 2.6.0)
7
- karafka-rdkafka (>= 0.19.2)
6
+ karafka-core (>= 2.5.2, < 2.6.0)
7
+ karafka-rdkafka (>= 0.19.5)
8
8
  waterdrop (>= 2.8.3, < 3.0.0)
9
9
  zeitwerk (~> 2.3)
10
10
 
@@ -27,9 +27,9 @@ GEM
27
27
  securerandom (>= 0.3)
28
28
  tzinfo (~> 2.0, >= 2.0.5)
29
29
  uri (>= 0.13.1)
30
- base64 (0.2.0)
31
- benchmark (0.4.0)
32
- bigdecimal (3.1.9)
30
+ base64 (0.3.0)
31
+ benchmark (0.4.1)
32
+ bigdecimal (3.2.2)
33
33
  byebug (12.0.0)
34
34
  concurrent-ruby (1.3.5)
35
35
  connection_pool (2.5.3)
@@ -39,7 +39,7 @@ GEM
39
39
  erubi (1.13.1)
40
40
  et-orbi (1.2.11)
41
41
  tzinfo
42
- factory_bot (6.5.1)
42
+ factory_bot (6.5.4)
43
43
  activesupport (>= 6.1.0)
44
44
  ffi (1.17.2)
45
45
  ffi (1.17.2-aarch64-linux-gnu)
@@ -59,19 +59,19 @@ GEM
59
59
  activesupport (>= 6.1)
60
60
  i18n (1.14.7)
61
61
  concurrent-ruby (~> 1.0)
62
- karafka-core (2.5.1)
62
+ karafka-core (2.5.2)
63
63
  karafka-rdkafka (>= 0.19.2, < 0.21.0)
64
64
  logger (>= 1.6.0)
65
- karafka-rdkafka (0.19.4)
65
+ karafka-rdkafka (0.19.5)
66
66
  ffi (~> 1.15)
67
67
  mini_portile2 (~> 2.6)
68
68
  rake (> 12)
69
- karafka-testing (2.5.0)
69
+ karafka-testing (2.5.1)
70
70
  karafka (>= 2.5.0.beta1, < 2.6.0)
71
71
  waterdrop (>= 2.8.0)
72
- karafka-web (0.11.0.beta3)
72
+ karafka-web (0.11.0.rc2)
73
73
  erubi (~> 1.4)
74
- karafka (>= 2.5.0.beta1, < 2.6.0)
74
+ karafka (>= 2.5.0.rc1, < 2.6.0)
75
75
  karafka-core (>= 2.5.0, < 2.6.0)
76
76
  roda (~> 3.68, >= 3.69)
77
77
  tilt (~> 2.0)
@@ -81,22 +81,22 @@ GEM
81
81
  ostruct (0.6.1)
82
82
  raabro (1.4.0)
83
83
  rack (3.1.15)
84
- rake (13.2.1)
84
+ rake (13.3.0)
85
85
  roda (3.92.0)
86
86
  rack
87
- rspec (3.13.0)
87
+ rspec (3.13.1)
88
88
  rspec-core (~> 3.13.0)
89
89
  rspec-expectations (~> 3.13.0)
90
90
  rspec-mocks (~> 3.13.0)
91
- rspec-core (3.13.3)
91
+ rspec-core (3.13.4)
92
92
  rspec-support (~> 3.13.0)
93
- rspec-expectations (3.13.4)
93
+ rspec-expectations (3.13.5)
94
94
  diff-lcs (>= 1.2.0, < 2.0)
95
95
  rspec-support (~> 3.13.0)
96
- rspec-mocks (3.13.4)
96
+ rspec-mocks (3.13.5)
97
97
  diff-lcs (>= 1.2.0, < 2.0)
98
98
  rspec-support (~> 3.13.0)
99
- rspec-support (3.13.3)
99
+ rspec-support (3.13.4)
100
100
  securerandom (0.4.1)
101
101
  simplecov (0.22.0)
102
102
  docile (~> 1.1)
@@ -113,7 +113,7 @@ GEM
113
113
  karafka-core (>= 2.4.9, < 3.0.0)
114
114
  karafka-rdkafka (>= 0.19.2)
115
115
  zeitwerk (~> 2.3)
116
- zeitwerk (2.6.18)
116
+ zeitwerk (2.7.3)
117
117
 
118
118
  PLATFORMS
119
119
  aarch64-linux-gnu
@@ -135,7 +135,7 @@ DEPENDENCIES
135
135
  fugit
136
136
  karafka!
137
137
  karafka-testing (>= 2.5.0)
138
- karafka-web (>= 0.11.0.beta1)
138
+ karafka-web (>= 0.11.0.rc2)
139
139
  ostruct
140
140
  rspec
141
141
  simplecov
data/bin/integrations CHANGED
@@ -45,6 +45,7 @@ class Scenario
45
45
  'shutdown/on_hanging_on_shutdown_job_and_a_shutdown_spec.rb' => [2].freeze,
46
46
  'shutdown/on_hanging_listener_and_shutdown_spec.rb' => [2].freeze,
47
47
  'swarm/forceful_shutdown_of_hanging_spec.rb' => [2].freeze,
48
+ 'swarm/with_blocking_at_exit_spec.rb' => [2].freeze,
48
49
  'instrumentation/post_errors_instrumentation_error_spec.rb' => [1].freeze,
49
50
  'cli/declaratives/delete/existing_with_exit_code_spec.rb' => [2].freeze,
50
51
  'cli/declaratives/create/new_with_exit_code_spec.rb' => [2].freeze,
@@ -84,7 +84,8 @@ en:
84
84
  admin.kafka_format: needs to be a hash
85
85
  admin.group_id_format: 'needs to be a string with a Kafka accepted format'
86
86
  admin.max_wait_time_format: 'needs to be an integer bigger than 0'
87
- admin.max_attempts_format: 'needs to be an integer bigger than 0'
87
+ admin.retry_backoff_format: 'needs to be an integer bigger than 100'
88
+ admin.max_retries_duration_format: 'needs to be an integer bigger than 1000'
88
89
 
89
90
  swarm.nodes_format: 'needs to be an integer bigger than 0'
90
91
  swarm.node_format: needs to be false or node instance
data/docker-compose.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  services:
2
2
  kafka:
3
3
  container_name: kafka
4
- image: confluentinc/cp-kafka:7.9.1
4
+ image: confluentinc/cp-kafka:8.0.0
5
5
 
6
6
  ports:
7
7
  - 9092:9092
File without changes
data/karafka.gemspec CHANGED
@@ -22,8 +22,8 @@ Gem::Specification.new do |spec|
22
22
  DESC
23
23
 
24
24
  spec.add_dependency 'base64', '~> 0.2'
25
- spec.add_dependency 'karafka-core', '>= 2.5.0', '< 2.6.0'
26
- spec.add_dependency 'karafka-rdkafka', '>= 0.19.2'
25
+ spec.add_dependency 'karafka-core', '>= 2.5.2', '< 2.6.0'
26
+ spec.add_dependency 'karafka-rdkafka', '>= 0.19.5'
27
27
  spec.add_dependency 'waterdrop', '>= 2.8.3', '< 3.0.0'
28
28
  spec.add_dependency 'zeitwerk', '~> 2.3'
29
29
 
@@ -21,7 +21,10 @@ module Karafka
21
21
 
22
22
  # Make sure, that karafka options that someone wants to use are valid before assigning
23
23
  # them
24
- App.config.internal.active_job.job_options_contract.validate!(new_options)
24
+ App.config.internal.active_job.job_options_contract.validate!(
25
+ new_options,
26
+ scope: %w[active_job]
27
+ )
25
28
 
26
29
  # We need to modify this hash because otherwise we would modify parent hash.
27
30
  self._karafka_options = _karafka_options.dup
data/lib/karafka/admin.rb CHANGED
@@ -10,10 +10,13 @@ module Karafka
10
10
  # Cluster on which operations are performed can be changed via `admin.kafka` config, however
11
11
  # there is no multi-cluster runtime support.
12
12
  module Admin
13
+ extend Core::Helpers::Time
14
+
13
15
  extend Helpers::ConfigImporter.new(
14
16
  max_wait_time: %i[admin max_wait_time],
15
17
  poll_timeout: %i[admin poll_timeout],
16
- max_attempts: %i[admin max_attempts],
18
+ max_retries_duration: %i[admin max_retries_duration],
19
+ retry_backoff: %i[admin retry_backoff],
17
20
  group_id: %i[admin group_id],
18
21
  app_kafka: %i[kafka],
19
22
  admin_kafka: %i[admin kafka]
@@ -122,7 +125,7 @@ module Karafka
122
125
  handler = admin.create_topic(name, partitions, replication_factor, topic_config)
123
126
 
124
127
  with_re_wait(
125
- -> { handler.wait(max_wait_timeout: max_wait_time) },
128
+ -> { handler.wait(max_wait_timeout: max_wait_time_seconds) },
126
129
  -> { topics_names.include?(name) }
127
130
  )
128
131
  end
@@ -136,7 +139,7 @@ module Karafka
136
139
  handler = admin.delete_topic(name)
137
140
 
138
141
  with_re_wait(
139
- -> { handler.wait(max_wait_timeout: max_wait_time) },
142
+ -> { handler.wait(max_wait_timeout: max_wait_time_seconds) },
140
143
  -> { !topics_names.include?(name) }
141
144
  )
142
145
  end
@@ -151,7 +154,7 @@ module Karafka
151
154
  handler = admin.create_partitions(name, partitions)
152
155
 
153
156
  with_re_wait(
154
- -> { handler.wait(max_wait_timeout: max_wait_time) },
157
+ -> { handler.wait(max_wait_timeout: max_wait_time_seconds) },
155
158
  -> { topic_info(name).fetch(:partition_count) >= partitions }
156
159
  )
157
160
  end
@@ -362,7 +365,7 @@ module Karafka
362
365
  def delete_consumer_group(consumer_group_id)
363
366
  with_admin do |admin|
364
367
  handler = admin.delete_group(consumer_group_id)
365
- handler.wait(max_wait_timeout: max_wait_time)
368
+ handler.wait(max_wait_timeout: max_wait_time_seconds)
366
369
  end
367
370
  end
368
371
 
@@ -564,6 +567,12 @@ module Karafka
564
567
 
565
568
  private
566
569
 
570
+ # @return [Integer] number of seconds to wait. `rdkafka` requires this value
571
+ # (`max_wait_time`) to be provided in seconds while we define it in ms hence the conversion
572
+ def max_wait_time_seconds
573
+ max_wait_time / 1_000.0
574
+ end
575
+
567
576
  # Adds a new callback for given rdkafka instance for oauth token refresh (if needed)
568
577
  #
569
578
  # @param id [String, Symbol] unique (for the lifetime of instance) id that we use for
@@ -602,20 +611,23 @@ module Karafka
602
611
  # @param handler [Proc] the wait handler operation
603
612
  # @param breaker [Proc] extra condition upon timeout that indicates things were finished ok
604
613
  def with_re_wait(handler, breaker)
605
- attempt ||= 0
606
- attempt += 1
614
+ start_time = monotonic_now
615
+ # Convert milliseconds to seconds for sleep
616
+ sleep_time = retry_backoff / 1000.0
607
617
 
608
- handler.call
618
+ loop do
619
+ handler.call
609
620
 
610
- # If breaker does not operate, it means that the requested change was applied but is still
611
- # not visible and we need to wait
612
- raise(Errors::ResultNotVisibleError) unless breaker.call
613
- rescue Rdkafka::AbstractHandle::WaitTimeoutError, Errors::ResultNotVisibleError
614
- return if breaker.call
621
+ sleep(sleep_time)
615
622
 
616
- retry if attempt <= max_attempts
623
+ return if breaker.call
624
+ rescue Rdkafka::AbstractHandle::WaitTimeoutError
625
+ return if breaker.call
617
626
 
618
- raise
627
+ next if monotonic_now - start_time < max_retries_duration
628
+
629
+ raise(Errors::ResultNotVisibleError)
630
+ end
619
631
  end
620
632
 
621
633
  # @param type [Symbol] type of config we want
@@ -427,6 +427,15 @@ module Karafka
427
427
  @wrapped_kafka.committed(tpl)
428
428
  end
429
429
 
430
+ # Reads watermark offsets for given topic
431
+ #
432
+ # @param topic [String] topic name
433
+ # @param partition [Integer] partition number
434
+ # @return [Array<Integer, Integer>] watermark offsets (low, high)
435
+ def query_watermark_offsets(topic, partition)
436
+ @wrapped_kafka.query_watermark_offsets(topic, partition)
437
+ end
438
+
430
439
  private
431
440
 
432
441
  # When we cannot store an offset, it means we no longer own the partition
@@ -44,7 +44,7 @@ module Karafka
44
44
  # clusters can handle our requests.
45
45
  #
46
46
  # @param topic [String] topic name
47
- # @param partition [Partition]
47
+ # @param partition [Integer] partition number
48
48
  # @return [Array<Integer, Integer>] watermark offsets
49
49
  def query_watermark_offsets(topic, partition)
50
50
  l_config = @config.query_watermark_offsets
@@ -5,12 +5,13 @@ module Karafka
5
5
  # Base contract for all Karafka contracts
6
6
  class Base < ::Karafka::Core::Contractable::Contract
7
7
  # @param data [Hash] data for validation
8
+ # @param scope [Array<String>] nested scope if in use
8
9
  # @return [Boolean] true if all good
9
10
  # @raise [Errors::InvalidConfigurationError] invalid configuration error
10
11
  # @note We use contracts only in the config validation context, so no need to add support
11
12
  # for multiple error classes. It will be added when it will be needed.
12
- def validate!(data)
13
- super(data, Errors::InvalidConfigurationError)
13
+ def validate!(data, scope: [])
14
+ super(data, Errors::InvalidConfigurationError, scope: scope)
14
15
  end
15
16
  end
16
17
  end
@@ -53,7 +53,8 @@ module Karafka
53
53
  required(:kafka) { |val| val.is_a?(Hash) }
54
54
  required(:group_id) { |val| val.is_a?(String) && Contracts::TOPIC_REGEXP.match?(val) }
55
55
  required(:max_wait_time) { |val| val.is_a?(Integer) && val.positive? }
56
- required(:max_attempts) { |val| val.is_a?(Integer) && val.positive? }
56
+ required(:retry_backoff) { |val| val.is_a?(Integer) && val >= 100 }
57
+ required(:max_retries_duration) { |val| val.is_a?(Integer) && val >= 1_000 }
57
58
  end
58
59
 
59
60
  # We validate internals just to be sure, that they are present and working
@@ -30,6 +30,14 @@ module Karafka
30
30
  @block.call
31
31
  end
32
32
 
33
+ # Runs the requested code bypassing any time frequencies
34
+ # Useful when we have certain actions that usually need to run periodically but in some
35
+ # cases need to run asap
36
+ def call!
37
+ @last_called_at = monotonic_now
38
+ @block.call
39
+ end
40
+
33
41
  # Resets the runner, so next `#call` will run the underlying code
34
42
  def reset
35
43
  @last_called_at = monotonic_now - @interval
@@ -76,7 +76,7 @@ module Karafka
76
76
  consumer = job.executor.topic.consumer
77
77
  topic = job.executor.topic.name
78
78
  partition = job.executor.partition
79
- info "[#{job.id}] #{job_type} job for #{consumer} on #{topic}/#{partition} started"
79
+ info "[#{job.id}] #{job_type} job for #{consumer} on #{topic}-#{partition} started"
80
80
  end
81
81
 
82
82
  # Prints info about the fact that a given job has finished
@@ -91,7 +91,7 @@ module Karafka
91
91
  partition = job.executor.partition
92
92
  info <<~MSG.tr("\n", ' ').strip!
93
93
  [#{job.id}] #{job_type} job for #{consumer}
94
- on #{topic}/#{partition} finished in #{time} ms
94
+ on #{topic}-#{partition} finished in #{time} ms
95
95
  MSG
96
96
  end
97
97
 
@@ -108,7 +108,7 @@ module Karafka
108
108
 
109
109
  info <<~MSG.tr("\n", ' ').strip!
110
110
  [#{client.id}]
111
- Pausing on topic #{topic}/#{partition}
111
+ Pausing on topic #{topic}-#{partition}
112
112
  on #{offset ? "offset #{offset}" : 'the consecutive offset'}
113
113
  MSG
114
114
  end
@@ -122,7 +122,7 @@ module Karafka
122
122
  client = event[:caller]
123
123
 
124
124
  info <<~MSG.tr("\n", ' ').strip!
125
- [#{client.id}] Resuming on topic #{topic}/#{partition}
125
+ [#{client.id}] Resuming on topic #{topic}-#{partition}
126
126
  MSG
127
127
  end
128
128
 
@@ -138,7 +138,7 @@ module Karafka
138
138
 
139
139
  info <<~MSG.tr("\n", ' ').strip!
140
140
  [#{consumer.id}] Retrying of #{consumer.class} after #{timeout} ms
141
- on topic #{topic}/#{partition} from offset #{offset}
141
+ on topic #{topic}-#{partition} from offset #{offset}
142
142
  MSG
143
143
  end
144
144
 
@@ -153,7 +153,7 @@ module Karafka
153
153
 
154
154
  info <<~MSG.tr("\n", ' ').strip!
155
155
  [#{consumer.id}] Seeking from #{consumer.class}
156
- on topic #{topic}/#{partition} to offset #{seek_offset}
156
+ on topic #{topic}-#{partition} to offset #{seek_offset}
157
157
  MSG
158
158
  end
159
159
 
@@ -233,7 +233,7 @@ module Karafka
233
233
  info "#{group_prefix}: No partitions revoked"
234
234
  else
235
235
  revoked_partitions.each do |topic, partitions|
236
- info "#{group_prefix}: Partition(s) #{partitions.join(', ')} of #{topic} revoked"
236
+ info "#{group_prefix}: #{topic}-[#{partitions.join(',')}] revoked"
237
237
  end
238
238
  end
239
239
  end
@@ -251,7 +251,7 @@ module Karafka
251
251
  info "#{group_prefix}: No partitions assigned"
252
252
  else
253
253
  assigned_partitions.each do |topic, partitions|
254
- info "#{group_prefix}: Partition(s) #{partitions.join(', ')} of #{topic} assigned"
254
+ info "#{group_prefix}: #{topic}-[#{partitions.join(',')}] assigned"
255
255
  end
256
256
  end
257
257
  end
@@ -269,7 +269,7 @@ module Karafka
269
269
 
270
270
  info <<~MSG.tr("\n", ' ').strip!
271
271
  [#{consumer.id}] Dispatched message #{offset}
272
- from #{topic}/#{partition}
272
+ from #{topic}-#{partition}
273
273
  to DLQ topic: #{dlq_topic}
274
274
  MSG
275
275
  end
@@ -288,7 +288,7 @@ module Karafka
288
288
  info <<~MSG.tr("\n", ' ').strip!
289
289
  [#{consumer.id}] Throttled and will resume
290
290
  from message #{offset}
291
- on #{topic}/#{partition}
291
+ on #{topic}-#{partition}
292
292
  MSG
293
293
  end
294
294
 
@@ -303,7 +303,7 @@ module Karafka
303
303
 
304
304
  info <<~MSG.tr("\n", ' ').strip!
305
305
  [#{consumer.id}] Post-filtering seeking to message #{offset}
306
- on #{topic}/#{partition}
306
+ on #{topic}-#{partition}
307
307
  MSG
308
308
  end
309
309
 
@@ -8,11 +8,12 @@ module Karafka
8
8
  # Namespace for instrumentation related with Kubernetes
9
9
  module Kubernetes
10
10
  # Base Kubernetes Listener providing basic HTTP server capabilities to respond with health
11
+ # statuses
11
12
  class BaseListener
12
13
  include ::Karafka::Core::Helpers::Time
13
14
 
14
15
  # All good with Karafka
15
- OK_CODE = '204 No Content'
16
+ OK_CODE = '200 OK'
16
17
 
17
18
  # Some timeouts, fail
18
19
  FAIL_CODE = '500 Internal Server Error'
@@ -38,11 +39,15 @@ module Karafka
38
39
 
39
40
  # Responds to a HTTP request with the process liveness status
40
41
  def respond
42
+ body = JSON.generate(status_body)
43
+
41
44
  client = @server.accept
42
45
  client.gets
43
46
  client.print "HTTP/1.1 #{healthy? ? OK_CODE : FAIL_CODE}\r\n"
44
- client.print "Content-Type: text/plain\r\n"
47
+ client.print "Content-Type: application/json\r\n"
48
+ client.print "Content-Length: #{body.bytesize}\r\n"
45
49
  client.print "\r\n"
50
+ client.print body
46
51
  client.close
47
52
 
48
53
  true
@@ -50,6 +55,16 @@ module Karafka
50
55
  !@server.closed?
51
56
  end
52
57
 
58
+ # @return [Hash] hash that will be the response body
59
+ def status_body
60
+ {
61
+ status: healthy? ? 'healthy' : 'unhealthy',
62
+ timestamp: Time.now.to_i,
63
+ port: @port,
64
+ process_id: ::Process.pid
65
+ }
66
+ end
67
+
53
68
  # Starts background thread with micro-http monitoring
54
69
  def start
55
70
  @server = TCPServer.new(*[@hostname, @port].compact)