rdkafka 0.8.1 → 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 +4 -4
- data/.rspec +1 -0
- data/.semaphore/semaphore.yml +3 -3
- data/CHANGELOG.md +21 -1
- data/Guardfile +19 -0
- data/README.md +3 -1
- data/bin/console +11 -0
- data/docker-compose.yml +3 -3
- data/ext/README.md +8 -1
- data/ext/Rakefile +4 -19
- data/lib/rdkafka/admin.rb +12 -1
- data/lib/rdkafka/bindings.rb +13 -1
- data/lib/rdkafka/config.rb +45 -2
- data/lib/rdkafka/consumer.rb +90 -0
- data/lib/rdkafka/producer/delivery_report.rb +1 -1
- data/lib/rdkafka/producer.rb +15 -11
- data/lib/rdkafka/version.rb +3 -3
- data/rdkafka.gemspec +9 -7
- data/spec/rdkafka/abstract_handle_spec.rb +0 -1
- data/spec/rdkafka/admin_spec.rb +13 -2
- data/spec/rdkafka/bindings_spec.rb +32 -8
- data/spec/rdkafka/config_spec.rb +67 -9
- data/spec/rdkafka/consumer_spec.rb +292 -40
- data/spec/rdkafka/metadata_spec.rb +2 -2
- data/spec/rdkafka/producer_spec.rb +80 -34
- data/spec/spec_helper.rb +50 -9
- metadata +60 -28
@@ -25,39 +25,39 @@ describe Rdkafka::Bindings do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
describe "log callback" do
|
28
|
-
let(:
|
28
|
+
let(:log_queue) { Rdkafka::Config.log_queue }
|
29
29
|
before do
|
30
|
-
|
30
|
+
allow(log_queue).to receive(:<<)
|
31
31
|
end
|
32
32
|
|
33
33
|
it "should log fatal messages" do
|
34
34
|
Rdkafka::Bindings::LogCallback.call(nil, 0, nil, "log line")
|
35
|
-
expect(
|
35
|
+
expect(log_queue).to have_received(:<<).with([Logger::FATAL, "rdkafka: log line"])
|
36
36
|
end
|
37
37
|
|
38
38
|
it "should log error messages" do
|
39
39
|
Rdkafka::Bindings::LogCallback.call(nil, 3, nil, "log line")
|
40
|
-
expect(
|
40
|
+
expect(log_queue).to have_received(:<<).with([Logger::ERROR, "rdkafka: log line"])
|
41
41
|
end
|
42
42
|
|
43
43
|
it "should log warning messages" do
|
44
44
|
Rdkafka::Bindings::LogCallback.call(nil, 4, nil, "log line")
|
45
|
-
expect(
|
45
|
+
expect(log_queue).to have_received(:<<).with([Logger::WARN, "rdkafka: log line"])
|
46
46
|
end
|
47
47
|
|
48
48
|
it "should log info messages" do
|
49
49
|
Rdkafka::Bindings::LogCallback.call(nil, 5, nil, "log line")
|
50
|
-
expect(
|
50
|
+
expect(log_queue).to have_received(:<<).with([Logger::INFO, "rdkafka: log line"])
|
51
51
|
end
|
52
52
|
|
53
53
|
it "should log debug messages" do
|
54
54
|
Rdkafka::Bindings::LogCallback.call(nil, 7, nil, "log line")
|
55
|
-
expect(
|
55
|
+
expect(log_queue).to have_received(:<<).with([Logger::DEBUG, "rdkafka: log line"])
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should log unknown messages" do
|
59
59
|
Rdkafka::Bindings::LogCallback.call(nil, 100, nil, "log line")
|
60
|
-
expect(
|
60
|
+
expect(log_queue).to have_received(:<<).with([Logger::UNKNOWN, "rdkafka: log line"])
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -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
|
data/spec/rdkafka/config_spec.rb
CHANGED
@@ -18,25 +18,83 @@ describe Rdkafka::Config do
|
|
18
18
|
Rdkafka::Config.logger = nil
|
19
19
|
}.to raise_error(Rdkafka::Config::NoLoggerError)
|
20
20
|
end
|
21
|
+
|
22
|
+
it "supports logging queue" do
|
23
|
+
log = StringIO.new
|
24
|
+
Rdkafka::Config.logger = Logger.new(log)
|
25
|
+
|
26
|
+
Rdkafka::Config.log_queue << [Logger::FATAL, "I love testing"]
|
27
|
+
20.times do
|
28
|
+
break if log.string != ""
|
29
|
+
sleep 0.05
|
30
|
+
end
|
31
|
+
|
32
|
+
expect(log.string).to include "FATAL -- : I love testing"
|
33
|
+
end
|
21
34
|
end
|
22
35
|
|
23
36
|
context "statistics callback" do
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
37
|
+
context "with a proc/lambda" do
|
38
|
+
it "should set the callback" do
|
39
|
+
expect {
|
40
|
+
Rdkafka::Config.statistics_callback = lambda do |stats|
|
41
|
+
puts stats
|
42
|
+
end
|
43
|
+
}.not_to raise_error
|
44
|
+
expect(Rdkafka::Config.statistics_callback).to respond_to :call
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with a callable object" do
|
49
|
+
it "should set the callback" do
|
50
|
+
callback = Class.new do
|
51
|
+
def call(stats); end
|
28
52
|
end
|
29
|
-
|
30
|
-
|
53
|
+
expect {
|
54
|
+
Rdkafka::Config.statistics_callback = callback.new
|
55
|
+
}.not_to raise_error
|
56
|
+
expect(Rdkafka::Config.statistics_callback).to respond_to :call
|
57
|
+
end
|
31
58
|
end
|
32
59
|
|
33
|
-
it "should not accept a callback that's not
|
60
|
+
it "should not accept a callback that's not callable" do
|
34
61
|
expect {
|
35
62
|
Rdkafka::Config.statistics_callback = 'a string'
|
36
63
|
}.to raise_error(TypeError)
|
37
64
|
end
|
38
65
|
end
|
39
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
|
+
|
40
98
|
context "configuration" do
|
41
99
|
it "should store configuration" do
|
42
100
|
config = Rdkafka::Config.new
|
@@ -50,7 +108,7 @@ describe Rdkafka::Config do
|
|
50
108
|
end
|
51
109
|
|
52
110
|
it "should create a consumer with valid config" do
|
53
|
-
consumer =
|
111
|
+
consumer = rdkafka_consumer_config.consumer
|
54
112
|
expect(consumer).to be_a Rdkafka::Consumer
|
55
113
|
consumer.close
|
56
114
|
end
|
@@ -78,7 +136,7 @@ describe Rdkafka::Config do
|
|
78
136
|
end
|
79
137
|
|
80
138
|
it "should create a producer with valid config" do
|
81
|
-
producer =
|
139
|
+
producer = rdkafka_consumer_config.producer
|
82
140
|
expect(producer).to be_a Rdkafka::Producer
|
83
141
|
producer.close
|
84
142
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "ostruct"
|
3
|
+
require 'securerandom'
|
3
4
|
|
4
5
|
describe Rdkafka::Consumer do
|
5
|
-
let(:
|
6
|
-
let(:
|
7
|
-
let(:producer) { config.producer }
|
6
|
+
let(:consumer) { rdkafka_consumer_config.consumer }
|
7
|
+
let(:producer) { rdkafka_producer_config.producer }
|
8
8
|
|
9
9
|
after { consumer.close }
|
10
10
|
after { producer.close }
|
@@ -271,6 +271,14 @@ describe Rdkafka::Consumer do
|
|
271
271
|
describe "#close" do
|
272
272
|
it "should close a consumer" do
|
273
273
|
consumer.subscribe("consume_test_topic")
|
274
|
+
100.times do |i|
|
275
|
+
report = producer.produce(
|
276
|
+
topic: "consume_test_topic",
|
277
|
+
payload: "payload #{i}",
|
278
|
+
key: "key #{i}",
|
279
|
+
partition: 0
|
280
|
+
).wait
|
281
|
+
end
|
274
282
|
consumer.close
|
275
283
|
expect {
|
276
284
|
consumer.poll(100)
|
@@ -319,7 +327,7 @@ describe Rdkafka::Consumer do
|
|
319
327
|
before :all do
|
320
328
|
# Make sure there are some messages.
|
321
329
|
handles = []
|
322
|
-
producer =
|
330
|
+
producer = rdkafka_producer_config.producer
|
323
331
|
10.times do
|
324
332
|
(0..2).each do |i|
|
325
333
|
handles << producer.produce(
|
@@ -395,7 +403,7 @@ describe Rdkafka::Consumer do
|
|
395
403
|
config = {}
|
396
404
|
config[:'enable.auto.offset.store'] = false
|
397
405
|
config[:'enable.auto.commit'] = false
|
398
|
-
@new_consumer =
|
406
|
+
@new_consumer = rdkafka_consumer_config(config).consumer
|
399
407
|
@new_consumer.subscribe("consume_test_topic")
|
400
408
|
wait_for_assignment(@new_consumer)
|
401
409
|
end
|
@@ -450,13 +458,13 @@ describe Rdkafka::Consumer do
|
|
450
458
|
end
|
451
459
|
|
452
460
|
describe "#lag" do
|
453
|
-
let(:
|
461
|
+
let(:consumer) { rdkafka_consumer_config(:"enable.partition.eof" => true).consumer }
|
454
462
|
|
455
463
|
it "should calculate the consumer lag" do
|
456
464
|
# Make sure there's a message in every partition and
|
457
465
|
# wait for the message to make sure everything is committed.
|
458
466
|
(0..2).each do |i|
|
459
|
-
|
467
|
+
producer.produce(
|
460
468
|
topic: "consume_test_topic",
|
461
469
|
key: "key lag #{i}",
|
462
470
|
partition: i
|
@@ -499,7 +507,7 @@ describe Rdkafka::Consumer do
|
|
499
507
|
|
500
508
|
# Produce message on every topic again
|
501
509
|
(0..2).each do |i|
|
502
|
-
|
510
|
+
producer.produce(
|
503
511
|
topic: "consume_test_topic",
|
504
512
|
key: "key lag #{i}",
|
505
513
|
partition: i
|
@@ -670,52 +678,295 @@ describe Rdkafka::Consumer do
|
|
670
678
|
end
|
671
679
|
end
|
672
680
|
|
673
|
-
describe "
|
674
|
-
|
675
|
-
listener = Struct.new(:queue) do
|
676
|
-
def on_partitions_assigned(consumer, list)
|
677
|
-
collect(:assign, list)
|
678
|
-
end
|
681
|
+
describe "#each_batch" do
|
682
|
+
let(:message_payload) { 'a' * 10 }
|
679
683
|
|
680
|
-
|
681
|
-
|
682
|
-
|
684
|
+
before do
|
685
|
+
@topic = SecureRandom.base64(10).tr('+=/', '')
|
686
|
+
end
|
683
687
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
end
|
688
|
-
end.new([])
|
688
|
+
after do
|
689
|
+
@topic = nil
|
690
|
+
end
|
689
691
|
|
690
|
-
|
692
|
+
def topic_name
|
693
|
+
@topic
|
694
|
+
end
|
691
695
|
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
+
def produce_n(n)
|
697
|
+
handles = []
|
698
|
+
n.times do |i|
|
699
|
+
handles << producer.produce(
|
700
|
+
topic: topic_name,
|
701
|
+
payload: Time.new.to_f.to_s,
|
702
|
+
key: i.to_s,
|
703
|
+
partition: 0
|
704
|
+
)
|
705
|
+
end
|
706
|
+
handles.each(&:wait)
|
696
707
|
end
|
697
708
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
end
|
709
|
+
def new_message
|
710
|
+
instance_double("Rdkafka::Consumer::Message").tap do |message|
|
711
|
+
allow(message).to receive(:payload).and_return(message_payload)
|
712
|
+
end
|
713
|
+
end
|
704
714
|
|
705
|
-
|
706
|
-
|
707
|
-
|
715
|
+
it "retrieves messages produced into a topic" do
|
716
|
+
# This is the only each_batch test that actually produces real messages
|
717
|
+
# into a topic in the real kafka of the container.
|
718
|
+
#
|
719
|
+
# The other tests stub 'poll' which makes them faster and more reliable,
|
720
|
+
# but it makes sense to keep a single test with a fully integrated flow.
|
721
|
+
# This will help to catch breaking changes in the behavior of 'poll',
|
722
|
+
# libdrkafka, or Kafka.
|
723
|
+
#
|
724
|
+
# This is, in effect, an integration test and the subsequent specs are
|
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)
|
728
|
+
consumer.subscribe(topic_name)
|
729
|
+
produce_n 42
|
730
|
+
all_yields = []
|
731
|
+
consumer.each_batch(max_items: 10) do |batch|
|
732
|
+
all_yields << batch
|
733
|
+
break if all_yields.flatten.size >= 42
|
734
|
+
end
|
735
|
+
expect(all_yields.flatten.first).to be_a Rdkafka::Consumer::Message
|
736
|
+
expect(all_yields.flatten.size).to eq 42
|
737
|
+
expect(all_yields.size).to be > 4
|
738
|
+
expect(all_yields.flatten.map(&:key)).to eq (0..41).map { |x| x.to_s }
|
739
|
+
end
|
740
|
+
|
741
|
+
it "should batch poll results and yield arrays of messages" do
|
742
|
+
consumer.subscribe(topic_name)
|
743
|
+
all_yields = []
|
744
|
+
expect(consumer)
|
745
|
+
.to receive(:poll)
|
746
|
+
.exactly(10).times
|
747
|
+
.and_return(new_message)
|
748
|
+
consumer.each_batch(max_items: 10) do |batch|
|
749
|
+
all_yields << batch
|
750
|
+
break if all_yields.flatten.size >= 10
|
751
|
+
end
|
752
|
+
expect(all_yields.first).to be_instance_of(Array)
|
753
|
+
expect(all_yields.flatten.size).to eq 10
|
754
|
+
non_empty_yields = all_yields.reject { |batch| batch.empty? }
|
755
|
+
expect(non_empty_yields.size).to be < 10
|
756
|
+
end
|
757
|
+
|
758
|
+
it "should yield a partial batch if the timeout is hit with some messages" do
|
759
|
+
consumer.subscribe(topic_name)
|
760
|
+
poll_count = 0
|
761
|
+
expect(consumer)
|
762
|
+
.to receive(:poll)
|
763
|
+
.at_least(3).times do
|
764
|
+
poll_count = poll_count + 1
|
765
|
+
if poll_count > 2
|
766
|
+
sleep 0.1
|
767
|
+
nil
|
768
|
+
else
|
769
|
+
new_message
|
708
770
|
end
|
709
|
-
end
|
771
|
+
end
|
772
|
+
all_yields = []
|
773
|
+
consumer.each_batch(max_items: 10) do |batch|
|
774
|
+
all_yields << batch
|
775
|
+
break if all_yields.flatten.size >= 2
|
776
|
+
end
|
777
|
+
expect(all_yields.flatten.size).to eq 2
|
778
|
+
end
|
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)
|
783
|
+
consumer.subscribe(topic_name)
|
784
|
+
consumer.each_batch do |batch|
|
785
|
+
expect(batch).to eq([])
|
786
|
+
break
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
it "should yield batchs of max_items in size if messages are already fetched" do
|
791
|
+
yielded_batches = []
|
792
|
+
expect(consumer)
|
793
|
+
.to receive(:poll)
|
794
|
+
.with(anything)
|
795
|
+
.exactly(20).times
|
796
|
+
.and_return(new_message)
|
797
|
+
|
798
|
+
consumer.each_batch(max_items: 10, timeout_ms: 500) do |batch|
|
799
|
+
yielded_batches << batch
|
800
|
+
break if yielded_batches.flatten.size >= 20
|
801
|
+
break if yielded_batches.size >= 20 # so failure doesn't hang
|
802
|
+
end
|
803
|
+
expect(yielded_batches.size).to eq 2
|
804
|
+
expect(yielded_batches.map(&:size)).to eq 2.times.map { 10 }
|
805
|
+
end
|
806
|
+
|
807
|
+
it "should yield batchs as soon as bytes_threshold is hit" do
|
808
|
+
yielded_batches = []
|
809
|
+
expect(consumer)
|
810
|
+
.to receive(:poll)
|
811
|
+
.with(anything)
|
812
|
+
.exactly(20).times
|
813
|
+
.and_return(new_message)
|
814
|
+
|
815
|
+
consumer.each_batch(bytes_threshold: message_payload.size * 4, timeout_ms: 500) do |batch|
|
816
|
+
yielded_batches << batch
|
817
|
+
break if yielded_batches.flatten.size >= 20
|
818
|
+
break if yielded_batches.size >= 20 # so failure doesn't hang
|
819
|
+
end
|
820
|
+
expect(yielded_batches.size).to eq 5
|
821
|
+
expect(yielded_batches.map(&:size)).to eq 5.times.map { 4 }
|
822
|
+
end
|
710
823
|
|
711
|
-
|
824
|
+
context "error raised from poll and yield_on_error is true" do
|
825
|
+
it "should yield buffered exceptions on rebalance, then break" do
|
826
|
+
config = rdkafka_consumer_config(
|
827
|
+
{
|
828
|
+
:"enable.auto.commit" => false,
|
829
|
+
:"enable.auto.offset.store" => false
|
830
|
+
}
|
831
|
+
)
|
832
|
+
consumer = config.consumer
|
833
|
+
consumer.subscribe(topic_name)
|
834
|
+
loop_count = 0
|
835
|
+
batches_yielded = []
|
836
|
+
exceptions_yielded = []
|
837
|
+
each_batch_iterations = 0
|
838
|
+
poll_count = 0
|
839
|
+
expect(consumer)
|
840
|
+
.to receive(:poll)
|
841
|
+
.with(anything)
|
842
|
+
.exactly(3).times
|
843
|
+
.and_wrap_original do |method, *args|
|
844
|
+
poll_count = poll_count + 1
|
845
|
+
if poll_count == 3
|
846
|
+
raise Rdkafka::RdkafkaError.new(27,
|
847
|
+
"partitions ... too ... heavy ... must ... rebalance")
|
848
|
+
else
|
849
|
+
new_message
|
850
|
+
end
|
851
|
+
end
|
852
|
+
expect {
|
853
|
+
consumer.each_batch(max_items: 30, yield_on_error: true) do |batch, pending_error|
|
854
|
+
batches_yielded << batch
|
855
|
+
exceptions_yielded << pending_error
|
856
|
+
each_batch_iterations = each_batch_iterations + 1
|
857
|
+
end
|
858
|
+
}.to raise_error(Rdkafka::RdkafkaError)
|
859
|
+
expect(poll_count).to eq 3
|
860
|
+
expect(each_batch_iterations).to eq 1
|
861
|
+
expect(batches_yielded.size).to eq 1
|
862
|
+
expect(batches_yielded.first.size).to eq 2
|
863
|
+
expect(exceptions_yielded.flatten.size).to eq 1
|
864
|
+
expect(exceptions_yielded.flatten.first).to be_instance_of(Rdkafka::RdkafkaError)
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
context "error raised from poll and yield_on_error is false" do
|
869
|
+
it "should yield buffered exceptions on rebalance, then break" do
|
870
|
+
config = rdkafka_consumer_config(
|
871
|
+
{
|
872
|
+
:"enable.auto.commit" => false,
|
873
|
+
:"enable.auto.offset.store" => false
|
874
|
+
}
|
875
|
+
)
|
876
|
+
consumer = config.consumer
|
877
|
+
consumer.subscribe(topic_name)
|
878
|
+
loop_count = 0
|
879
|
+
batches_yielded = []
|
880
|
+
exceptions_yielded = []
|
881
|
+
each_batch_iterations = 0
|
882
|
+
poll_count = 0
|
883
|
+
expect(consumer)
|
884
|
+
.to receive(:poll)
|
885
|
+
.with(anything)
|
886
|
+
.exactly(3).times
|
887
|
+
.and_wrap_original do |method, *args|
|
888
|
+
poll_count = poll_count + 1
|
889
|
+
if poll_count == 3
|
890
|
+
raise Rdkafka::RdkafkaError.new(27,
|
891
|
+
"partitions ... too ... heavy ... must ... rebalance")
|
892
|
+
else
|
893
|
+
new_message
|
894
|
+
end
|
895
|
+
end
|
896
|
+
expect {
|
897
|
+
consumer.each_batch(max_items: 30, yield_on_error: false) do |batch, pending_error|
|
898
|
+
batches_yielded << batch
|
899
|
+
exceptions_yielded << pending_error
|
900
|
+
each_batch_iterations = each_batch_iterations + 1
|
901
|
+
end
|
902
|
+
}.to raise_error(Rdkafka::RdkafkaError)
|
903
|
+
expect(poll_count).to eq 3
|
904
|
+
expect(each_batch_iterations).to eq 0
|
905
|
+
expect(batches_yielded.size).to eq 0
|
906
|
+
expect(exceptions_yielded.size).to eq 0
|
907
|
+
end
|
908
|
+
end
|
909
|
+
end
|
712
910
|
|
713
|
-
|
911
|
+
describe "a rebalance listener" do
|
912
|
+
let(:consumer) do
|
913
|
+
config = rdkafka_consumer_config
|
914
|
+
config.consumer_rebalance_listener = listener
|
915
|
+
config.consumer
|
916
|
+
end
|
917
|
+
|
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
|
924
|
+
|
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
|
935
|
+
|
936
|
+
it "should get notifications" do
|
937
|
+
notify_listener(listener)
|
938
|
+
|
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
|
944
|
+
end
|
945
|
+
|
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
|
953
|
+
|
954
|
+
def on_partitions_revoked(consumer, list)
|
955
|
+
queue << :revoked
|
956
|
+
raise 'boom'
|
957
|
+
end
|
958
|
+
end.new([])
|
959
|
+
end
|
960
|
+
|
961
|
+
it 'should handle callback exceptions' do
|
962
|
+
notify_listener(listener)
|
963
|
+
|
964
|
+
expect(listener.queue).to eq([:assigned, :revoked])
|
965
|
+
end
|
714
966
|
end
|
715
967
|
|
716
968
|
def notify_listener(listener)
|
717
969
|
# 1. subscribe and poll
|
718
|
-
config.consumer_rebalance_listener = listener
|
719
970
|
consumer.subscribe("consume_test_topic")
|
720
971
|
wait_for_assignment(consumer)
|
721
972
|
consumer.poll(100)
|
@@ -736,6 +987,7 @@ describe Rdkafka::Consumer do
|
|
736
987
|
{
|
737
988
|
:subscribe => [ nil ],
|
738
989
|
:unsubscribe => nil,
|
990
|
+
:each_batch => nil,
|
739
991
|
:pause => [ nil ],
|
740
992
|
:resume => [ nil ],
|
741
993
|
:subscription => nil,
|
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
require "securerandom"
|
3
3
|
|
4
4
|
describe Rdkafka::Metadata do
|
5
|
-
let(: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:
|
21
|
+
}.to raise_exception(Rdkafka::RdkafkaError, "Broker: Unknown topic or partition (unknown_topic_or_part)")
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|