karafka 2.0.22 → 2.0.24

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +2 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +39 -1
  6. data/Gemfile.lock +9 -9
  7. data/README.md +3 -3
  8. data/config/{errors.yml → locales/errors.yml} +1 -1
  9. data/config/locales/pro_errors.yml +18 -0
  10. data/docker-compose.yml +3 -0
  11. data/karafka.gemspec +2 -3
  12. data/lib/karafka/active_job/job_options_contract.rb +1 -1
  13. data/lib/karafka/admin.rb +84 -6
  14. data/lib/karafka/app.rb +15 -4
  15. data/lib/karafka/base_consumer.rb +43 -1
  16. data/lib/karafka/connection/client.rb +4 -4
  17. data/lib/karafka/connection/listener.rb +1 -1
  18. data/lib/karafka/contracts/config.rb +1 -1
  19. data/lib/karafka/contracts/consumer_group.rb +1 -1
  20. data/lib/karafka/contracts/server_cli_options.rb +1 -1
  21. data/lib/karafka/contracts/topic.rb +1 -1
  22. data/lib/karafka/instrumentation/logger_listener.rb +32 -0
  23. data/lib/karafka/instrumentation/notifications.rb +3 -0
  24. data/lib/karafka/messages/message.rb +14 -2
  25. data/lib/karafka/messages/parser.rb +14 -0
  26. data/lib/karafka/pro/active_job/job_options_contract.rb +1 -1
  27. data/lib/karafka/pro/encryption/cipher.rb +58 -0
  28. data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
  29. data/lib/karafka/pro/encryption/errors.rb +24 -0
  30. data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
  31. data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
  32. data/lib/karafka/pro/encryption/setup/config.rb +48 -0
  33. data/lib/karafka/pro/encryption.rb +47 -0
  34. data/lib/karafka/pro/loader.rb +23 -2
  35. data/lib/karafka/pro/processing/strategies/aj_dlq_mom.rb +2 -2
  36. data/lib/karafka/pro/processing/strategies/aj_lrj_mom_vp.rb +2 -2
  37. data/lib/karafka/pro/processing/strategies/aj_mom_vp.rb +1 -1
  38. data/lib/karafka/pro/processing/strategies/default.rb +5 -1
  39. data/lib/karafka/pro/processing/strategies/dlq.rb +4 -2
  40. data/lib/karafka/pro/processing/strategies/dlq_lrj.rb +3 -1
  41. data/lib/karafka/pro/processing/strategies/dlq_lrj_mom.rb +1 -1
  42. data/lib/karafka/pro/processing/strategies/dlq_mom.rb +2 -2
  43. data/lib/karafka/pro/processing/strategies/lrj.rb +4 -2
  44. data/lib/karafka/pro/processing/strategies/lrj_mom.rb +2 -2
  45. data/lib/karafka/pro/processing/strategies/mom.rb +1 -1
  46. data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +2 -2
  47. data/lib/karafka/pro/routing/features/long_running_job/contract.rb +2 -2
  48. data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +2 -2
  49. data/lib/karafka/processing/coordinator.rb +15 -0
  50. data/lib/karafka/processing/executor.rb +1 -1
  51. data/lib/karafka/processing/strategies/aj_dlq_mom.rb +2 -2
  52. data/lib/karafka/processing/strategies/default.rb +7 -1
  53. data/lib/karafka/processing/strategies/dlq.rb +4 -2
  54. data/lib/karafka/processing/strategies/dlq_mom.rb +2 -2
  55. data/lib/karafka/processing/strategies/mom.rb +1 -1
  56. data/lib/karafka/processing/worker.rb +1 -1
  57. data/lib/karafka/railtie.rb +3 -0
  58. data/lib/karafka/routing/builder.rb +1 -1
  59. data/lib/karafka/routing/consumer_group.rb +3 -3
  60. data/lib/karafka/routing/features/active_job/contract.rb +1 -1
  61. data/lib/karafka/routing/features/dead_letter_queue/contract.rb +1 -1
  62. data/lib/karafka/routing/features/manual_offset_management/contract.rb +1 -1
  63. data/lib/karafka/server.rb +14 -14
  64. data/lib/karafka/setup/config.rb +15 -2
  65. data/lib/karafka/status.rb +27 -9
  66. data/lib/karafka/templates/karafka.rb.erb +1 -2
  67. data/lib/karafka/time_trackers/pause.rb +3 -1
  68. data/lib/karafka/version.rb +1 -1
  69. data.tar.gz.sig +0 -0
  70. metadata +17 -23
  71. metadata.gz.sig +2 -4
  72. data/lib/karafka/instrumentation.rb +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a096a44a1a2988ff394215b8d63f6f2e33b2a3e5100f68f75fcf33eeda4b490
