sbmt-kafka_consumer 3.5.1 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 872a886b138e7956051e347be06915f7c9153abd50076ae2ae77e0fc83672e48
4
- data.tar.gz: 651678b88fe6f8fde83ec4c086dc7bbfcc5edd5ff1dfd214b151bca76f8b3783
3
+ metadata.gz: 3acb66e582aa1dc634195368a85f0a9fba8857f1fe319614b7caa05f25ea1614
4
+ data.tar.gz: 393db4b5c06f1b587aa3c1cd2d61b6dc13b99eeb102b52aa359e865e2e7b78dd
5
5
  SHA512:
6
- metadata.gz: 2b60b4a2a5f84f9e3f6bc7602e352d14a373f49c9bab3d30784259e8a41e01e4d5d4324257a283cec6f3a23169ff47824fbf5c702acab891214c1ba806f2da90
7
- data.tar.gz: 2ad55a703d2a6e264d1ceca17335d0a9a6c38250eba00d7c4b8dc4848df1a83442e6c6de1e59916be7ac0de9f3d99114b3c1b0fc0dda24501b8f05d38c867b8f
6
+ metadata.gz: 25c52294c777720e106a21e73eb35dd0c06dd1425ad2f0ab0286e046498506e8e7ebf7ba0032159abfd38ef4145d3746fea2dd6d1966445c006d220dc7018e92
7
+ data.tar.gz: bf2364f20485662e310ba80b45e6c6169d6966c30dc37ce49e60b071831e20a6667eccf747156de26751b2d9cd3320beebfa822cd8c883cf533dd5e950eab9e4
data/.rubocop.yml CHANGED
@@ -33,3 +33,7 @@ Style/SingleLineMethods:
33
33
 
34
34
  Style/EmptyMethod:
35
35
  Enabled: false
36
+
37
+ Rails/Exit:
38
+ Exclude:
39
+ - spec/rails_helper.rb
data/CHANGELOG.md CHANGED
@@ -13,6 +13,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
13
13
 
14
14
  ### Fixed
15
15
 
16
+ ## [3.6.0] - 2025-12-02
17
+
18
+ ### Added
19
+
20
+ - Allow middleware configuration for all consumers
21
+ - Added option `batch_midlewares` to add middleware before message batch processing
22
+
23
+ ## [3.5.2] - 2025-07-21
24
+
25
+ ### Changed
26
+
27
+ - Add Rack 3 support: unfreeze headers hash in probes response.
28
+
16
29
  ## [3.5.1] - 2025-04-18
17
30
 
18
31
  ### Fixed
data/README.md CHANGED
@@ -185,10 +185,12 @@ consumer_groups:
185
185
 
186
186
  - `skip_on_error` - optional, default false, omit consumer errors in message processing and commit the offset to Kafka
187
187
  - `middlewares` - optional, default [], type String, add middleware before message processing
188
+ - `batch_middlewares` - optional, default [], type String, add middleware before batch message processing
188
189
 
189
190
  ```yaml
190
191
  init_attrs:
191
192
  middlewares: ['SomeMiddleware']
193
+ batch_middlewares: ['SomeBatchMiddleware']
192
194
  ```
193
195
 
194
196
  ```ruby
@@ -197,9 +199,16 @@ class SomeMiddleware
197
199
  yield if message.payload.id.to_i % 2 == 0
198
200
  end
199
201
  end
202
+
203
+ class SomeBatchMiddleware
204
+ def call(message, consumer)
205
+ yield
206
+ end
207
+ end
200
208
  ```
201
209
  __CAUTION__:
202
- - ⚠️ `yield` is mandatory for all middleware, as it returns control to the `process_message` method.
210
+ - ⚠️ `yield` is mandatory for all middleware, as it returns control to the `process_message/process_batch` method.
211
+ - ⚠️ all middlewares must take either 1 or 2 parameters.
203
212
 
204
213
  #### `consumer.init_attrs` options for `InboxConsumer`
205
214
 
@@ -207,10 +216,12 @@ __CAUTION__:
207
216
  - `event_name` - optional, default nil, used when the inbox item keep several event types
