karafka 2.0.23 → 2.0.24

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 (68) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +2 -0
  4. data/CHANGELOG.md +32 -1
  5. data/Gemfile.lock +8 -7
  6. data/README.md +3 -3
  7. data/config/{errors.yml → locales/errors.yml} +1 -1
  8. data/config/locales/pro_errors.yml +18 -0
  9. data/docker-compose.yml +3 -0
  10. data/karafka.gemspec +2 -2
  11. data/lib/karafka/active_job/job_options_contract.rb +1 -1
  12. data/lib/karafka/admin.rb +2 -4
  13. data/lib/karafka/app.rb +15 -4
  14. data/lib/karafka/base_consumer.rb +36 -0
  15. data/lib/karafka/connection/listener.rb +1 -1
  16. data/lib/karafka/contracts/config.rb +1 -1
  17. data/lib/karafka/contracts/consumer_group.rb +1 -1
  18. data/lib/karafka/contracts/server_cli_options.rb +1 -1
  19. data/lib/karafka/contracts/topic.rb +1 -1
  20. data/lib/karafka/instrumentation/logger_listener.rb +32 -0
  21. data/lib/karafka/instrumentation/notifications.rb +3 -0
  22. data/lib/karafka/messages/message.rb +14 -2
  23. data/lib/karafka/messages/parser.rb +14 -0
  24. data/lib/karafka/pro/active_job/job_options_contract.rb +1 -1
  25. data/lib/karafka/pro/encryption/cipher.rb +58 -0
  26. data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
  27. data/lib/karafka/pro/encryption/errors.rb +24 -0
  28. data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
  29. data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
  30. data/lib/karafka/pro/encryption/setup/config.rb +48 -0
  31. data/lib/karafka/pro/encryption.rb +47 -0
  32. data/lib/karafka/pro/loader.rb +22 -1
  33. data/lib/karafka/pro/processing/strategies/aj_dlq_mom.rb +1 -1
  34. data/lib/karafka/pro/processing/strategies/aj_lrj_mom_vp.rb +1 -1
  35. data/lib/karafka/pro/processing/strategies/aj_mom_vp.rb +1 -1
  36. data/lib/karafka/pro/processing/strategies/default.rb +1 -1
  37. data/lib/karafka/pro/processing/strategies/dlq.rb +1 -1
  38. data/lib/karafka/pro/processing/strategies/dlq_lrj.rb +1 -1
  39. data/lib/karafka/pro/processing/strategies/dlq_lrj_mom.rb +1 -1
  40. data/lib/karafka/pro/processing/strategies/dlq_mom.rb +1 -1
  41. data/lib/karafka/pro/processing/strategies/lrj.rb +1 -1
  42. data/lib/karafka/pro/processing/strategies/lrj_mom.rb +1 -1
  43. data/lib/karafka/pro/processing/strategies/mom.rb +1 -1
  44. data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +2 -2
  45. data/lib/karafka/pro/routing/features/long_running_job/contract.rb +2 -2
  46. data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +2 -2
  47. data/lib/karafka/processing/executor.rb +1 -1
  48. data/lib/karafka/processing/strategies/aj_dlq_mom.rb +1 -1
  49. data/lib/karafka/processing/strategies/default.rb +1 -1
  50. data/lib/karafka/processing/strategies/dlq.rb +1 -1
  51. data/lib/karafka/processing/strategies/dlq_mom.rb +1 -1
  52. data/lib/karafka/processing/strategies/mom.rb +1 -1
  53. data/lib/karafka/processing/worker.rb +1 -1
  54. data/lib/karafka/railtie.rb +3 -0
  55. data/lib/karafka/routing/builder.rb +1 -1
  56. data/lib/karafka/routing/consumer_group.rb +3 -3
  57. data/lib/karafka/routing/features/active_job/contract.rb +1 -1
  58. data/lib/karafka/routing/features/dead_letter_queue/contract.rb +1 -1
  59. data/lib/karafka/routing/features/manual_offset_management/contract.rb +1 -1
  60. data/lib/karafka/server.rb +14 -14
  61. data/lib/karafka/setup/config.rb +15 -2
  62. data/lib/karafka/status.rb +27 -9
  63. data/lib/karafka/templates/karafka.rb.erb +1 -2
  64. data/lib/karafka/time_trackers/pause.rb +3 -1
  65. data/lib/karafka/version.rb +1 -1
  66. data.tar.gz.sig +0 -0
  67. metadata +16 -7
  68. 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: 25eb837923f78f1bf35402bf24469228d8f5af12090b2d406718a0077ce42598
