karafka-rdkafka 0.20.0.rc3-x86_64-linux-gnu

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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CODEOWNERS +3 -0
  3. data/.github/FUNDING.yml +1 -0
  4. data/.github/workflows/ci_linux_x86_64_gnu.yml +248 -0
  5. data/.github/workflows/ci_macos_arm64.yml +301 -0
  6. data/.github/workflows/push_linux_x86_64_gnu.yml +60 -0
  7. data/.github/workflows/push_ruby.yml +37 -0
  8. data/.github/workflows/verify-action-pins.yml +16 -0
  9. data/.gitignore +15 -0
  10. data/.rspec +2 -0
  11. data/.ruby-gemset +1 -0
  12. data/.ruby-version +1 -0
  13. data/.yardopts +2 -0
  14. data/CHANGELOG.md +323 -0
  15. data/Gemfile +5 -0
  16. data/MIT-LICENSE +22 -0
  17. data/README.md +177 -0
  18. data/Rakefile +96 -0
  19. data/docker-compose.yml +25 -0
  20. data/ext/README.md +19 -0
  21. data/ext/Rakefile +131 -0
  22. data/ext/build_common.sh +361 -0
  23. data/ext/build_linux_x86_64_gnu.sh +306 -0
  24. data/ext/build_macos_arm64.sh +550 -0
  25. data/ext/librdkafka.so +0 -0
  26. data/karafka-rdkafka.gemspec +61 -0
  27. data/lib/rdkafka/abstract_handle.rb +116 -0
  28. data/lib/rdkafka/admin/acl_binding_result.rb +51 -0
  29. data/lib/rdkafka/admin/config_binding_result.rb +30 -0
  30. data/lib/rdkafka/admin/config_resource_binding_result.rb +18 -0
  31. data/lib/rdkafka/admin/create_acl_handle.rb +28 -0
  32. data/lib/rdkafka/admin/create_acl_report.rb +24 -0
  33. data/lib/rdkafka/admin/create_partitions_handle.rb +30 -0
  34. data/lib/rdkafka/admin/create_partitions_report.rb +6 -0
  35. data/lib/rdkafka/admin/create_topic_handle.rb +32 -0
  36. data/lib/rdkafka/admin/create_topic_report.rb +24 -0
  37. data/lib/rdkafka/admin/delete_acl_handle.rb +30 -0
  38. data/lib/rdkafka/admin/delete_acl_report.rb +23 -0
  39. data/lib/rdkafka/admin/delete_groups_handle.rb +28 -0
  40. data/lib/rdkafka/admin/delete_groups_report.rb +24 -0
  41. data/lib/rdkafka/admin/delete_topic_handle.rb +32 -0
  42. data/lib/rdkafka/admin/delete_topic_report.rb +24 -0
  43. data/lib/rdkafka/admin/describe_acl_handle.rb +30 -0
  44. data/lib/rdkafka/admin/describe_acl_report.rb +24 -0
  45. data/lib/rdkafka/admin/describe_configs_handle.rb +33 -0
  46. data/lib/rdkafka/admin/describe_configs_report.rb +48 -0
  47. data/lib/rdkafka/admin/incremental_alter_configs_handle.rb +33 -0
  48. data/lib/rdkafka/admin/incremental_alter_configs_report.rb +48 -0
  49. data/lib/rdkafka/admin.rb +832 -0
  50. data/lib/rdkafka/bindings.rb +582 -0
  51. data/lib/rdkafka/callbacks.rb +415 -0
  52. data/lib/rdkafka/config.rb +398 -0
  53. data/lib/rdkafka/consumer/headers.rb +79 -0
  54. data/lib/rdkafka/consumer/message.rb +86 -0
  55. data/lib/rdkafka/consumer/partition.rb +57 -0
  56. data/lib/rdkafka/consumer/topic_partition_list.rb +190 -0
  57. data/lib/rdkafka/consumer.rb +663 -0
  58. data/lib/rdkafka/error.rb +201 -0
  59. data/lib/rdkafka/helpers/oauth.rb +58 -0
  60. data/lib/rdkafka/helpers/time.rb +14 -0
  61. data/lib/rdkafka/metadata.rb +115 -0
  62. data/lib/rdkafka/native_kafka.rb +139 -0
  63. data/lib/rdkafka/producer/delivery_handle.rb +48 -0
  64. data/lib/rdkafka/producer/delivery_report.rb +45 -0
  65. data/lib/rdkafka/producer/partitions_count_cache.rb +216 -0
  66. data/lib/rdkafka/producer.rb +492 -0
  67. data/lib/rdkafka/version.rb +7 -0
  68. data/lib/rdkafka.rb +54 -0
  69. data/renovate.json +92 -0
  70. data/spec/rdkafka/abstract_handle_spec.rb +117 -0
  71. data/spec/rdkafka/admin/create_acl_handle_spec.rb +56 -0
  72. data/spec/rdkafka/admin/create_acl_report_spec.rb +18 -0
  73. data/spec/rdkafka/admin/create_topic_handle_spec.rb +54 -0
  74. data/spec/rdkafka/admin/create_topic_report_spec.rb +16 -0
  75. data/spec/rdkafka/admin/delete_acl_handle_spec.rb +85 -0
  76. data/spec/rdkafka/admin/delete_acl_report_spec.rb +72 -0
  77. data/spec/rdkafka/admin/delete_topic_handle_spec.rb +54 -0
  78. data/spec/rdkafka/admin/delete_topic_report_spec.rb +16 -0
  79. data/spec/rdkafka/admin/describe_acl_handle_spec.rb +85 -0
  80. data/spec/rdkafka/admin/describe_acl_report_spec.rb +73 -0
  81. data/spec/rdkafka/admin_spec.rb +769 -0
  82. data/spec/rdkafka/bindings_spec.rb +222 -0
  83. data/spec/rdkafka/callbacks_spec.rb +20 -0
  84. data/spec/rdkafka/config_spec.rb +258 -0
  85. data/spec/rdkafka/consumer/headers_spec.rb +73 -0
  86. data/spec/rdkafka/consumer/message_spec.rb +139 -0
  87. data/spec/rdkafka/consumer/partition_spec.rb +57 -0
  88. data/spec/rdkafka/consumer/topic_partition_list_spec.rb +248 -0
  89. data/spec/rdkafka/consumer_spec.rb +1299 -0
  90. data/spec/rdkafka/error_spec.rb +95 -0
  91. data/spec/rdkafka/metadata_spec.rb +79 -0
  92. data/spec/rdkafka/native_kafka_spec.rb +130 -0
  93. data/spec/rdkafka/producer/delivery_handle_spec.rb +60 -0
  94. data/spec/rdkafka/producer/delivery_report_spec.rb +25 -0
  95. data/spec/rdkafka/producer/partitions_count_cache_spec.rb +359 -0
  96. data/spec/rdkafka/producer/partitions_count_spec.rb +359 -0
  97. data/spec/rdkafka/producer_spec.rb +1234 -0
  98. data/spec/spec_helper.rb +181 -0
  99. metadata +244 -0
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Rdkafka::RdkafkaError do
4
+ it "should raise a type error for a nil response" do
5
+ expect {
6
+ Rdkafka::RdkafkaError.new(nil)
7
+ }.to raise_error TypeError
8
+ end
9
+
10
+ it "should create an error with a message prefix" do
11
+ expect(Rdkafka::RdkafkaError.new(10, "message prefix").message_prefix).to eq "message prefix"
12
+ end
13
+
14
+ it "should have empty frozen details by default" do
15
+ error = Rdkafka::RdkafkaError.new(10, "message prefix")
16
+ expect(error.details).to eq({})
17
+ expect(error.details).to be_frozen
18
+ end
19
+
20
+ it "should create an error with a broker message" do
21
+ expect(Rdkafka::RdkafkaError.new(10, broker_message: "broker message").broker_message).to eq "broker message"
22
+ end
23
+
24
+ describe "#code" do
25
+ it "should handle an invalid response" do
26
+ expect(Rdkafka::RdkafkaError.new(933975).code).to eq :err_933975?
27
+ end
28
+
29
+ it "should return error messages from rdkafka" do
30
+ expect(Rdkafka::RdkafkaError.new(10).code).to eq :msg_size_too_large
31
+ end
32
+
33
+ it "should strip a leading underscore" do
34
+ expect(Rdkafka::RdkafkaError.new(-191).code).to eq :partition_eof
35
+ end
36
+ end
37
+
38
+ describe "#to_s" do
39
+ it "should handle an invalid response" do
40
+ expect(Rdkafka::RdkafkaError.new(933975).to_s).to eq "Err-933975? (err_933975?)"
41
+ end
42
+
43
+ it "should return error messages from rdkafka" do
44
+ expect(Rdkafka::RdkafkaError.new(10).to_s).to eq "Broker: Message size too large (msg_size_too_large)"
45
+ end
46
+
47
+ it "should add the message prefix if present" do
48
+ expect(Rdkafka::RdkafkaError.new(10, "Error explanation").to_s).to eq "Error explanation - Broker: Message size too large (msg_size_too_large)"
49
+ end
50
+ end
51
+
52
+ describe "#message" do
53
+ it "should handle an invalid response" do
54
+ expect(Rdkafka::RdkafkaError.new(933975).message).to eq "Err-933975? (err_933975?)"
55
+ end
56
+
57
+ it "should return error messages from rdkafka" do
58
+ expect(Rdkafka::RdkafkaError.new(10).message).to eq "Broker: Message size too large (msg_size_too_large)"
59
+ end
60
+
61
+ it "should add the message prefix if present" do
62
+ expect(Rdkafka::RdkafkaError.new(10, "Error explanation").message).to eq "Error explanation - Broker: Message size too large (msg_size_too_large)"
63
+ end
64
+ end
65
+
66
+ describe "#is_partition_eof?" do
67
+ it "should be false when not partition eof" do
68
+ expect(Rdkafka::RdkafkaError.new(933975).is_partition_eof?).to be false
69
+ end
70
+
71
+ it "should be true when partition eof" do
72
+ expect(Rdkafka::RdkafkaError.new(-191).is_partition_eof?).to be true
73
+ end
74
+ end
75
+
76
+ describe "#==" do
77
+ subject { Rdkafka::RdkafkaError.new(10, "Error explanation") }
78
+
79
+ it "should equal another error with the same content" do
80
+ expect(subject).to eq Rdkafka::RdkafkaError.new(10, "Error explanation")
81
+ end
82
+
83
+ it "should not equal another error with a different error code" do
84
+ expect(subject).not_to eq Rdkafka::RdkafkaError.new(20, "Error explanation")
85
+ end
86
+
87
+ it "should not equal another error with a different message" do
88
+ expect(subject).not_to eq Rdkafka::RdkafkaError.new(10, "Different error explanation")
89
+ end
90
+
91
+ it "should not equal another error with no message" do
92
+ expect(subject).not_to eq Rdkafka::RdkafkaError.new(10)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ describe Rdkafka::Metadata do
6
+ let(:config) { rdkafka_consumer_config }
7
+ let(:native_config) { config.send(:native_config) }
8
+ let(:native_kafka) { config.send(:native_kafka, native_config, :rd_kafka_consumer) }
9
+
10
+ after do
11
+ Rdkafka::Bindings.rd_kafka_consumer_close(native_kafka)
12
+ Rdkafka::Bindings.rd_kafka_destroy(native_kafka)
13
+ end
14
+
15
+ context "passing in a topic name" do
16
+ context "that is non-existent topic" do
17
+ let(:topic_name) { SecureRandom.uuid.to_s }
18
+
19
+ it "raises an appropriate exception" do
20
+ expect {
21
+ described_class.new(native_kafka, topic_name)
22
+ }.to raise_exception(Rdkafka::RdkafkaError, "Broker: Unknown topic or partition (unknown_topic_or_part)")
23
+ end
24
+ end
25
+
26
+ context "that is one of our test topics" do
27
+ subject { described_class.new(native_kafka, topic_name) }
28
+ let(:topic_name) { "partitioner_test_topic" }
29
+
30
+ it "#brokers returns our single broker" do
31
+ expect(subject.brokers.length).to eq(1)
32
+ expect(subject.brokers[0][:broker_id]).to eq(1)
33
+ expect(%w[127.0.0.1 localhost]).to include(subject.brokers[0][:broker_name])
34
+ expect(subject.brokers[0][:broker_port]).to eq(9092)
35
+ end
36
+
37
+ it "#topics returns data on our test topic" do
38
+ expect(subject.topics.length).to eq(1)
39
+ expect(subject.topics[0][:partition_count]).to eq(25)
40
+ expect(subject.topics[0][:partitions].length).to eq(25)
41
+ expect(subject.topics[0][:topic_name]).to eq(topic_name)
42
+ end
43
+ end
44
+ end
45
+
46
+ context "not passing in a topic name" do
47
+ subject { described_class.new(native_kafka, topic_name) }
48
+ let(:topic_name) { nil }
49
+ let(:test_topics) {
50
+ %w(consume_test_topic empty_test_topic load_test_topic produce_test_topic rake_test_topic watermarks_test_topic partitioner_test_topic)
51
+ } # Test topics crated in spec_helper.rb
52
+
53
+ it "#brokers returns our single broker" do
54
+ expect(subject.brokers.length).to eq(1)
55
+ expect(subject.brokers[0][:broker_id]).to eq(1)
56
+ expect(%w[127.0.0.1 localhost]).to include(subject.brokers[0][:broker_name])
57
+ expect(subject.brokers[0][:broker_port]).to eq(9092)
58
+ end
59
+
60
+ it "#topics returns data about all of our test topics" do
61
+ result = subject.topics.map { |topic| topic[:topic_name] }
62
+ expect(result).to include(*test_topics)
63
+ end
64
+ end
65
+
66
+ context "when a non-zero error code is returned" do
67
+ let(:topic_name) { SecureRandom.uuid.to_s }
68
+
69
+ before do
70
+ allow(Rdkafka::Bindings).to receive(:rd_kafka_metadata).and_return(-165)
71
+ end
72
+
73
+ it "creating the instance raises an exception" do
74
+ expect {
75
+ described_class.new(native_kafka, topic_name)
76
+ }.to raise_error(Rdkafka::RdkafkaError, /Local: Required feature not supported by broker \(unsupported_feature\)/)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Rdkafka::NativeKafka do
4
+ let(:config) { rdkafka_producer_config }
5
+ let(:native) { config.send(:native_kafka, config.send(:native_config), :rd_kafka_producer) }
6
+ let(:closing) { false }
7
+ let(:thread) { double(Thread) }
8
+ let(:opaque) { Rdkafka::Opaque.new }
9
+
10
+ subject(:client) { described_class.new(native, run_polling_thread: true, opaque: opaque) }
11
+
12
+ before do
13
+ allow(Rdkafka::Bindings).to receive(:rd_kafka_name).and_return('producer-1')
14
+ allow(Thread).to receive(:new).and_return(thread)
15
+ allow(thread).to receive(:name=).with("rdkafka.native_kafka#producer-1")
16
+ allow(thread).to receive(:[]=).with(:closing, anything)
17
+ allow(thread).to receive(:join)
18
+ allow(thread).to receive(:abort_on_exception=).with(anything)
19
+ end
20
+
21
+ after { client.close }
22
+
23
+ context "defaults" do
24
+ it "sets the thread name" do
25
+ expect(thread).to receive(:name=).with("rdkafka.native_kafka#producer-1")
26
+
27
+ client
28
+ end
29
+
30
+ it "sets the thread to abort on exception" do
31
+ expect(thread).to receive(:abort_on_exception=).with(true)
32
+
33
+ client
34
+ end
35
+
36
+ it "sets the thread `closing` flag to false" do
37
+ expect(thread).to receive(:[]=).with(:closing, false)
38
+
39
+ client
40
+ end
41
+ end
42
+
43
+ context "the polling thread" do
44
+ it "is created" do
45
+ expect(Thread).to receive(:new)
46
+
47
+ client
48
+ end
49
+ end
50
+
51
+ it "exposes the inner client" do
52
+ client.with_inner do |inner|
53
+ expect(inner).to eq(native)
54
+ end
55
+ end
56
+
57
+ context "when client was not yet closed (`nil`)" do
58
+ it "is not closed" do
59
+ expect(client.closed?).to eq(false)
60
+ end
61
+
62
+ context "and attempt to close" do
63
+ it "calls the `destroy` binding" do
64
+ expect(Rdkafka::Bindings).to receive(:rd_kafka_destroy).with(native).and_call_original
65
+
66
+ client.close
67
+ end
68
+
69
+ it "indicates to the polling thread that it is closing" do
70
+ expect(thread).to receive(:[]=).with(:closing, true)
71
+
72
+ client.close
73
+ end
74
+
75
+ it "joins the polling thread" do
76
+ expect(thread).to receive(:join)
77
+
78
+ client.close
79
+ end
80
+
81
+ it "closes and unassign the native client" do
82
+ client.close
83
+
84
+ expect(client.closed?).to eq(true)
85
+ end
86
+ end
87
+ end
88
+
89
+ context "when client was already closed" do
90
+ before { client.close }
91
+
92
+ it "is closed" do
93
+ expect(client.closed?).to eq(true)
94
+ end
95
+
96
+ context "and attempt to close again" do
97
+ it "does not call the `destroy` binding" do
98
+ expect(Rdkafka::Bindings).not_to receive(:rd_kafka_destroy_flags)
99
+
100
+ client.close
101
+ end
102
+
103
+ it "does not indicate to the polling thread that it is closing" do
104
+ expect(thread).not_to receive(:[]=).with(:closing, true)
105
+
106
+ client.close
107
+ end
108
+
109
+ it "does not join the polling thread" do
110
+ expect(thread).not_to receive(:join)
111
+
112
+ client.close
113
+ end
114
+
115
+ it "does not close and unassign the native client again" do
116
+ client.close
117
+
118
+ expect(client.closed?).to eq(true)
119
+ end
120
+ end
121
+ end
122
+
123
+ it "provides a finalizer that closes the native kafka client" do
124
+ expect(client.closed?).to eq(false)
125
+
126
+ client.finalizer.call("some-ignored-object-id")
127
+
128
+ expect(client.closed?).to eq(true)
129
+ end
130
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Rdkafka::Producer::DeliveryHandle do
4
+ let(:response) { 0 }
5
+
6
+ subject do
7
+ Rdkafka::Producer::DeliveryHandle.new.tap do |handle|
8
+ handle[:pending] = pending_handle
9
+ handle[:response] = response
10
+ handle[:partition] = 2
11
+ handle[:offset] = 100
12
+ handle.topic = "produce_test_topic"
13
+ end
14
+ end
15
+
16
+ describe "#wait" do
17
+ let(:pending_handle) { true }
18
+
19
+ it "should wait until the timeout and then raise an error" do
20
+ expect {
21
+ subject.wait(max_wait_timeout: 0.1)
22
+ }.to raise_error Rdkafka::Producer::DeliveryHandle::WaitTimeoutError, /delivery/
23
+ end
24
+
25
+ context "when not pending anymore and no error" do
26
+ let(:pending_handle) { false }
27
+
28
+ it "should return a delivery report" do
29
+ report = subject.wait
30
+
31
+ expect(report.partition).to eq(2)
32
+ expect(report.offset).to eq(100)
33
+ expect(report.topic_name).to eq("produce_test_topic")
34
+ end
35
+
36
+ it "should wait without a timeout" do
37
+ report = subject.wait(max_wait_timeout: nil)
38
+
39
+ expect(report.partition).to eq(2)
40
+ expect(report.offset).to eq(100)
41
+ expect(report.topic_name).to eq("produce_test_topic")
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#create_result' do
47
+ let(:pending_handle) { false }
48
+ let(:report) { subject.create_result }
49
+
50
+ context 'when response is 0' do
51
+ it { expect(report.error).to eq(nil) }
52
+ end
53
+
54
+ context 'when response is not 0' do
55
+ let(:response) { 1 }
56
+
57
+ it { expect(report.error).to eq(Rdkafka::RdkafkaError.new(response)) }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Rdkafka::Producer::DeliveryReport do
4
+ subject { Rdkafka::Producer::DeliveryReport.new(2, 100, "topic", -1) }
5
+
6
+ it "should get the partition" do
7
+ expect(subject.partition).to eq 2
8
+ end
9
+
10
+ it "should get the offset" do
11
+ expect(subject.offset).to eq 100
12
+ end
13
+
14
+ it "should get the topic_name" do
15
+ expect(subject.topic_name).to eq "topic"
16
+ end
17
+
18
+ it "should get the same topic name under topic alias" do
19
+ expect(subject.topic).to eq "topic"
20
+ end
21
+
22
+ it "should get the error" do
23
+ expect(subject.error).to eq -1
24
+ end
25
+ end