208
217
  - `skip_on_error` - optional, default false, omit consumer errors in message processing and commit the offset to Kafka
209
218
  - `middlewares` - optional, default [], type String, add middleware before message processing
219
+ - `batch_middlewares` - optional, default [], type String, add middleware before batch message processing
210
220
 
211
221
  ```yaml
212
222
  init_attrs:
213
223
  middlewares: ['SomeMiddleware']
224
+ batch_middlewares: ['SomeBatchMiddleware']
214
225
  ```
215
226
 
216
227
  ```ruby
@@ -219,15 +230,40 @@ class SomeMiddleware
219
230
  yield if message.payload.id.to_i % 2 == 0
220
231
  end
221
232
  end
233
+
234
+ class SomeBatchMiddleware
235
+ def call(message, consumer)
236
+ yield
237
+ end
238
+ end
222
239
  ```
223
240
  __CAUTION__:
224
241
  - ⚠️ `yield` is mandatory for all middleware, as it returns control to the `process_message` method.
225
- - ⚠️ Doesn't work with `process_batch`.
242
+ - ⚠️ all middlewares must take either 1 or 2 parameters.
226
243
 
227
244
  #### `deserializer.init_attrs` options
228
245
 
229
246
  - `skip_decoding_error` — don't raise an exception when cannot deserialize the message
230
247
 
248
+ ### Middlewares for all consumersа
249
+
250
+ To add middleware for all consumers, define them in the configuration
251
+
252
+ ```ruby
253
+ # config/initializers/kafka_consumer.rb
254
+
255
+ Sbmt::KafkaConsumer.process_message_middlewares.push(
256
+ SomeMiddleware
257
+ )
258
+
259
+ Sbmt::KafkaConsumer.process_batch_middlewares(
260
+ SomeBatchMiddleware
261
+ )
262
+ ```
263
+ __CAUTION__:
264
+ - ⚠️ `yield` is mandatory for all middleware, as it returns control to the `process_message` method.
265
+ - ⚠️ all middlewares must take either 1 or 2 parameters.
266
+
231
267
  ### `probes` config section
232
268
 
233
269
  In Kubernetes, probes are mechanisms used to assess the health of your application running within a container.
@@ -5,8 +5,9 @@ module Sbmt
5
5
  class BaseConsumer < Karafka::BaseConsumer
6
6
  class_attribute :skip_on_error, instance_writer: false, default: false
7
7
  class_attribute :middlewares, instance_writer: false, default: []
8
+ class_attribute :batch_middlewares, instance_writer: false, default: []
8
9
 
9
- def self.consumer_klass(skip_on_error: nil, middlewares: nil)
10
+ def self.consumer_klass(skip_on_error: nil, middlewares: nil, batch_middlewares: nil)
10
11
  klass = Class.new(self) do
11
12
  def self.name
12
13
  superclass.name
@@ -16,6 +17,7 @@ module Sbmt
16
17
  # defaults are set in class_attribute definition
17
18
  klass.skip_on_error = skip_on_error if skip_on_error
18
19
  klass.middlewares = middlewares.map(&:constantize) if middlewares
20
+ klass.batch_middlewares = batch_middlewares.map(&:constantize) if batch_middlewares
19
21
  klass
20
22
  end
21
23
 
@@ -23,7 +25,7 @@ module Sbmt
23
25
  ::Rails.application.executor.wrap do
24
26
  if process_batch?
25
27
  with_batch_instrumentation(messages) do
26
- process_batch(messages)
28
+ do_process_batch(messages)
27
29
  mark_message(messages.last)
28
30
  end
29
31
  else
@@ -99,6 +101,11 @@ module Sbmt
99
101
  end
100
102
  end
101
103
 
104
+ def do_process_batch(messages)
105
+ middleware_list = process_batch_middlewares + batch_middlewares
106
+ call_middlewares(messages, middleware_list) { process_batch(messages) }
107
+ end
108
+
102
109
  def do_consume(message)
103
110
  log_message(message) if log_payload?
104
111
 
@@ -107,7 +114,8 @@ module Sbmt
107
114
  message.payload