4
+ data.tar.gz: 23520ecab430080061046e683c0dcb47316d1216d1ad9843894caefff76e24cc
5
5
  SHA512:
6
- metadata.gz: 8e1f3fc0a3c73035fdb38093eb0594c999d0a715752f41ed95079b1d8f2e89a5a0fa0abdf054272891e89ffa4dd8d010dd2225540d4480067d8bc0359bf2b7b8
7
- data.tar.gz: 7c1f3f958ef52682e46efb11e8a3ddcbe641ba85c62956fefb84ec6eacadbe9d28ee509bdfd61fcd73ea8f52b1ab867de4909990503024cc5c603788931264ca
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/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
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
+
3
34
  ## 2.0.23 (2022-12-07)
4
35
  - [Maintenance] Align with `waterdrop` and `karafka-core`
5
36
  - [Improvement] Provide `Admin#read_topic` API to get topic data without subscribing.
@@ -425,7 +456,7 @@ There are several things in the plan already for 2.1 and beyond, including a web
425
456
  - Small integration specs refactoring + specs for pausing scenarios
426
457
 
427
458
  ## 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)
459
+ - Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
429
460
  - Fix an issue with parallel pristine specs colliding with each other during `bundle install` (#820)
430
461
  - Replace `consumer.consume` with `consumer.consumed` event to match the behaviour
431
462
  - 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.24)
5
+ karafka-core (>= 2.0.7, < 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,7 +29,7 @@ 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.7)
33
33
  concurrent-ruby (>= 1.1)
34
34
  rdkafka (>= 0.12)
35
35
  mini_portile2 (2.8.0)
@@ -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,12 +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.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
+ x86_64-darwin-21
70
71
  x86_64-linux
71
72
 
72
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,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.7', '< 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
@@ -116,8 +116,7 @@ module Karafka
116
116
  # Creates admin instance and yields it. After usage it closes the admin instance
117
117
  def with_admin
118
118
  admin = config(:producer).admin
119
- result = yield(admin)
120
- result
119
+ yield(admin)
121
120
  ensure
122
121
  admin&.close
123
122
  end
@@ -125,8 +124,7 @@ module Karafka
125
124
  # Creates consumer instance and yields it. After usage it closes the consumer instance
126
125
  def with_consumer
127
126
  consumer = config(:consumer).consumer
128
- result = yield(consumer)
129
- result
127
+ yield(consumer)
130
128
  ensure
131
129
  consumer&.close
132
130
  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
@@ -204,6 +211,17 @@ module Karafka
204
211
 
205
212
  # Indicate, that user took a manual action of pausing
206
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
+ )
207
225
  end
208
226
 
209
227
  # Resumes processing of the current topic partition
@@ -232,5 +250,23 @@ module Karafka
232
250
  def revoked?
233
251
  coordinator.revoked?
234
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
235
271
  end
236
272
  end