4
- data.tar.gz: 2af0875b550f37ef9ea47b2a3011a3ed3e535adc3f6632ab10510d91bc69590c
3
+ metadata.gz: 25eb837923f78f1bf35402bf24469228d8f5af12090b2d406718a0077ce42598
4
+ data.tar.gz: 23520ecab430080061046e683c0dcb47316d1216d1ad9843894caefff76e24cc
5
5
  SHA512:
6
- metadata.gz: a81cf3305482e0e925f3a5aa48d978585766826e0990081d74f5f56bc535ae0a8a6be3a4ec986e6a7702d70d0d207ca9acc71b7aeb3909557b44518413ff9bc6
7
- data.tar.gz: 6c66140b258135367a120a5dac4eb8a08f4dc94e24fb3a714b32ec6ad909bc9f85bf5e160aefe5f5d307c9d554c6ecab61cbdebf315ce6953476303e9783122c
6
+ metadata.gz: 3e84c4c4dedd7a160ebcbd6cff53e0dfcf601a31eb19469d1a86eabb3f33507f3b1871c8f4489b743651118ed47e52925f12dba4fc67e462bd8be04d71dfa3bb
7
+ data.tar.gz: 8b9754f5566c5ef213c4803cfba2a4263553a5112e23fd12e42a963fd493f8f20c1409b12303628519ba7ef5bcf07aa09fabc6dd4ffc2a2114a6bad8f94cbc72
checksums.yaml.gz.sig CHANGED
Binary file
@@ -46,6 +46,7 @@ jobs:
46
46
  run: \curl -sSL https://api.coditsu.io/run/ci | bash
47
47
 
48
48
  specs:
49
+ timeout-minutes: 30
49
50
  runs-on: ubuntu-latest
50
51
  needs: diffend
51
52
  strategy:
@@ -84,6 +85,7 @@ jobs:
84
85
  run: bin/rspecs
85
86
 
86
87
  integrations:
88
+ timeout-minutes: 30
87
89
  runs-on: ubuntu-latest
88
90
  needs: diffend
89
91
  strategy:
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.2
1
+ 3.1.3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Karafka framework changelog
2
2
 