108
115
 
109
116
  with_common_instrumentation("process_message", message) do
110
- call_middlewares(message, middlewares) { process_message(message) }
117
+ middleware_list = process_message_middlewares + middlewares
118
+ call_middlewares(message, middleware_list) { process_message(message) }
111
119
  end
112
120
 
113
121
  with_common_instrumentation("mark_as_consumed", message) do
@@ -147,7 +155,7 @@ module Sbmt
147
155
  message.payload || message.raw_payload
148
156
  end
149
157
 
150
- def call_middlewares(message, middlewares)
158
+ def call_middlewares(payload, middlewares)
151
159
  return yield if middlewares.empty?
152
160
 
153
161
  chain = middlewares.map { |middleware_class| middleware_class.new }
@@ -156,7 +164,13 @@ module Sbmt
156
164
  if chain.empty?
157
165
  yield
158
166
  else
159
- chain.shift.call(message, &traverse_chain)
167
+ middleware = chain.shift
168
+
169
+ if middleware.method(:call).arity == 2
170
+ middleware.call(payload, self, &traverse_chain)
171
+ else
172
+ middleware.call(payload, &traverse_chain)
173
+ end
160
174
  end
161
175
  end
162
176
  traverse_chain.call
@@ -174,6 +188,14 @@ module Sbmt
174
188
  @config ||= Sbmt::KafkaConsumer::Config.new
175
189
  end
176
190
 
191
+ def process_message_middlewares
192
+ @process_message_middlewares ||= config.process_message_middlewares
193
+ end
194
+
195
+ def process_batch_middlewares
196
+ @process_batch_middlewares ||= config.process_batch_middlewares
197
+ end
198
+
177
199
  def cooperative_sticky?
178
200
  config.partition_assignment_strategy == "cooperative-sticky"
179
201
  end
@@ -27,6 +27,7 @@ class Sbmt::KafkaConsumer::Config < Anyway::Config
27
27
  :pause_timeout, :pause_max_timeout, :pause_with_exponential_backoff,
28
28
  :max_wait_time, :shutdown_timeout, :partition_assignment_strategy,
29
29
  concurrency: 4, auth: {}, kafka: {}, consumer_groups: {}, probes: {}, metrics: {},
30
+ process_message_middlewares: [], process_batch_middlewares: [],
30
31
  deserializer_class: "::Sbmt::KafkaConsumer::Serialization::NullDeserializer",
31
32
  monitor_class: "::Sbmt::KafkaConsumer::Instrumentation::TracingMonitor",
32
33
  logger_class: "::Sbmt::KafkaConsumer::Logger",
@@ -38,6 +39,8 @@ class Sbmt::KafkaConsumer::Config < Anyway::Config
38
39
 
39
40
  on_load :validate_consumer_groups
40
41
  on_load :set_default_metrics_port
42
+ on_load :set_process_message_middlewares
43
+ on_load :set_process_batch_middlewares
41
44
 
42
45
  coerce_types client_id: :string,
43
46
  pause_timeout: :integer,
@@ -80,4 +83,12 @@ class Sbmt::KafkaConsumer::Config < Anyway::Config
80
83
  def set_default_metrics_port
81
84
  self.metrics = metrics.new(port: probes.port) unless metrics.port
82
85
  end
86
+
87
+ def set_process_message_middlewares
88
+ self.process_message_middlewares = Sbmt::KafkaConsumer.process_message_middlewares || []
89
+ end
90
+
91
+ def set_process_batch_middlewares
92
+ self.process_batch_middlewares = Sbmt::KafkaConsumer.process_batch_middlewares || []
93
+ end
83
94
  end
@@ -9,9 +9,9 @@ module Sbmt
9
9
  class_attribute :inbox_item_class, instance_writer: false, default: nil
10
10
  class_attribute :event_name, instance_writer: false, default: nil
11
11
 
12
- def self.consumer_klass(inbox_item:, event_name: nil, skip_on_error: nil, name: nil, middlewares: nil)
12
+ def self.consumer_klass(inbox_item:, event_name: nil, skip_on_error: nil, name: nil, middlewares: nil, batch_middlewares: nil)
13
13
  # defaults are set in class_attribute definition
