rdkafka 0.9.0 → 0.11.1

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: 22bda74a815bebd487179ac5ec3faa4566752f2c87945ca5c461c427633daf1f
4
- data.tar.gz: '0802ca4118ed248fa11226a2c1b2140d073d230c02e3696a03e462fe59cb129e'
3
+ metadata.gz: 9b7042f241be5aad91c403f61aabb8e8ba87afa46674db8ba58c487fe01f88c9
4
+ data.tar.gz: 5cb00dc3dc8b4069a8e62cda9271e290d1371434332767270426488dedcff641
5
5
  SHA512:
6
- metadata.gz: 4d3b7042fa2290386d4feaaa01c4289bc858042d66806157066257f282c84db459e80e32a2613efd3452a46c63faaff7fad9f110fb153c3385cef00fb6017f27
7
- data.tar.gz: e431a320535df8293e099e1b25bf0e6da68caf9a641621faf847dd206093dd9973b41f03f95412c43a740b50a0ef70e7caad418ff20c07218f19342d1cb3826a
6
+ metadata.gz: 2b287a0d81aca7909702969778fb64585b6808860971d50e1c5540eb0c43cf73617f9e701605b816e8f52a81e39d9909512e1d113f6a550555e6ca503dc5ff58
7
+ data.tar.gz: 7924ab06f52646168b52d0478b605484e4aa1a3674ace4f97af5b56588aea6dec8e614d3258b0670f92f18a905509ae6982d2c8999fa719f8ea7451e515ecb3d
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --format documentation
@@ -13,7 +13,7 @@ blocks:
13
13
  - name: bundle exec rspec
14
14
  matrix:
15
15
  - env_var: RUBY_VERSION
16
- values: [ "2.5.8", "2.6.6", "2.7.2", "3.0.0", "jruby-9.2.13.0" ]
16
+ values: [ "2.6.8", "2.7.4", "3.0.2", "jruby-9.3.1.0"]
17
17
  commands:
18
18
  - sem-version ruby $RUBY_VERSION
19
19
  - checkout
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # 0.11.1
2
+ * Use mini_portile2 2.6, otherwise you can't run nokogiri and rdkafka at the same time.
3
+
4
+ # 0.11.0
5
+ * Upgrade librdkafka to 1.8.2
6
+ * Bump supported minimum Ruby version to 2.6
7
+ * Better homebrew path detection
8
+
9
+ # 0.10.0
10
+ * Upgrade librdkafka to 1.5.0
11
+ * Add error callback config
12
+
1
13
  # 0.9.0
2
14
  * Fixes for Ruby 3.0
3
15
  * Allow any callable object for callbacks (gremerritt)
@@ -41,7 +53,7 @@
41
53
  * Use default Homebrew openssl location if present
42
54
  * Consumer lag handles empty topics
43
55
  * End iteration in consumer when it is closed
44
- * Add suport for storing message offsets
56
+ * Add support for storing message offsets
45
57
  * Add missing runtime dependency to rake
46
58
 
47
59
  # 0.4.1
data/Guardfile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ logger level: :error
4
+
5
+ guard :rspec, cmd: "bundle exec rspec --format #{ENV.fetch("FORMAT", "documentation")}" do
6
+ require "guard/rspec/dsl"
7
+ dsl = Guard::RSpec::Dsl.new(self)
8
+
9
+ # Ruby files
10
+ ruby = dsl.ruby
11
+ dsl.watch_spec_files_for(ruby.lib_files)
12
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
13
+
14
+ # RSpec files
15
+ rspec = dsl.rspec
16
+ watch(rspec.spec_helper) { rspec.spec_dir }
17
+ watch(rspec.spec_support) { rspec.spec_dir }
18
+ watch(rspec.spec_files)
19
+ end
data/README.md CHANGED
@@ -7,7 +7,9 @@
7
7
  The `rdkafka` gem is a modern Kafka client library for Ruby based on