@@ -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
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component under a commercial license.
4
+ # This Karafka component is NOT licensed under LGPL.
5
+ #
6
+ # All of the commercial components are present in the lib/karafka/pro directory of this
7
+ # repository and their usage requires commercial license agreement.
8
+ #
9
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
10
+ #
11
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
12
+ # your code to Maciej Mensfeld.
13
+
14
+ module Karafka
15
+ module Pro
16
+ module Encryption
17
+ # Cipher for encrypting and decrypting data
18
+ class Cipher
19
+ def initialize
20
+ @private_pems = {}
21
+ end
22
+
23
+ # Encrypts given string content with the public key
24
+ # @param content [String]
25
+ # @return [String]
26
+ def encrypt(content)
27
+ public_pem.public_encrypt(content)
28
+ end
29
+
30
+ # Decrypts provided content using `version` key
31
+ # @param version [String] encryption version
32
+ # @param content [String] encrypted content
33
+ # @return [String] decrypted content
34
+ def decrypt(version, content)
35
+ private_pem(version).private_decrypt(content)
36
+ end
37
+
38
+ private
39
+
40
+ # @return [::OpenSSL::PKey::RSA] rsa public key
41
+ def public_pem
42
+ @public_pem ||= ::OpenSSL::PKey::RSA.new(::Karafka::App.config.encryption.public_key)
43
+ end
44
+
45
+ # @param version [String] version for which we want to get the rsa key
46
+ # @return [::OpenSSL::PKey::RSA] rsa private key
47
+ def private_pem(version)
48
+ return @private_pems[version] if @private_pems.key?(version)
49
+
50
+ key_string = ::Karafka::App.config.encryption.private_keys[version]
51
+ key_string || raise(Errors::PrivateKeyNotFound, version)
52
+
53
+ @private_pems[version] = ::OpenSSL::PKey::RSA.new(key_string)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component under a commercial license.
4
+ # This Karafka component is NOT licensed under LGPL.
5
+ #
6
+ # All of the commercial components are present in the lib/karafka/pro directory of this
7
+ # repository and their usage requires commercial license agreement.
8
+ #
9
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
10
+ #
11
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
12
+ # your code to Maciej Mensfeld.
13
+
14
+ module Karafka
15
+ module Pro
16
+ module Encryption
17
+ # Encryption related contracts
18
+ module Contracts
19
+ # Makes sure, all the expected config is defined as it should be
20
+ class Config < ::Karafka::Contracts::Base
21
+ configure do |config|
22
+ config.error_messages = YAML.safe_load(
23
+ File.read(
24
+ File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
25
+ )
26
+ ).fetch('en').fetch('validations').fetch('config')
27
+ end
28
+
29
+ nested(:encryption) do
30
+ required(:active) { |val| [true, false].include?(val) }
31
+ required(:version) { |val| val.is_a?(String) && !val.empty? }
32
+ required(:public_key) { |val| val.is_a?(String) }
33
+
34
+ required(:private_keys) do |val|
35
+ val.is_a?(Hash) &&
36
+ val.keys.all? { |key| key.is_a?(String) } &&
37
+ val.values.all? { |key| key.is_a?(String) }
38
+ end
39
+ end
40
+
41
+ # Public key validation
42
+ virtual do |data, errors|
43
+ next unless errors.empty?
44
+ next unless data.fetch(:encryption).fetch(:active)
45
+
46
+ key = OpenSSL::PKey::RSA.new(data.fetch(:encryption).fetch(:public_key))
47
+
48
+ next unless key.private?
49
+
50
+ [[%i[encryption public_key], :needs_to_be_public]]
51
+ rescue OpenSSL::PKey::RSAError
52
+ [[%i[encryption public_key], :invalid]]
53
+ end
54
+
55
+ # Private keys validation
56
+ virtual do |data, errors|
57
+ next unless errors.empty?
58
+ next unless data.fetch(:encryption).fetch(:active)
59
+
60
+ private_keys = data.fetch(:encryption).fetch(:private_keys)
61
+
62
+ # Keys may be empty for production only envs
63
+ next if private_keys.empty?
64
+
65
+ keys = private_keys.each_value.map do |key|
66
+ OpenSSL::PKey::RSA.new(key)
67
+ end
68
+
69
+ next if keys.all?(&:private?)
70
+
71
+ [[%i[encryption private_keys], :need_to_be_private]]
72
+ rescue OpenSSL::PKey::RSAError
73
+ [[%i[encryption private_keys], :invalid]]
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component under a commercial license.
4
+ # This Karafka component is NOT licensed under LGPL.
5
+ #
6
+ # All of the commercial components are present in the lib/karafka/pro directory of this
7
+ # repository and their usage requires commercial license agreement.
8
+ #
9
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
10
+ #
11
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
12
+ # your code to Maciej Mensfeld.
13
+
14
+ module Karafka
15
+ module Pro
16
+ module Encryption
17
+ # Encryption related errors
18
+ module Errors
19
+ # Raised when we have encountered encryption key with version we do not have
20
+ PrivateKeyNotFound = Class.new(::Karafka::Errors::BaseError)
21
+ end
22
+ end
23
+ end
24
+ end