sbmt-kafka_consumer 2.8.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c5c4115c8a5cc26e7d4798244f8e3052f1b6abdddb11a4a96699a1e4f950659
4
- data.tar.gz: 7e6d290f5d0866f4e6ea0b424e07afd95770840d6a5a1324ee4d01b28c62b49d
3
+ metadata.gz: 7af7925acd4a61d131a66dd3cd290064ba11aac749ee64ca00841912bd038adc
4
+ data.tar.gz: 9cd1e1f789c6e4252adedf3dd3115f512c62240b75edadc38d1590e4a85b6000
5
5
  SHA512:
6
- metadata.gz: 4f07ac9ad6a9e65898e9d3a48ed86b0df7fdd52f38ea04b2e41aa0406351d314f0a5aa72e33334e9a619779564afb6105481d19ec2a27706adf197346af66776
7
- data.tar.gz: 1d65a2e194afade6fc2230bd9d031c457101f799342ea688be292a119ae0661754f4cbca62ce66d8257af2616faef675bdc638ef03059a4eaa9611f41de8a674
6
+ metadata.gz: 60dbd904786ddc419e9efbb0f02904c6035089a3b23fcb4035d41c85c8db303ae807138fa1e26c042688521c34a355f0314799e82addc73ef6308fc3fe6cad15
7
+ data.tar.gz: ca1d3af3c319695cbb33e11f9f66a384efb2c1884a387a233d5b197ae9ed5d3a3852c49662586a67ba15eb497ed325b92363cfe65ce8e086c6319b48dfb0569e
data/Appraisals CHANGED
@@ -3,10 +3,10 @@
3
3
  # See compatibility table at https://www.fastruby.io/blog/ruby/rails/versions/compatibility-table.html
4
4
 
5
5
  versions_map = {
6
- "6.0" => %w[2.7],
7
6
  "6.1" => %w[2.7 3.0],
8
7
  "7.0" => %w[3.1],
9
- "7.1" => %w[3.2, 3.3]
8
+ "7.1" => %w[3.2, 3.3],
9
+ "7.2" => %w[3.3]
10
10
  }
11
11
 
12
12
  current_ruby_version = RUBY_VERSION.split(".").first(2).join(".")
data/CHANGELOG.md CHANGED
@@ -13,11 +13,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
13
13
 
14
14
  ### Fixed
15
15
 
16
- ## [2.8.0] - 2024-09-09
16
+ ## [3.0.0] - 2024-09-04
17
+
18
+ ## BREAKING
19
+
20
+ - Drop support for Ruby 2.7
21
+ - Drop support for Rails 6.0
22
+ - Add support for Karafka 2.4
17
23
 
18
24
  ### Fixed
19
25
 
20
- - Refactor consumer class initialization
26
+ - Support consumer group mappers to support backward compatibility of consumer group naming
21
27
 
22
28
  ## [2.7.1] - 2024-08-01