14
- klass = super(skip_on_error: skip_on_error, middlewares: middlewares)
14
+ klass = super(skip_on_error: skip_on_error, middlewares: middlewares, batch_middlewares: batch_middlewares)
15
15
  klass.inbox_item_class = inbox_item.constantize
16
16
  klass.event_name = event_name if event_name
17
17
  klass
@@ -5,6 +5,7 @@ module Sbmt
5
5
  module Instrumentation
6
6
  class LoggerListener < Karafka::Instrumentation::LoggerListener
7
7
  include ListenerHelper
8
+
8
9
  CUSTOM_ERROR_TYPES = %w[consumer.base.consume_one consumer.inbox.consume_one].freeze
9
10
  VALID_LOG_LEVELS = %i[error warn].freeze
10
11
 
@@ -4,7 +4,7 @@ module Sbmt
4
4
  module KafkaConsumer
5
5
  module Probes
6
6
  module Probe
7
- HEADERS = {"Content-Type" => "application/json"}.freeze
7
+ HEADERS = {"Content-Type" => "application/json"}
8
8
 
9
9
  def call(env)
10
10
  with_error_handler { probe(env) }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sbmt
4
4
  module KafkaConsumer
5
- VERSION = "3.5.1"
5
+ VERSION = "3.6.0"
6
6
  end
7
7
  end
@@ -26,6 +26,14 @@ module Sbmt
26
26
  def logger
27
27
  @logger ||= Rails.logger
28
28
  end
29
+
30
+ def process_message_middlewares
31
+ @process_message_middlewares ||= []
32
+ end
33
+
34
+ def process_batch_middlewares
35
+ @process_batch_middlewares ||= []
36
+ end
29
37
  end
30
38
 
31
39
  class Error < StandardError; end
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
 
34
34
  spec.add_dependency "rails", ">= 6.1"
35
35
  spec.add_dependency "zeitwerk", "~> 2.3"
36
- spec.add_dependency "karafka", "~> 2.4"
36
+ spec.add_dependency "karafka", "~> 2.4.0"
37
37
  spec.add_dependency "yabeda", ">= 0.11"
38
38
  spec.add_dependency "anyway_config", ">= 2.4.0"
39
39
  spec.add_dependency "thor"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sbmt-kafka_consumer
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.1
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuper Ruby-Platform Team
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-04-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -44,14 +43,14 @@ dependencies:
44
43
  requirements:
45
44
  - - "~>"
46
45
  - !ruby/object:Gem::Version
47
- version: '2.4'
46
+ version: 2.4.0
48
47
  type: :runtime
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - "~>"
53
52
  - !ruby/object:Gem::Version
54
- version: '2.4'
53
+ version: 2.4.0
55
54
  - !ruby/object:Gem::Dependency
56
55
  name: yabeda
57
56
  requirement: !ruby/object:Gem::Requirement
@@ -460,7 +459,6 @@ dependencies:
460
459
  version: '0'
461
460
  description: This gem is used for consuming Kafka messages. It represents a wrapper
462
461
  over Karafka gem and is recommended for using as a transport with sbmt-outbox
463
- email:
464
462
  executables:
465
463
  - kafka_consumer
466
464
  extensions: []
@@ -550,7 +548,6 @@ metadata:
550
548
  source_code_uri: https://github.com/Kuper-Tech/sbmt-kafka_consumer
551
549
  changelog_uri: https://github.com/Kuper-Tech/sbmt-kafka_consumer/blob/master/CHANGELOG.md
552
550
  rubygems_mfa_required: 'false'
553
- post_install_message:
554
551
  rdoc_options: []
555
552
  require_paths:
556
553
  - lib
@@ -565,8 +562,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
565
562
  - !ruby/object:Gem::Version
566
563
  version: '0'
567
564
  requirements: []
568
- rubygems_version: 3.5.21
569
- signing_key:
565
+ rubygems_version: 3.6.9
570
566
  specification_version: 4
571
567
  summary: Ruby gem for consuming Kafka messages
572
568
  test_files: []