8
8
  [librdkafka](https://github.com/edenhill/librdkafka/).
9
9
  It wraps the production-ready C client using the [ffi](https://github.com/ffi/ffi)
10
- gem and targets Kafka 1.0+ and Ruby 2.4+.
10
+ gem and targets Kafka 1.0+ and Ruby versions that are under security or
11
+ active maintenance. We remove Ruby version from our CI builds if they
12
+ become EOL.
11
13
 
12
14
  `rdkafka` was written because we needed a reliable Ruby client for
13
15
  Kafka that supports modern Kafka at [AppSignal](https://appsignal.com).
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ ENV["IRBRC"] = File.join(File.dirname(__FILE__), ".irbrc")
6
+
7
+ require "bundler/setup"
8
+ require "rdkafka"
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/docker-compose.yml CHANGED
@@ -4,13 +4,13 @@ version: '2'
4
4
 
5
5
  services:
6
6
  zookeeper:
7
- image: confluentinc/cp-zookeeper:latest
7
+ image: confluentinc/cp-zookeeper:5.2.6
8
8
  environment:
9
9
  ZOOKEEPER_CLIENT_PORT: 2181
10
10
  ZOOKEEPER_TICK_TIME: 2000
11
11
 
12
12
  kafka:
13
- image: confluentinc/cp-kafka:latest
13
+ image: confluentinc/cp-kafka:5.2.5-10
14
14
  depends_on:
15
15
  - zookeeper
16
16
  ports:
@@ -18,7 +18,7 @@ services:
18
18
  environment:
19
19
  KAFKA_BROKER_ID: 1
20
20
  KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
21
- KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
21
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:29092,PLAINTEXT_HOST://localhost:9092
22
22
  KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
23
23
  KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
24
24
  KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
data/ext/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ext
2
2
 
3
- This gem dependes on the `librdkafka` C library. It is downloaded when
3
+ This gem depends on the `librdkafka` C library. It is downloaded when
4
4
  this gem is installed.
5
5
 
6
6
  To update the `librdkafka` version follow the following steps:
data/ext/Rakefile CHANGED
@@ -4,30 +4,14 @@ require "fileutils"
4
4
  require "open-uri"
5
5
 
6
6
  task :default => :clean do
7
- # MiniPortile#download_file_http is a monkey patch that removes the download
8
- # progress indicator. This indicator relies on the 'Content Length' response
9
- # headers, which is not set by GitHub
10
- class MiniPortile
11
- def download_file_http(url, full_path, _count)
12
- filename = File.basename(full_path)
13
- with_tempfile(filename, full_path) do |temp_file|
14
- params = { 'Accept-Encoding' => 'identity' }
15
- OpenURI.open_uri(url, 'rb', params) do |io|
16
- temp_file.write(io.read)
17
- end
18
- output
19
- end
20
- end
21
- end
22
-
23
7
  # Download and compile librdkafka
24
8
  recipe = MiniPortile.new("librdkafka", Rdkafka::LIBRDKAFKA_VERSION)
25
9
 
26
10
  # Use default homebrew openssl if we're on mac and the directory exists
27
11
  # and each of flags is not empty
28
- if recipe.host&.include?("darwin") && Dir.exist?("/usr/local/opt/openssl")
29
- ENV["CPPFLAGS"] = "-I/usr/local/opt/openssl/include" unless ENV["CPPFLAGS"]
30
- ENV["LDFLAGS"] = "-L/usr/local/opt/openssl/lib" unless ENV["LDFLAGS"]
12
+ if recipe.host&.include?("darwin") && system("which brew &> /dev/null") && Dir.exist?("#{homebrew_prefix = %x(brew --prefix openssl).strip}")
13
+ ENV["CPPFLAGS"] = "-I#{homebrew_prefix}/include" unless ENV["CPPFLAGS"]
14
+ ENV["LDFLAGS"] = "-L#{homebrew_prefix}/lib" unless ENV["LDFLAGS"]
31
15
  end
32
16
 
33
17
  recipe.files << {
@@ -108,6 +108,8 @@ module Rdkafka
108
108
  attach_function :rd_kafka_conf_set_opaque, [:pointer, :pointer], :void
109
109
  callback :stats_cb, [:pointer, :string, :int, :pointer], :int
110
110
  attach_function :rd_kafka_conf_set_stats_cb, [:pointer, :stats_cb], :void
111
+ callback :error_cb, [:pointer, :int, :string, :pointer], :void
112
+ attach_function :rd_kafka_conf_set_error_cb, [:pointer, :error_cb], :void
111
113
 
112
114
  # Log queue
113
115
  attach_function :rd_kafka_set_log_queue, [:pointer, :pointer], :void
@@ -146,6 +148,15 @@ module Rdkafka
146
148
  0
147
149
  end
148
150
 
151
+ ErrorCallback = FFI::Function.new(
152
+ :void, [:pointer, :int, :string, :pointer]
153
+ ) do |_client_prr, err_code, reason, _opaque|
154
+ if Rdkafka::Config.error_callback
155
+ error = Rdkafka::RdkafkaError.new(err_code, broker_message: reason)
156
+ Rdkafka::Config.error_callback.call(error)
157
+ end
158
+ end
159
+
149
160
  # Handle
150
161
 
151
162
  enum :kafka_type, [
@@ -10,6 +10,8 @@ module Rdkafka
10
10
  # @private
11
11
  @@statistics_callback = nil
12
12
  # @private
13
+ @@error_callback = nil
14
+ # @private
13
15
  @@opaques = {}
14
16
  # @private
15
17
  @@log_queue = Queue.new
@@ -28,6 +30,7 @@ module Rdkafka
28
30
  @@logger
29
31
  end
30
32
 
33
+
31
34
  # Returns a queue whose contents will be passed to the configured logger. Each entry
32
35
  # should follow the format [Logger::Severity, String]. The benefit over calling the
33
36
  # logger directly is that this is safe to use from trap contexts.
@@ -66,6 +69,25 @@ module Rdkafka
66
69
  @@statistics_callback
67
70
  end
68
71
 
72
+ # Set a callback that will be called every time the underlying client emits an error.
73
+ # If this callback is not set, global errors such as brokers becoming unavailable will only be sent to the logger, as defined by librdkafka.
74
+ # The callback is called with an instance of RdKafka::Error.
75
+ #
76
+ # @param callback [Proc, #call] The callback
77
+ #
78
+ # @return [nil]
79
+ def self.error_callback=(callback)
80
+ raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call)
81
+ @@error_callback = callback
82
+ end
83
+
84
+ # Returns the current error callback, by default this is nil.
85
+ #
86
+ # @return [Proc, nil]
87
+ def self.error_callback
88
+ @@error_callback
89
+ end
90
+
69
91
  # @private
70
92
  def self.opaques
71
93
  @@opaques
@@ -221,6 +243,9 @@ module Rdkafka
221
243
 
222
244
  # Set stats callback
223
245
  Rdkafka::Bindings.rd_kafka_conf_set_stats_cb(config, Rdkafka::Bindings::StatsCallback)
246
+
247
+ # Set error callback
248
+ Rdkafka::Bindings.rd_kafka_conf_set_error_cb(config, Rdkafka::Bindings::ErrorCallback)
224
249
  end
225
250
  end
226
251
 
@@ -498,11 +498,11 @@ module Rdkafka
498
498
  # Exception behavior is more complicated than with `each`, in that if
499
499
  # :yield_on_error is true, and an exception is raised during the
500
500
  # poll, and messages have already been received, they will be yielded to
501
- # the caller before the exception is allowed to propogate.
501
+ # the caller before the exception is allowed to propagate.
502
502
  #
503
503
  # If you are setting either auto.commit or auto.offset.store to false in
504
504
  # the consumer configuration, then you should let yield_on_error keep its
505
- # default value of false because you are gauranteed to see these messages
505
+ # default value of false because you are guaranteed to see these messages
506
506
  # again. However, if both auto.commit and auto.offset.store are set to
507
507
  # true, you should set yield_on_error to true so you can process messages
508
508
  # that you may or may not see again.
@@ -518,7 +518,7 @@ module Rdkafka
518
518
  # @yield [messages, pending_exception]
519
519
  # @yieldparam messages [Array] An array of received Message
520
520
  # @yieldparam pending_exception [Exception] normally nil, or an exception
521
- # which will be propogated after processing of the partial batch is complete.
521
+ # which will be propagated after processing of the partial batch is complete.
522
522
  #
523
523
  # @return [nil]
524
524
  def each_batch(max_items: 100, bytes_threshold: Float::INFINITY, timeout_ms: 250, yield_on_error: false, &block)
@@ -11,7 +11,7 @@ module Rdkafka
11
11
  attr_reader :offset
12
12
 
13
13
  # Error in case happen during produce.
14
- # @return [string]
14
+ # @return [String]
15
15
  attr_reader :error
16
16
 
17
17
  private
@@ -1,3 +1,5 @@
1
+ require "securerandom"
2
+
1
3
  module Rdkafka
2
4
  # A producer for Kafka messages. To create a producer set up a {Config} and call {Config#producer producer} on that.
3
5
  class Producer
@@ -9,11 +11,12 @@ module Rdkafka
9
11
 
10
12
  # @private
11
13
  def initialize(native_kafka)
14
+ @id = SecureRandom.uuid
12
15
  @closing = false
13
16
  @native_kafka = native_kafka
14
17
 
15
18
  # Makes sure, that the producer gets closed before it gets GCed by Ruby
16
- ObjectSpace.define_finalizer(self, proc { close })
19
+ ObjectSpace.define_finalizer(@id, proc { close })
17
20
 
18
21
  # Start thread to poll client for delivery callbacks
19
22
  @polling_thread = Thread.new do
@@ -41,6 +44,8 @@ module Rdkafka
41
44
 
42
45
  # Close this producer and wait for the internal poll queue to empty.
43
46
  def close
47
+ ObjectSpace.undefine_finalizer(@id)
48
+
44
49
  return unless @native_kafka
45
50
 
46
51
  # Indicate to polling thread that we're closing
@@ -70,8 +75,9 @@ module Rdkafka
70
75
  #
71
76
  # @param topic [String] The topic to produce to
72
77
  # @param payload [String,nil] The message's payload
73
- # @param key [String] The message's key
78
+ # @param key [String, nil] The message's key
74
79
  # @param partition [Integer,nil] Optional partition to produce to
80
+ # @param partition_key [String, nil] Optional partition key based on which partition assignment can happen
75
81
  # @param timestamp [Time,Integer,nil] Optional timestamp of this message. Integer timestamp is in milliseconds since Jan 1 1970.
76
82
  # @param headers [Hash<String,String>] Optional message headers
77
83
  #
@@ -1,5 +1,5 @@
1
1
  module Rdkafka
2
- VERSION = "0.9.0"
3
- LIBRDKAFKA_VERSION = "1.4.0"
4
- LIBRDKAFKA_SOURCE_SHA256 = "ae27ea3f3d0d32d29004e7f709efbba2666c5383a107cc45b3a1949486b2eb84"
2
+ VERSION = "0.11.1"
3
+ LIBRDKAFKA_VERSION = "1.8.2"
4
+ LIBRDKAFKA_SOURCE_SHA256 = "6a747d293a7a4613bd2897e28e8791476fbe1ae7361f2530a876e0fd483482a6"
5
5
  end
data/rdkafka.gemspec CHANGED
@@ -14,15 +14,17 @@ Gem::Specification.new do |gem|
14
14
  gem.name = 'rdkafka'
15
15
  gem.require_paths = ['lib']
16
16
  gem.version = Rdkafka::VERSION
17
- gem.required_ruby_version = '>= 2.4'
17
+ gem.required_ruby_version = '>= 2.6'
18
18
  gem.extensions = %w(ext/Rakefile)
19
19
 
20
- gem.add_dependency 'ffi', '~> 1.9'
21
- gem.add_dependency 'mini_portile2', '~> 2.1'
22
- gem.add_dependency 'rake', '>= 12.3'
20
+ gem.add_dependency 'ffi', '~> 1.15'
21
+ gem.add_dependency 'mini_portile2', '~> 2.6'
22
+ gem.add_dependency 'rake', '> 12'
23
23
 
24
- gem.add_development_dependency 'pry', '~> 0.10'
24
+ gem.add_development_dependency 'pry'
25
25
  gem.add_development_dependency 'rspec', '~> 3.5'
26
- gem.add_development_dependency 'rake', '~> 12.0'
27
- gem.add_development_dependency 'simplecov', '~> 0.15'
26
+ gem.add_development_dependency 'rake'
27
+ gem.add_development_dependency 'simplecov'
28
+ gem.add_development_dependency 'guard'
29
+ gem.add_development_dependency 'guard-rspec'
28
30
  end
@@ -111,4 +111,3 @@ describe Rdkafka::AbstractHandle do
111
111
  end
112
112
  end
113
113
  end
114
-
@@ -52,7 +52,7 @@ describe Rdkafka::Admin do
52
52
  end
53
53
 
54
54
  describe "with an invalid partition count" do
55
- let(:topic_partition_count) { -1 }
55
+ let(:topic_partition_count) { -999 }
56
56
 
57
57
  it "raises an exception" do
58
58
  expect {
@@ -100,4 +100,28 @@ describe Rdkafka::Bindings do
100
100
  end
101
101
  end
102
102
  end
103
+
104
+ describe "error callback" do
105
+ context "without an error callback" do
106
+ it "should do nothing" do
107
+ expect {
108
+ Rdkafka::Bindings::ErrorCallback.call(nil, 1, "error", nil)
109
+ }.not_to raise_error
110
+ end
111
+ end
112
+
113
+ context "with an error callback" do
114
+ before do
115
+ Rdkafka::Config.error_callback = lambda do |error|
116
+ $received_error = error
117
+ end
118
+ end
119
+
120
+ it "should call the error callback with an Rdkafka::Error" do
121
+ Rdkafka::Bindings::ErrorCallback.call(nil, 8, "Broker not available", nil)
122
+ expect($received_error.code).to eq(:broker_not_available)
123
+ expect($received_error.broker_message).to eq("Broker not available")
124
+ end
125
+ end
126
+ end
103
127
  end
@@ -64,6 +64,37 @@ describe Rdkafka::Config do
64
64
  end
65
65
  end
66
66
 
67
+ context "error callback" do
68
+ context "with a proc/lambda" do
69
+ it "should set the callback" do
70
+ expect {
71
+ Rdkafka::Config.error_callback = lambda do |error|
72
+ puts error
73
+ end
74
+ }.not_to raise_error
75
+ expect(Rdkafka::Config.error_callback).to respond_to :call
76
+ end
77
+ end
78
+
79
+ context "with a callable object" do
80
+ it "should set the callback" do
81
+ callback = Class.new do
82
+ def call(stats); end
83
+ end
84
+ expect {
85
+ Rdkafka::Config.error_callback = callback.new
86
+ }.not_to raise_error
87
+ expect(Rdkafka::Config.error_callback).to respond_to :call
88
+ end
89
+ end
90
+
91
+ it "should not accept a callback that's not callable" do
92
+ expect {
93
+ Rdkafka::Config.error_callback = 'a string'
94
+ }.to raise_error(TypeError)
95
+ end
96
+ end
97
+
67
98
  context "configuration" do
68
99
  it "should store configuration" do
69
100
  config = Rdkafka::Config.new
@@ -77,7 +108,7 @@ describe Rdkafka::Config do
77
108
  end
78
109
 
79
110
  it "should create a consumer with valid config" do
80
- consumer = rdkafka_config.consumer
111
+ consumer = rdkafka_consumer_config.consumer
81
112
  expect(consumer).to be_a Rdkafka::Consumer
82
113
  consumer.close
83
114
  end
@@ -105,7 +136,7 @@ describe Rdkafka::Config do
105
136
  end
106
137
 
107
138
  it "should create a producer with valid config" do
108
- producer = rdkafka_config.producer
139
+ producer = rdkafka_consumer_config.producer
109
140
  expect(producer).to be_a Rdkafka::Producer
110
141
  producer.close
111
142
  end
@@ -3,9 +3,8 @@ require "ostruct"
3
3
  require 'securerandom'
4
4
 
5
5
  describe Rdkafka::Consumer do
6
- let(:config) { rdkafka_config }
7
- let(:consumer) { config.consumer }
8
- let(:producer) { config.producer }
6
+ let(:consumer) { rdkafka_consumer_config.consumer }
7
+ let(:producer) { rdkafka_producer_config.producer }
9
8
 
10
9
  after { consumer.close }
11
10
  after { producer.close }
@@ -328,7 +327,7 @@ describe Rdkafka::Consumer do
328
327
  before :all do
329
328
  # Make sure there are some messages.
330
329
  handles = []
331
- producer = rdkafka_config.producer
330
+ producer = rdkafka_producer_config.producer
332
331
  10.times do
333
332
  (0..2).each do |i|
334
333
  handles << producer.produce(
@@ -404,7 +403,7 @@ describe Rdkafka::Consumer do
404
403
  config = {}
405
404
  config[:'enable.auto.offset.store'] = false
406
405
  config[:'enable.auto.commit'] = false
407
- @new_consumer = rdkafka_config(config).consumer
406
+ @new_consumer = rdkafka_consumer_config(config).consumer
408
407
  @new_consumer.subscribe("consume_test_topic")
409
408
  wait_for_assignment(@new_consumer)
410
409
  end
@@ -459,13 +458,13 @@ describe Rdkafka::Consumer do
459
458
  end
460
459
 
461
460
  describe "#lag" do
462
- let(:config) { rdkafka_config(:"enable.partition.eof" => true) }
461
+ let(:consumer) { rdkafka_consumer_config(:"enable.partition.eof" => true).consumer }
463
462
 
464
463
  it "should calculate the consumer lag" do
465
464
  # Make sure there's a message in every partition and
466
465
  # wait for the message to make sure everything is committed.
467
466
  (0..2).each do |i|
468
- report = producer.produce(
467
+ producer.produce(
469
468
  topic: "consume_test_topic",
470
469
  key: "key lag #{i}",
471
470
  partition: i
@@ -508,7 +507,7 @@ describe Rdkafka::Consumer do
508
507
 
509
508
  # Produce message on every topic again
510
509
  (0..2).each do |i|
511
- report = producer.produce(
510
+ producer.produce(
512
511
  topic: "consume_test_topic",
513
512
  key: "key lag #{i}",
514
513
  partition: i
@@ -724,6 +723,8 @@ describe Rdkafka::Consumer do
724
723
  #
725
724
  # This is, in effect, an integration test and the subsequent specs are
726
725
  # unit tests.
726
+ create_topic_handle = rdkafka_config.admin.create_topic(topic_name, 1, 1)
727
+ create_topic_handle.wait(max_wait_timeout: 15.0)
727
728
  consumer.subscribe(topic_name)
728
729
  produce_n 42
729
730
  all_yields = []
@@ -777,6 +778,8 @@ describe Rdkafka::Consumer do
777
778
  end
778
779
 
779
780
  it "should yield [] if nothing is received before the timeout" do
781
+ create_topic_handle = rdkafka_config.admin.create_topic(topic_name, 1, 1)
782
+ create_topic_handle.wait(max_wait_timeout: 15.0)
780
783
  consumer.subscribe(topic_name)
781
784
  consumer.each_batch do |batch|
782
785
  expect(batch).to eq([])
@@ -820,8 +823,12 @@ describe Rdkafka::Consumer do
820
823
 
821
824
  context "error raised from poll and yield_on_error is true" do
822
825
  it "should yield buffered exceptions on rebalance, then break" do
823
- config = rdkafka_config({:"enable.auto.commit" => false,
824
- :"enable.auto.offset.store" => false })
826
+ config = rdkafka_consumer_config(
827
+ {
828
+ :"enable.auto.commit" => false,
829
+ :"enable.auto.offset.store" => false
830
+ }
831
+ )
825
832
  consumer = config.consumer
826
833
  consumer.subscribe(topic_name)
827
834
  loop_count = 0
@@ -860,8 +867,12 @@ describe Rdkafka::Consumer do
860
867
 
861
868
  context "error raised from poll and yield_on_error is false" do
862
869
  it "should yield buffered exceptions on rebalance, then break" do
863
- config = rdkafka_config({:"enable.auto.commit" => false,
864
- :"enable.auto.offset.store" => false })
870
+ config = rdkafka_consumer_config(
871
+ {
872
+ :"enable.auto.commit" => false,
873
+ :"enable.auto.offset.store" => false
874
+ }
875
+ )
865
876
  consumer = config.consumer
866
877
  consumer.subscribe(topic_name)
867
878
  loop_count = 0
@@ -898,51 +909,64 @@ describe Rdkafka::Consumer do
898
909
  end
899
910
 
900
911
  describe "a rebalance listener" do
901
- it "should get notifications" do
902
- listener = Struct.new(:queue) do
903
- def on_partitions_assigned(consumer, list)
904
- collect(:assign, list)
905
- end
912
+ let(:consumer) do
913
+ config = rdkafka_consumer_config
914
+ config.consumer_rebalance_listener = listener
915
+ config.consumer
916
+ end
906
917
 
907
- def on_partitions_revoked(consumer, list)
908
- collect(:revoke, list)
909
- end
918
+ context "with a working listener" do
919
+ let(:listener) do
920
+ Struct.new(:queue) do
921
+ def on_partitions_assigned(consumer, list)
922
+ collect(:assign, list)
923
+ end
910
924
 
911
- def collect(name, list)
912
- partitions = list.to_h.map { |key, values| [key, values.map(&:partition)] }.flatten
913
- queue << ([name] + partitions)
914
- end
915
- end.new([])
925
+ def on_partitions_revoked(consumer, list)
926
+ collect(:revoke, list)
927
+ end
928
+
929
+ def collect(name, list)
930
+ partitions = list.to_h.map { |key, values| [key, values.map(&:partition)] }.flatten
931
+ queue << ([name] + partitions)
932
+ end
933
+ end.new([])
934
+ end
916
935
 
917
- notify_listener(listener)
936
+ it "should get notifications" do
937
+ notify_listener(listener)
918
938
 
919
- expect(listener.queue).to eq([
920
- [:assign, "consume_test_topic", 0, 1, 2],
921
- [:revoke, "consume_test_topic", 0, 1, 2]
922
- ])
939
+ expect(listener.queue).to eq([
940
+ [:assign, "consume_test_topic", 0, 1, 2],
941
+ [:revoke, "consume_test_topic", 0, 1, 2]
942
+ ])
943
+ end
923
944
  end
924
945
 
925
- it 'should handle callback exceptions' do
926
- listener = Struct.new(:queue) do
927
- def on_partitions_assigned(consumer, list)
928
- queue << :assigned
929
- raise 'boom'
930
- end
946
+ context "with a broken listener" do
947
+ let(:listener) do
948
+ Struct.new(:queue) do
949
+ def on_partitions_assigned(consumer, list)
950
+ queue << :assigned
951
+ raise 'boom'
952
+ end
931
953
 
932
- def on_partitions_revoked(consumer, list)
933
- queue << :revoked
934
- raise 'boom'
935
- end
936
- end.new([])
954
+ def on_partitions_revoked(consumer, list)
955
+ queue << :revoked
956
+ raise 'boom'
957
+ end
958
+ end.new([])
959
+ end
937
960
 
938
- notify_listener(listener)
961
+ it 'should handle callback exceptions' do
962
+ notify_listener(listener)
939
963
 
940
- expect(listener.queue).to eq([:assigned, :revoked])
964
+ expect(listener.queue).to eq([:assigned, :revoked])
965
+ end
941
966
  end
942
967
 
943
968
  def notify_listener(listener)
944
969
  # 1. subscribe and poll
945
- config.consumer_rebalance_listener = listener
946
970
  consumer.subscribe("consume_test_topic")
947
971
  wait_for_assignment(consumer)
948
972
  consumer.poll(100)
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
  require "securerandom"
3
3
 
4
4
  describe Rdkafka::Metadata do
5
- let(:config) { rdkafka_config }
5
+ let(:config) { rdkafka_consumer_config }
6
6
  let(:native_config) { config.send(:native_config) }
7
7
  let(:native_kafka) { config.send(:native_kafka, native_config, :rd_kafka_consumer) }
8
8
 
@@ -18,7 +18,7 @@ describe Rdkafka::Metadata do
18
18
  it "raises an appropriate exception" do
19
19
  expect {
20
20
  described_class.new(native_kafka, topic_name)
21
- }.to raise_exception(Rdkafka::RdkafkaError, "Broker: Leader not available (leader_not_available)")
21
+ }.to raise_exception(Rdkafka::RdkafkaError, "Broker: Unknown topic or partition (unknown_topic_or_part)")
22
22
  end
23
23
  end
24
24
 
@@ -1,8 +1,9 @@
1
1
  require "spec_helper"
2
+ require "zlib"
2
3
 
3
4
  describe Rdkafka::Producer do
4
- let(:producer) { rdkafka_config.producer }
5
- let(:consumer) { rdkafka_config.consumer }
5
+ let(:producer) { rdkafka_producer_config.producer }
6
+ let(:consumer) { rdkafka_consumer_config.consumer }
6
7
 
7
8
  after do
8
9
  # Registry should always end up being empty
@@ -388,7 +389,7 @@ describe Rdkafka::Producer do
388
389
  reader.close
389
390
 
390
391
  # Avoids sharing the socket between processes.
391
- producer = rdkafka_config.producer
392
+ producer = rdkafka_producer_config.producer
392
393
 
393
394
  handle = producer.produce(
394
395
  topic: "produce_test_topic",
data/spec/spec_helper.rb CHANGED
@@ -8,27 +8,57 @@ end
8
8
  require "pry"
9
9
  require "rspec"
10
10
  require "rdkafka"
11
+ require "timeout"
11
12
 
12
- def rdkafka_config(config_overrides={})
13
- config = {
13
+ def rdkafka_base_config
14
+ {
14
15
  :"api.version.request" => false,
15
16
  :"broker.version.fallback" => "1.0",
16
17
  :"bootstrap.servers" => "localhost:9092",
17
- :"group.id" => "ruby-test-#{Random.new.rand(0..1_000_000)}",
18
- :"auto.offset.reset" => "earliest",
19
- :"enable.partition.eof" => false
20
18
  }
19
+ end
20
+
21
+ def rdkafka_config(config_overrides={})
22
+ # Generate the base config
23
+ config = rdkafka_base_config
24
+ # Merge overrides
25
+ config.merge!(config_overrides)
26
+ # Return it
27
+ Rdkafka::Config.new(config)
28
+ end
29
+
30
+ def rdkafka_consumer_config(config_overrides={})
31
+ # Generate the base config
32
+ config = rdkafka_base_config
33
+ # Add consumer specific fields to it
34
+ config[:"auto.offset.reset"] = "earliest"
35
+ config[:"enable.partition.eof"] = false
36
+ config[:"group.id"] = "ruby-test-#{Random.new.rand(0..1_000_000)}"
37
+ # Enable debug mode if required
38
+ if ENV["DEBUG_CONSUMER"]
39
+ config[:debug] = "cgrp,topic,fetch"
40
+ end
41
+ # Merge overrides
42
+ config.merge!(config_overrides)
43
+ # Return it
44
+ Rdkafka::Config.new(config)
45
+ end
46
+
47
+ def rdkafka_producer_config(config_overrides={})
48
+ # Generate the base config
49
+ config = rdkafka_base_config
50
+ # Enable debug mode if required
21
51
  if ENV["DEBUG_PRODUCER"]
22
52
  config[:debug] = "broker,topic,msg"
23
- elsif ENV["DEBUG_CONSUMER"]
24
- config[:debug] = "cgrp,topic,fetch"
25
53
  end
54
+ # Merge overrides
26
55
  config.merge!(config_overrides)
56
+ # Return it
27
57
  Rdkafka::Config.new(config)
28
58
  end
29
59
 
30
60
  def new_native_client
31
- config = rdkafka_config
61
+ config = rdkafka_consumer_config
32
62
  config.send(:native_kafka, config.send(:native_config), :rd_kafka_producer)
33
63
  end
34
64
 
@@ -42,7 +72,7 @@ end
42
72
 
43
73
  def wait_for_message(topic:, delivery_report:, timeout_in_seconds: 30, consumer: nil)
44
74
  new_consumer = !!consumer
45
- consumer ||= rdkafka_config.consumer
75
+ consumer ||= rdkafka_consumer_config.consumer
46
76
  consumer.subscribe(topic)
47
77
  timeout = Time.now.to_i + timeout_in_seconds
48
78
  loop do
@@ -75,6 +105,9 @@ def wait_for_unassignment(consumer)
75
105
  end
76
106
 
77
107
  RSpec.configure do |config|
108
+ config.filter_run focus: true
109
+ config.run_all_when_everything_filtered = true
110
+
78
111
  config.before(:suite) do
79
112
  admin = rdkafka_config.admin
80
113
  {
@@ -95,4 +128,12 @@ RSpec.configure do |config|
95
128
  end
96
129
  admin.close
97
130
  end
131
+
132
+ config.around(:each) do |example|
133
+ # Timeout specs after a minute. If they take longer
134
+ # they are probably stuck
135
+ Timeout::timeout(60) do
136
+ example.run
137
+ end
138
+ end
98
139
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdkafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thijs Cadier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-23 00:00:00.000000000 Z
11
+ date: 2021-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -16,56 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.9'
19
+ version: '1.15'
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: '1.9'
26
+ version: '1.15'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mini_portile2
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.1'
33
+ version: '2.6'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.1'
40
+ version: '2.6'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - ">"
46
46
  - !ruby/object:Gem::Version
47
- version: '12.3'
47
+ version: '12'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - ">"
53
53
  - !ruby/object:Gem::Version
54
- version: '12.3'
54
+ version: '12'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0.10'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0.10'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -84,46 +84,78 @@ dependencies:
84
84
  name: rake
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '12.0'
89
+ version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '12.0'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: simplecov
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "~>"
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: '0.15'
103
+ version: '0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "~>"
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
109
137
  - !ruby/object:Gem::Version
110
- version: '0.15'
138
+ version: '0'
111
139
  description: Modern Kafka client library for Ruby based on librdkafka
112
140
  email:
113
141
  - thijs@appsignal.com
114
- executables: []
142
+ executables:
143
+ - console
115
144
  extensions:
116
145
  - ext/Rakefile
117
146
  extra_rdoc_files: []
118
147
  files:
119
148
  - ".gitignore"
149
+ - ".rspec"
120
150
  - ".semaphore/semaphore.yml"
121
151
  - ".yardopts"
122
152
  - CHANGELOG.md
123
153
  - Gemfile
154
+ - Guardfile
124
155
  - LICENSE
125
156
  - README.md
126
157
  - Rakefile
158
+ - bin/console
127
159
  - docker-compose.yml
128
160
  - ext/README.md
129
161
  - ext/Rakefile
@@ -180,14 +212,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
180
212
  requirements:
181
213
  - - ">="
182
214
  - !ruby/object:Gem::Version
183
- version: '2.4'
215
+ version: '2.6'
184
216
  required_rubygems_version: !ruby/object:Gem::Requirement
185
217
  requirements:
186
218
  - - ">="
187
219
  - !ruby/object:Gem::Version
188
220
  version: '0'
189
221
  requirements: []
190
- rubygems_version: 3.2.3
222
+ rubygems_version: 3.1.4
191
223
  signing_key:
192
224
  specification_version: 4
193
225
  summary: The rdkafka gem is a modern Kafka client library for Ruby based on librdkafka.