23
29
 
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 SberMarket Tech
3
+ Copyright (c) 2024 Kuper Tech
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/sbmt-kafka_consumer.svg)](https://badge.fury.io/rb/sbmt-kafka_consumer)
2
- [![Build Status](https://github.com/SberMarket-Tech/sbmt-kafka_consumer/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/SberMarket-Tech/sbmt-kafka_consumer/actions?query=branch%3Amaster)
2
+ [![Build Status](https://github.com/Kuper-Tech/sbmt-kafka_consumer/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/Kuper-Tech/sbmt-kafka_consumer/actions?query=branch%3Amaster)
3
3
 
4
4
  # Sbmt-KafkaConsumer
5
5
 
6
- This gem is used to consume Kafka messages. It is a wrapper over the [Karafka](https://github.com/karafka/karafka) gem, and is recommended for use as a transport with the [sbmt-outbox](https://github.com/SberMarket-Tech/sbmt-outbox) gem.
6
+ This gem is used to consume Kafka messages. It is a wrapper over the [Karafka](https://github.com/karafka/karafka) gem, and is recommended for use as a transport with the [sbmt-outbox](https://github.com/Kuper-Tech/sbmt-outbox) gem.
7
7
 
8
8
  ## Installation
9
9
 
@@ -21,7 +21,7 @@ bundle install
21
21
 
22
22
  ## Demo
23
23
 
24
- Learn how to use this gem and how it works with Ruby on Rails at here https://github.com/SberMarket-Tech/outbox-example-apps
24
+ Learn how to use this gem and how it works with Ruby on Rails at here https://github.com/Kuper-Tech/outbox-example-apps
25
25
 
26
26
  ## Auto configuration
27
27
 
@@ -47,7 +47,7 @@ rails g kafka_consumer:consumer MaybeNamespaced::Name
47
47
 
48
48
  ### Inbox consumer
49
49
 
50
- To generate an Inbox consumer for use with gem [sbmt-outbox](https://github.com/SberMarket-Tech/sbmt-outbox), run the following command:
50
+ To generate an Inbox consumer for use with gem [sbmt-outbox](https://github.com/Kuper-Tech/sbmt-outbox), run the following command:
51
51
 
52
52
  ```shell
53
53
  rails g kafka_consumer:inbox_consumer MaybeNamespaced::Name some-consumer-group some-topic
data/dip.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  version: '7'
2
2
 
3
3
  environment:
4
- RUBY_VERSION: '3.2'
4
+ RUBY_VERSION: '3.3'
5
5
 
6
6
  compose:
7
7
  files:
@@ -35,14 +35,14 @@ interaction:
35
35
  subcommands:
36
36
  all:
37
37
  command: bundle exec appraisal rspec
38
- rails-6.0:
39
- command: bundle exec appraisal rails-6.0 rspec
40
38
  rails-6.1:
41
39
  command: bundle exec appraisal rails-6.1 rspec
42
40
  rails-7.0:
43
41
  command: bundle exec appraisal rails-7.0 rspec
44
42
  rails-7.1:
45
43
  command: bundle exec appraisal rails-7.1 rspec
44
+ rails-7.2:
45
+ command: bundle exec appraisal rails-7.2 rspec
46
46
 
47
47
  rubocop:
48
48
  description: Run Ruby linter
@@ -3,20 +3,15 @@
3
3
  module Sbmt
4
4
  module KafkaConsumer
5
5
  class BaseConsumer < Karafka::BaseConsumer
6
- class_attribute :skip_on_error, instance_writer: false, default: false
7
- class_attribute :middlewares, instance_writer: false, default: []
6
+ def self.consumer_klass(skip_on_error: false, middlewares: [])
7
+ Class.new(self) do
8
+ const_set(:SKIP_ON_ERROR, skip_on_error)
9
+ const_set(:MIDDLEWARES, middlewares.map(&:constantize))
8
10
 
9
- def self.consumer_klass(skip_on_error: nil, middlewares: nil)
10
- klass = Class.new(self) do
11
11
  def self.name
12
12
  superclass.name
13
13
  end
14
14
  end
15
-
16
- # defaults are set in class_attribute definition
17
- klass.skip_on_error = skip_on_error if skip_on_error
18
- klass.middlewares = middlewares.map(&:constantize) if middlewares
19
- klass
20
15
  end
21
16
 
22
17
  def consume
@@ -117,6 +112,14 @@ module Sbmt
117
112
  end
118
113
  end
119
114
 
115
+ def skip_on_error
116
+ self.class::SKIP_ON_ERROR
117
+ end
118
+
119
+ def middlewares
120
+ self.class::MIDDLEWARES
121
+ end
122
+
120
123
  # can be overridden in consumer to enable message logging
121
124
  def log_payload?
122
125
  false
@@ -6,10 +6,8 @@ class Sbmt::KafkaConsumer::ClientConfigurer
6
6
  Karafka::App.setup do |karafka_config|
7
7
  karafka_config.monitor = config.monitor_class.classify.constantize.new
8
8
  karafka_config.logger = Sbmt::KafkaConsumer.logger
9
- karafka_config.deserializer = config.deserializer_class.classify.constantize.new
10
9
 
11
10
  karafka_config.client_id = config.client_id
12
- karafka_config.consumer_mapper = config.consumer_mapper_class.classify.constantize.new
13
11
  karafka_config.kafka = config.to_kafka_options
14
12
 
15
13
  karafka_config.pause_timeout = config.pause_timeout * 1_000 if config.pause_timeout.present?
@@ -43,13 +41,16 @@ class Sbmt::KafkaConsumer::ClientConfigurer
43
41
 
44
42
  raise "No configured consumer groups found, exiting" if target_consumer_groups.blank?
45
43
 
44
+ consumer_mapper = config.consumer_mapper_class.classify.constantize.new
45
+
46
46
  # clear routes in case CLI runner tries to reconfigure them
47
47
  # but railtie initializer had already executed and did the same
48
48
  # otherwise we'll get duplicate routes error from sbmt-karafka internal config validation process
49
49
  Karafka::App.routes.clear
50
50
  Karafka::App.routes.draw do
51
51
  target_consumer_groups.each do |cg|
52
- consumer_group cg.name do
52
+ group_id = consumer_mapper.call(cg.name)
53
+ consumer_group group_id do
53
54
  cg.topics.each do |t|
54
55
  topic t.name do
55
56
  active t.active
@@ -66,7 +67,7 @@ class Sbmt::KafkaConsumer::ClientConfigurer
66
67
 
67
68
  def self.routes
68
69
  Karafka::App.routes.map do |cg|
69
- topics = cg.topics.map { |t| {name: t.name, deserializer: t.deserializer} }
70
+ topics = cg.topics.map { |t| {name: t.name, deserializer: t.deserializers.payload} }
70
71
  {group: cg.id, topics: topics}
71
72
  end
72
73
  end
@@ -6,20 +6,17 @@ module Sbmt
6
6
  IDEMPOTENCY_HEADER_NAME = "Idempotency-Key"
7
7
  DEFAULT_SOURCE = "KAFKA"
8
8
 
9
- class_attribute :inbox_item_class, instance_writer: false, default: nil
10
- class_attribute :event_name, instance_writer: false, default: nil
11
-
12
- def self.consumer_klass(inbox_item:, event_name: nil, skip_on_error: nil, name: nil, middlewares: nil)
13
- # defaults are set in class_attribute definition
14
- klass = super(skip_on_error: skip_on_error, middlewares: middlewares)
15
- klass.inbox_item_class = inbox_item.constantize
16
- klass.event_name = event_name if event_name
17
- klass
18
- end
19
-
20
- def initialize
21
- raise Sbmt::KafkaConsumer::Error, "inbox_item param is not set" if inbox_item_class.blank?
22
- super
9
+ def self.consumer_klass(inbox_item:, event_name: nil, skip_on_error: false, name: nil, middlewares: [])
10
+ Class.new(self) do
11
+ const_set(:INBOX_ITEM_CLASS_NAME, inbox_item)
12
+ const_set(:EVENT_NAME, event_name)
13
+ const_set(:SKIP_ON_ERROR, skip_on_error)
14
+ const_set(:MIDDLEWARES, middlewares.map(&:constantize))
15
+
16
+ def self.name
17
+ superclass.name
18
+ end
19
+ end
23
20
  end
24
21
 
25
22
  def extra_message_attrs(_message)
@@ -104,6 +101,14 @@ module Sbmt
104
101
  message.metadata.headers.fetch(IDEMPOTENCY_HEADER_NAME, nil).presence
105
102
  end
106
103
 
104
+ def inbox_item_class
105
+ @inbox_item_class ||= self.class::INBOX_ITEM_CLASS_NAME.constantize
106
+ end
107
+
108
+ def event_name
109
+ @event_name ||= self.class::EVENT_NAME
110
+ end
111
+
107
112
  def inbox_name
108
113
  inbox_item_class.box_name
109
114
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module KafkaConsumer
5
+ module Routing
6
+ module ConsumerMapper
7
+ class Base
8
+ # @param raw_consumer_group_name [String, Symbol] string or symbolized consumer group name
9
+ # @return [String] remapped final consumer group name
10
+ def call(raw_consumer_group_name)
11
+ raise "Implement #call in a subclass"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,7 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "consumer_mapper/base"
4
+
1
5
  module Sbmt
2
6
  module KafkaConsumer
3
7
  module Routing
4
- class KarafkaV1ConsumerMapper < Karafka::Routing::ConsumerMapper
8
+ class KarafkaV1ConsumerMapper < ConsumerMapper::Base
9
+ # karafka v1 consumer group name mapper
5
10
  def call(raw_consumer_group_name)
6
11
  client_id = ActiveSupport::Inflector.underscore(Karafka::App.config.client_id).tr("/", "_")
7
12
  "#{client_id}_#{raw_consumer_group_name}"
@@ -1,9 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "consumer_mapper/base"
4
+
1
5
  module Sbmt
2
6
  module KafkaConsumer
3
7
  module Routing
4
- # uses default karafka v2 mapper
5
- # exists just for naming consistency with KarafkaV1ConsumerMapper
6
- class KarafkaV2ConsumerMapper < Karafka::Routing::ConsumerMapper; end
8
+ # karafka v2 (before 2.4) consumer group name mapper
9
+ class KarafkaV2ConsumerMapper < ConsumerMapper::Base
10
+ def call(raw_consumer_group_name)
11
+ "#{Karafka::App.config.client_id}_#{raw_consumer_group_name}"
12
+ end
13
+ end
7
14
  end
8
15
  end
9
16
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  RSpec.shared_context "with sbmt karafka consumer" do
4
4
  subject(:consume_with_sbmt_karafka) do
5
- coordinator.increment
5
+ coordinator.increment(:consume)
6
6
  consumer.on_consume
7
7
  end
8
8
 
@@ -16,8 +16,9 @@ RSpec.shared_context "with sbmt karafka consumer" do
16
16
  let(:kafka_client) { instance_double(Karafka::Connection::Client) }
17
17
  let(:null_deserializer) { Sbmt::KafkaConsumer::Serialization::NullDeserializer.new }
18
18
 
19
- let(:consumer_class) { described_class.consumer_klass }
20
- let(:consumer) { build_consumer(consumer_class.new) }
19
+ let(:consumer) {
20
+ build_consumer(described_class.new)
21
+ }
21
22
 
22
23
  before {
23
24
  Sbmt::KafkaConsumer::ClientConfigurer.configure!
@@ -27,27 +28,27 @@ RSpec.shared_context "with sbmt karafka consumer" do
27
28
  }
28
29
 
29
30
  def publish_to_sbmt_karafka(raw_payload, opts = {})
30
- message = Karafka::Messages::Message.new(raw_payload, Karafka::Messages::Metadata.new(metadata_defaults.merge(opts)))
31
+ message = Karafka::Messages::Message.new(raw_payload, Karafka::Messages::Metadata.new(build_metadata_hash(opts)))
31
32
  consumer.messages = consumer_messages([message])
32
33
  end
33
34
 
34
35
  def publish_to_sbmt_karafka_batch(raw_payloads, opts = {})
35
36
  messages = raw_payloads.map do |p|
36
- Karafka::Messages::Message.new(p, Karafka::Messages::Metadata.new(metadata_defaults.merge(opts)))
37
+ Karafka::Messages::Message.new(p, Karafka::Messages::Metadata.new(build_metadata_hash(opts)))
37
38
  end
38
39
  consumer.messages = consumer_messages(messages)
39
40
  end
40
41
 
41
42
  # @return [Hash] message default options
42
- def metadata_defaults
43
+ def build_metadata_hash(opts)
43
44
  {
44
- deserializer: null_deserializer,
45
- headers: {},
46
- key: nil,
47
- offset: 0,
48
- partition: 0,
49
- received_at: Time.current,
50
- topic: test_topic.name
45
+ deserializers: test_topic.deserializers(payload: opts[:deserializer] || null_deserializer),
46
+ raw_headers: opts[:headers] || {},
47
+ raw_key: opts[:key],
48
+ offset: opts[:offset] || 0,
49
+ partition: opts[:partition] || 0,
50
+ received_at: opts[:received_at] || Time.current,
51
+ topic: opts[:topic] || test_topic.name
51
52
  }
52
53
  end
53
54
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sbmt
4
4
  module KafkaConsumer
5
- VERSION = "2.8.0"
5
+ VERSION = "3.0.0"
6
6
  end
7
7
  end
@@ -6,12 +6,12 @@ Gem::Specification.new do |spec|
6
6
  spec.name = "sbmt-kafka_consumer"
7
7
  spec.license = "MIT"
8
8
  spec.version = Sbmt::KafkaConsumer::VERSION
9
- spec.authors = ["Sbermarket Ruby-Platform Team"]
9
+ spec.authors = ["Kuper Ruby-Platform Team"]
10
10
 
11
11
  spec.summary = "Ruby gem for consuming Kafka messages"
12
12
  spec.description = "This gem is used for consuming Kafka messages. It represents a wrapper over Karafka gem and is recommended for using as a transport with sbmt-outbox"
13
- spec.homepage = "https://github.com/SberMarket-Tech/sbmt-kafka_consumer"
14
- spec.required_ruby_version = ">= 2.7.0"
13
+ spec.homepage = "https://github.com/Kuper-Tech/sbmt-kafka_consumer"
14
+ spec.required_ruby_version = ">= 3.0.0"
15
15
 
16
16
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
17
17
 
@@ -31,9 +31,9 @@ Gem::Specification.new do |spec|
31
31
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
32
  spec.require_paths = ["lib"]
33
33
 
34
- spec.add_dependency "rails", ">= 6.0"
34
+ spec.add_dependency "rails", ">= 6.1"
35
35
  spec.add_dependency "zeitwerk", "~> 2.3"
36
- spec.add_dependency "karafka", "~> 2.2", "< 2.4" # [Breaking] Drop the concept of consumer group mapping.
36
+ spec.add_dependency "karafka", "~> 2.4"
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,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sbmt-kafka_consumer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Sbermarket Ruby-Platform Team
7
+ - Kuper Ruby-Platform Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-09-09 00:00:00.000000000 Z
11
+ date: 2024-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '6.0'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '6.0'
26
+ version: '6.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: zeitwerk
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -43,9 +43,6 @@ dependencies:
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2.2'
48
- - - "<"
49
46
  - !ruby/object:Gem::Version
50
47
  version: '2.4'
51
48
  type: :runtime
@@ -53,9 +50,6 @@ dependencies:
53
50
  version_requirements: !ruby/object:Gem::Requirement
54
51
  requirements:
55
52
  - - "~>"
56
- - !ruby/object:Gem::Version
57
- version: '2.2'
58
- - - "<"
59
53
  - !ruby/object:Gem::Version
60
54
  version: '2.4'
61
55
  - !ruby/object:Gem::Dependency
@@ -531,6 +525,7 @@ files:
531
525
  - lib/sbmt/kafka_consumer/probes/host.rb
532
526
  - lib/sbmt/kafka_consumer/probes/probe.rb
533
527
  - lib/sbmt/kafka_consumer/railtie.rb
528
+ - lib/sbmt/kafka_consumer/routing/consumer_mapper/base.rb
534
529
  - lib/sbmt/kafka_consumer/routing/karafka_v1_consumer_mapper.rb
535
530
  - lib/sbmt/kafka_consumer/routing/karafka_v2_consumer_mapper.rb
536
531
  - lib/sbmt/kafka_consumer/serialization/base_deserializer.rb
@@ -546,14 +541,14 @@ files:
546
541
  - lib/sbmt/kafka_consumer/yabeda_configurer.rb
547
542
  - rubocop/rspec.yml
548
543
  - sbmt-kafka_consumer.gemspec
549
- homepage: https://github.com/SberMarket-Tech/sbmt-kafka_consumer
544
+ homepage: https://github.com/Kuper-Tech/sbmt-kafka_consumer
550
545
  licenses:
551
546
  - MIT
552
547
  metadata:
553
548
  allowed_push_host: https://rubygems.org
554
- homepage_uri: https://github.com/SberMarket-Tech/sbmt-kafka_consumer
555
- source_code_uri: https://github.com/SberMarket-Tech/sbmt-kafka_consumer
556
- changelog_uri: https://github.com/SberMarket-Tech/sbmt-kafka_consumer/blob/master/CHANGELOG.md
549
+ homepage_uri: https://github.com/Kuper-Tech/sbmt-kafka_consumer
550
+ source_code_uri: https://github.com/Kuper-Tech/sbmt-kafka_consumer
551
+ changelog_uri: https://github.com/Kuper-Tech/sbmt-kafka_consumer/blob/master/CHANGELOG.md
557
552
  rubygems_mfa_required: 'false'
558
553
  post_install_message:
559
554
  rdoc_options: []
@@ -563,7 +558,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
563
558
  requirements:
564
559
  - - ">="
565
560
  - !ruby/object:Gem::Version
566
- version: 2.7.0
561
+ version: 3.0.0
567
562
  required_rubygems_version: !ruby/object:Gem::Requirement
568
563
  requirements:
569
564
  - - ">="