3
+ ## 2.0.24 (2022-12-19)
4
+ - **[Feature]** Provide out of the box encryption support for Pro.
5
+ - [Improvement] Add instrumentation upon `#pause`.
6
+ - [Improvement] Add instrumentation upon retries.
7
+ - [Improvement] Assign `#id` to consumers similar to other entities for ease of debugging.
8
+ - [Improvement] Add retries and pausing to the default `LoggerListener`.
9
+ - [Improvement] Introduce a new final `terminated` state that will kick in prior to exit but after all the instrumentation and other things are done.
10
+ - [Improvement] Ensure that state transitions are thread-safe and ensure state transitions can occur in one direction.
11
+ - [Improvement] Optimize status methods proxying to `Karafka::App`.
12
+ - [Improvement] Allow for easier state usage by introducing explicit `#to_s` for reporting.
13
+ - [Improvement] Change auto-generated id from `SecureRandom#uuid` to `SecureRandom#hex(6)`
14
+ - [Improvement] Emit statistic every 5 seconds by default.
15
+ - [Improvement] Introduce general messages parser that can be swapped when needed.
16
+ - [Fix] Do not trigger code reloading when `consumer_persistence` is enabled.
17
+ - [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.
18
+
19
+ ### Upgrade notes
20
+
21
+ 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`:
22
+
23
+ ```ruby
24
+ class KarafkaApp < Karafka::App
25
+ setup do |config|
26
+ # Other settings...
27
+ config.kafka = {
28
+ 'statistics.interval.ms': 0
29
+ }
30
+ end
31
+ end
32
+ ```
33
+
34
+ ## 2.0.23 (2022-12-07)
35
+ - [Maintenance] Align with `waterdrop` and `karafka-core`
36
+ - [Improvement] Provide `Admin#read_topic` API to get topic data without subscribing.
37
+ - [Improvement] Upon an end user `#pause`, do not commit the offset in automatic offset management mode. This will prevent from a scenario where pause is needed but during it a rebalance occurs and a different assigned process starts not from the pause location but from the automatic offset that may be different. This still allows for using the `#mark_as_consumed`.
38
+ - [Fix] Fix a scenario where manual `#pause` would be overwritten by a resume initiated by the strategy.
39
+ - [Fix] Fix a scenario where manual `#pause` in LRJ would cause infinite pause.
40
+
3
41
  ## 2.0.22 (2022-12-02)
4
42
  - [Improvement] Load Pro components upon Karafka require so they can be altered prior to setup.
5
43
  - [Improvement] Do not run LRJ jobs that were added to the jobs queue but were revoked meanwhile.
@@ -418,7 +456,7 @@ There are several things in the plan already for 2.1 and beyond, including a web
418
456
  - Small integration specs refactoring + specs for pausing scenarios
419
457
 
420
458
  ## 2.0.0-alpha6 (2022-04-17)
421
- - Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
459
+ - Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
422
460
  - Fix an issue with parallel pristine specs colliding with each other during `bundle install` (#820)
423
461
  - Replace `consumer.consume` with `consumer.consumed` event to match the behaviour
424
462
  - Make sure, that offset committing happens before the `consumer.consumed` event is propagated
data/Gemfile.lock CHANGED
@@ -1,11 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.0.22)
5
- karafka-core (>= 2.0.4, < 3.0.0)
6
- rdkafka (>= 0.12)
4
+ karafka (2.0.24)
5
+ karafka-core (>= 2.0.7, < 3.0.0)
7
6
  thor (>= 0.20)
8
- waterdrop (>= 2.4.1, < 3.0.0)
7
+ waterdrop (>= 2.4.7, < 3.0.0)
9
8
  zeitwerk (~> 2.3)
10
9
 
11
10
  GEM
@@ -30,8 +29,9 @@ GEM
30
29
  activesupport (>= 5.0)
31
30
  i18n (1.12.0)
32
31
  concurrent-ruby (~> 1.0)
33
- karafka-core (2.0.4)
32
+ karafka-core (2.0.7)
34
33
  concurrent-ruby (>= 1.1)
34
+ rdkafka (>= 0.12)
35
35
  mini_portile2 (2.8.0)
36
36
  minitest (5.16.3)
37
37
  rake (13.0.6)
@@ -48,7 +48,7 @@ GEM
48
48
  rspec-expectations (3.12.0)
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)
@@ -61,13 +61,13 @@ GEM
61
61
  thor (1.2.1)
62
62
  tzinfo (2.0.5)
63
63
  concurrent-ruby (~> 1.0)
64
- waterdrop (2.4.2)
65
- karafka-core (>= 2.0.2, < 3.0.0)
66
- rdkafka (>= 0.10)
64
+ waterdrop (2.4.7)
65
+ karafka-core (>= 2.0.7, < 3.0.0)
67
66
  zeitwerk (~> 2.3)
68
67
  zeitwerk (2.6.6)
69
68
 
70
69
  PLATFORMS
70
+ x86_64-darwin-21
71
71
  x86_64-linux
72
72
 
73
73
  DEPENDENCIES
data/README.md CHANGED
@@ -72,10 +72,10 @@ Karafka.producer.produce_sync(topic: 'example', payload: { 'ping' => 'pong' }.to
72
72
  ```bash
73
73
  bundle exec karafka server
74
74
 
75
- [7616dc24-505a-417f-b87b-6bf8fc2d98c5] Polled 1 message in 1000ms
76
- [dcf3a8d8-0bd9-433a-8f63-b70a0cdb0732] Consume job for ExampleConsumer on example started
75
+ [86d47f0b92f7] Polled 1 message in 1000ms
76
+ [3732873c8a74] Consume job for ExampleConsumer on example started
77
77
  {"ping"=>"pong"}
78
- [dcf3a8d8-0bd9-433a-8f63-b70a0cdb0732] Consume job for ExampleConsumer on example finished in 0ms
78
+ [3732873c8a74] Consume job for ExampleConsumer on example finished in 0ms
79
79
  ```
80
80
 
81
81
  ## Want to Upgrade? LGPL is not for you? Want to help?
@@ -63,7 +63,7 @@ en:
63
63
 
64
64
  pro_topic:
65
65
  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
66
+ virtual_partitions.max_partitions_format: needs to be equal or more than 1
67
67
  manual_offset_management_not_with_virtual_partitions: cannot be used together with Virtual Partitions
68
68
  long_running_job.active_format: needs to be either true or false
69
69
  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
@@ -21,10 +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.4', '< 3.0.0'
25
- spec.add_dependency 'rdkafka', '>= 0.12'
24
+ spec.add_dependency 'karafka-core', '>= 2.0.7', '< 3.0.0'
26
25
  spec.add_dependency 'thor', '>= 0.20'
27
- spec.add_dependency 'waterdrop', '>= 2.4.1', '< 3.0.0'
26
+ spec.add_dependency 'waterdrop', '>= 2.4.7', '< 3.0.0'
28
27
  spec.add_dependency 'zeitwerk', '~> 2.3'
29
28
 
30
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
@@ -9,7 +9,70 @@ module Karafka
9
9
  # @note It always uses the primary defined cluster and does not support multi-cluster work.
10
10
  # If you need this, just replace the cluster info for the time you use this
11
11
  module Admin
12
+ # A fake admin topic representation that we use for messages fetched using this API
13
+ # We cannot use the topics directly because we may want to request data from topics that we
14
+ # do not have in the routing
15
+ Topic = Struct.new(:name, :deserializer)
16
+
17
+ # Defaults for config
18
+ CONFIG_DEFAULTS = {
19
+ 'group.id': 'karafka_admin',
20
+ # We want to know when there is no more data not to end up with an endless loop
21
+ 'enable.partition.eof': true,
22
+ 'statistics.interval.ms': 0
23
+ }.freeze
24
+
25
+ private_constant :Topic, :CONFIG_DEFAULTS
26
+
12
27
  class << self
28
+ # Allows us to read messages from the topic
29
+ #
30
+ # @param name [String, Symbol] topic name
31
+ # @param partition [Integer] partition
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
35
+ #
36
+ # @return [Array<Karafka::Messages::Message>] array with messages
37
+ def read_topic(name, partition, count, offset = -1)
38
+ messages = []
39
+ tpl = Rdkafka::Consumer::TopicPartitionList.new
40
+
41
+ with_consumer do |consumer|
42
+ if offset.negative?
43
+ offsets = consumer.query_watermark_offsets(name, partition)
44
+ offset = offsets.last - count
45
+ end
46
+
47
+ offset = offset.negative? ? 0 : offset
48
+
49
+ tpl.add_topic_and_partitions_with_offsets(name, partition => offset)
50
+ consumer.assign(tpl)
51
+
52
+ # We should poll as long as we don't have all the messages that we need or as long as
53
+ # we do not read all the messages from the topic
54
+ loop do
55
+ break if messages.size >= count
56
+
57
+ message = consumer.poll(200)
58
+ messages << message if message
59
+ rescue Rdkafka::RdkafkaError => e
60
+ # End of partition
61
+ break if e.code == :partition_eof
62
+
63
+ raise e
64
+ end
65
+ end
66
+
67
+ messages.map do |message|
68
+ Messages::Builders::Message.call(
69
+ message,
70
+ Topic.new(name, Karafka::App.config.deserializer),
71
+ Time.now
72
+ )
73
+ end
74
+ end
75
+
13
76
  # Creates Kafka topic with given settings
14
77
  #
15
78
  # @param name [String] topic name
@@ -52,15 +115,30 @@ module Karafka
52
115
 
53
116
  # Creates admin instance and yields it. After usage it closes the admin instance
54
117
  def with_admin
55
- # Admin needs a producer config
56
- config = Karafka::Setup::AttributesMap.producer(Karafka::App.config.kafka.dup)
57
-
58
- admin = ::Rdkafka::Config.new(config).admin
59
- result = yield(admin)
60
- result
118
+ admin = config(:producer).admin
119
+ yield(admin)
61
120
  ensure
62
121
  admin&.close
63
122
  end
123
+
124
+ # Creates consumer instance and yields it. After usage it closes the consumer instance
125
+ def with_consumer
126
+ consumer = config(:consumer).consumer
127
+ yield(consumer)
128
+ ensure
129
+ consumer&.close
130
+ end
131
+
132
+ # @param type [Symbol] type of config we want
133
+ # @return [::Rdkafka::Config] rdkafka config
134
+ def config(type)
135
+ config_hash = Karafka::Setup::AttributesMap.public_send(
136
+ type,
137
+ Karafka::App.config.kafka.dup.merge(CONFIG_DEFAULTS)
138
+ )
139
+
140
+ ::Rdkafka::Config.new(config_hash)
141
+ end
64
142
  end
65
143
  end
66
144
  end
data/lib/karafka/app.rb CHANGED
@@ -35,10 +35,21 @@ module Karafka
35
35
  # Just a nicer name for the consumer groups
36
36
  alias routes consumer_groups
37
37
 
38
- Status.instance_methods(false).each do |delegated|
39
- define_method(delegated) do
40
- App.config.internal.status.send(delegated)
41
- end
38
+ # Allow for easier status management via `Karafka::App` by aliasing status methods here
39
+ Status::STATES.each do |state, transition|
40
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
41
+ def #{state}
42
+ App.config.internal.status.#{state}
43
+ end
44
+
45
+ def #{state}?
46
+ App.config.internal.status.#{state}?
47
+ end
48
+
49
+ def #{transition}
50
+ App.config.internal.status.#{transition}
51
+ end
52
+ RUBY
42
53
  end
43
54
 
44
55
  # 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
@@ -190,7 +197,10 @@ module Karafka
190
197
  # @param offset [Integer] offset from which we want to restart the processing
191
198
  # @param timeout [Integer, nil] how long in milliseconds do we want to pause or nil to use the
192
199
  # default exponential pausing strategy defined for retries
193
- def pause(offset, timeout = nil)
200
+ # @param manual_pause [Boolean] Flag to differentiate between user pause and system/strategy
201
+ # based pause. While they both pause in exactly the same way, the strategy application
202
+ # may need to differentiate between them.
203
+ def pause(offset, timeout = nil, manual_pause = true)
194
204
  timeout ? coordinator.pause_tracker.pause(timeout) : coordinator.pause_tracker.pause
195
205
 
196
206
  client.pause(
@@ -198,6 +208,20 @@ module Karafka
198
208
  messages.metadata.partition,
199
209
  offset
200
210
  )
211
+
212
+ # Indicate, that user took a manual action of pausing
213
+ coordinator.manual_pause if manual_pause
214
+
215
+ Karafka.monitor.instrument(
216
+ 'consumer.consuming.pause',
217
+ caller: self,
218
+ manual: manual_pause,
219
+ topic: messages.metadata.topic,
220
+ partition: messages.metadata.partition,
221
+ offset: offset,
222
+ timeout: coordinator.pause_tracker.current_timeout,
223
+ attempt: coordinator.pause_tracker.attempt
224
+ )
201
225
  end
202
226
 
203
227
  # Resumes processing of the current topic partition
@@ -226,5 +250,23 @@ module Karafka
226
250
  def revoked?
227
251
  coordinator.revoked?
228
252
  end
253
+
254
+ # Pauses the processing from the last offset to retry on given message
255
+ # @private
256
+ def retry_after_pause
257
+ pause(coordinator.seek_offset, nil, false)
258
+
259
+ # Instrumentation needs to run **after** `#pause` invocation because we rely on the states
260
+ # set by `#pause`
261
+ Karafka.monitor.instrument(
262
+ 'consumer.consuming.retry',
263
+ caller: self,
264
+ topic: messages.metadata.topic,
265
+ partition: messages.metadata.partition,
266
+ offset: coordinator.seek_offset,
267
+ timeout: coordinator.pause_tracker.current_timeout,
268
+ attempt: coordinator.pause_tracker.attempt
269
+ )
270
+ end
229
271
  end
230
272
  end
@@ -308,8 +308,8 @@ module Karafka
308
308
  @closed = true
309
309
 
310
310
  # Remove callbacks runners that were registered
311
- ::Karafka::Instrumentation.statistics_callbacks.delete(@subscription_group.id)
312
- ::Karafka::Instrumentation.error_callbacks.delete(@subscription_group.id)
311
+ ::Karafka::Core::Instrumentation.statistics_callbacks.delete(@subscription_group.id)
312
+ ::Karafka::Core::Instrumentation.error_callbacks.delete(@subscription_group.id)
313
313
 
314
314
  @kafka.close
315
315
  @buffer.clear
@@ -397,7 +397,7 @@ module Karafka
397
397
  @name = consumer.name
398
398
 
399
399
  # Register statistics runner for this particular type of callbacks
400
- ::Karafka::Instrumentation.statistics_callbacks.add(
400
+ ::Karafka::Core::Instrumentation.statistics_callbacks.add(
401
401
  @subscription_group.id,
402
402
  Instrumentation::Callbacks::Statistics.new(
403
403
  @subscription_group.id,
@@ -408,7 +408,7 @@ module Karafka
408
408
  )
409
409
 
410
410
  # Register error tracking callback
411
- ::Karafka::Instrumentation.error_callbacks.add(
411
+ ::Karafka::Core::Instrumentation.error_callbacks.add(
412
412
  @subscription_group.id,
413
413
  Instrumentation::Callbacks::Error.new(
414
414
  @subscription_group.id,
@@ -21,7 +21,7 @@ module Karafka
21
21
  def initialize(consumer_group_coordinator, subscription_group, jobs_queue)
22
22
  proc_config = ::Karafka::App.config.internal.processing
23
23
 
24
- @id = SecureRandom.uuid
24
+ @id = SecureRandom.hex(6)
25
25
  @consumer_group_coordinator = consumer_group_coordinator
26
26
  @subscription_group = subscription_group
27
27
  @jobs_queue = jobs_queue
@@ -12,7 +12,7 @@ module Karafka
12
12
  configure do |config|
13
13
  config.error_messages = YAML.safe_load(
14
14
  File.read(
15
- File.join(Karafka.gem_root, 'config', 'errors.yml')
15
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
16
16
  )
17
17
  ).fetch('en').fetch('validations').fetch('config')
18
18
  end
@@ -7,7 +7,7 @@ module Karafka
7
7
  configure do |config|
8
8
  config.error_messages = YAML.safe_load(
9
9
  File.read(
10
- File.join(Karafka.gem_root, 'config', 'errors.yml')
10
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
11
11
  )
12
12
  ).fetch('en').fetch('validations').fetch('consumer_group')
13
13
  end
@@ -7,7 +7,7 @@ module Karafka
7
7
  configure do |config|
8
8
  config.error_messages = YAML.safe_load(
9
9
  File.read(
10
- File.join(Karafka.gem_root, 'config', 'errors.yml')
10
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
11
11
  )
12
12
  ).fetch('en').fetch('validations').fetch('server_cli_options')
13
13
  end
@@ -7,7 +7,7 @@ module Karafka
7
7
  configure do |config|
8
8
  config.error_messages = YAML.safe_load(
9
9
  File.read(
10
- File.join(Karafka.gem_root, 'config', 'errors.yml')
10
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
11
11
  )
12
12
  ).fetch('en').fetch('validations').fetch('topic')
13
13
  end
@@ -63,6 +63,38 @@ module Karafka
63
63
  info "[#{job.id}] #{job_type} job for #{consumer} on #{topic} finished in #{time}ms"
64
64
  end
65
65
 
66
+ # Prints info about a pause occurrence. Irrelevant if user or system initiated.
67
+ #
68
+ # @param event [Karafka::Core::Monitoring::Event] event details including payload
69
+ def on_consumer_consuming_pause(event)
70
+ topic = event[:topic]
71
+ partition = event[:partition]
72
+ offset = event[:offset]
73
+ consumer = event[:caller]
74
+ timeout = event[:timeout]
75
+
76
+ info <<~MSG.tr("\n", ' ').strip!
77
+ [#{consumer.id}] Pausing partition #{partition} of topic #{topic}
78
+ on offset #{offset} for #{timeout} ms.
79
+ MSG
80
+ end
81
+
82
+ # Prints info about retry of processing after an error
83
+ #
84
+ # @param event [Karafka::Core::Monitoring::Event] event details including payload
85
+ def on_consumer_consuming_retry(event)
86
+ topic = event[:topic]
87
+ partition = event[:partition]
88
+ offset = event[:offset]
89
+ consumer = event[:caller]
90
+ timeout = event[:timeout]
91
+
92
+ info <<~MSG.tr("\n", ' ').strip!
93
+ [#{consumer.id}] Retrying of #{consumer.class} after #{timeout} ms
94
+ on partition #{partition} of topic #{topic} from offset #{offset}
95
+ MSG
96
+ end
97
+
66
98
  # Logs info about system signals that Karafka received and prints backtrace for threads in
67
99
  # case of ttin
68
100
  #
@@ -22,8 +22,11 @@ module Karafka
22
22
  app.quieting
23
23
  app.stopping
24
24
  app.stopped
25
+ app.terminated
25
26
 
26
27
  consumer.consumed
28
+ consumer.consuming.pause
29
+ consumer.consuming.retry
27
30
  consumer.revoked
28
31
  consumer.shutdown
29
32
 
@@ -9,7 +9,19 @@ module Karafka
9
9
  class Message
10
10
  extend Forwardable
11
11
 
12
- attr_reader :raw_payload, :metadata
12
+ class << self
13
+ # @return [Object] general parser
14
+ # @note We cache it here for performance reasons. It is 2.5x times faster than getting it
15
+ # via the config chain.
16
+ def parser
17
+ @parser ||= App.config.internal.messages.parser
18
+ end
19
+ end
20
+
21
+ attr_reader :metadata
22
+ # raw payload needs to be mutable as we want to have option to change it in the parser
23
+ # prior to the final deserialization
24
+ attr_accessor :raw_payload
13
25
 
14
26
  def_delegators :metadata, *Metadata.members
15
27
 
@@ -42,7 +54,7 @@ module Karafka
42
54
 
43
55
  # @return [Object] deserialized data
44
56
  def deserialize
45
- metadata.deserializer.call(self)
57
+ self.class.parser.call(self)
46
58
  end
47
59
  end
48
60
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Messages
5
+ # Default message parser. The only thing it does, is calling the deserializer
6
+ class Parser
7
+ # @param message [::Karafka::Messages::Message]
8
+ # @return [Object] deserialized payload
9
+ def call(message)
10
+ message.metadata.deserializer.call(message)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -20,7 +20,7 @@ module Karafka
20
20
  configure do |config|
21
21
  config.error_messages = YAML.safe_load(
22
22
  File.read(
23
- File.join(Karafka.gem_root, 'config', 'errors.yml')
23
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
24
24
  )
25
25
  ).fetch('en').fetch('validations').fetch('job_options')
26
26
  end