rdkafka 0.22.2 → 0.27.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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -3
  3. data/Gemfile +8 -0
  4. data/Gemfile.lint +14 -0
  5. data/Gemfile.lint.lock +123 -0
  6. data/README.md +19 -14
  7. data/Rakefile +21 -21
  8. data/bin/verify_kafka_warnings +39 -0
  9. data/dist/{librdkafka-2.8.0.tar.gz → librdkafka-2.14.0.tar.gz} +0 -0
  10. data/docker-compose-ssl.yml +35 -0
  11. data/docker-compose.yml +2 -2
  12. data/ext/Rakefile +27 -27
  13. data/lib/rdkafka/abstract_handle.rb +23 -5
  14. data/lib/rdkafka/admin/acl_binding_result.rb +5 -5
  15. data/lib/rdkafka/admin/config_resource_binding_result.rb +1 -0
  16. data/lib/rdkafka/admin/create_acl_handle.rb +7 -4
  17. data/lib/rdkafka/admin/create_acl_report.rb +3 -2
  18. data/lib/rdkafka/admin/create_partitions_handle.rb +8 -5
  19. data/lib/rdkafka/admin/create_partitions_report.rb +1 -0
  20. data/lib/rdkafka/admin/create_topic_handle.rb +8 -5
  21. data/lib/rdkafka/admin/create_topic_report.rb +3 -0
  22. data/lib/rdkafka/admin/delete_acl_handle.rb +9 -6
  23. data/lib/rdkafka/admin/delete_acl_report.rb +5 -3
  24. data/lib/rdkafka/admin/delete_groups_handle.rb +10 -5
  25. data/lib/rdkafka/admin/delete_groups_report.rb +3 -0
  26. data/lib/rdkafka/admin/delete_topic_handle.rb +8 -5
  27. data/lib/rdkafka/admin/delete_topic_report.rb +3 -0
  28. data/lib/rdkafka/admin/describe_acl_handle.rb +9 -6
  29. data/lib/rdkafka/admin/describe_acl_report.rb +5 -3
  30. data/lib/rdkafka/admin/describe_configs_handle.rb +7 -4
  31. data/lib/rdkafka/admin/describe_configs_report.rb +7 -1
  32. data/lib/rdkafka/admin/incremental_alter_configs_handle.rb +7 -4
  33. data/lib/rdkafka/admin/incremental_alter_configs_report.rb +7 -1
  34. data/lib/rdkafka/admin/list_offsets_handle.rb +36 -0
  35. data/lib/rdkafka/admin/list_offsets_report.rb +51 -0
  36. data/lib/rdkafka/admin.rb +301 -135
  37. data/lib/rdkafka/bindings.rb +199 -110
  38. data/lib/rdkafka/callbacks.rb +124 -21
  39. data/lib/rdkafka/config.rb +81 -33
  40. data/lib/rdkafka/consumer/headers.rb +3 -2
  41. data/lib/rdkafka/consumer/message.rb +12 -11
  42. data/lib/rdkafka/consumer/partition.rb +8 -4
  43. data/lib/rdkafka/consumer/topic_partition_list.rb +21 -17
  44. data/lib/rdkafka/consumer.rb +397 -45
  45. data/lib/rdkafka/defaults.rb +106 -0
  46. data/lib/rdkafka/error.rb +40 -14
  47. data/lib/rdkafka/helpers/oauth.rb +45 -13
  48. data/lib/rdkafka/helpers/time.rb +5 -0
  49. data/lib/rdkafka/metadata.rb +45 -21
  50. data/lib/rdkafka/native_kafka.rb +89 -4
  51. data/lib/rdkafka/producer/delivery_handle.rb +5 -5
  52. data/lib/rdkafka/producer/delivery_report.rb +10 -6
  53. data/lib/rdkafka/producer/partitions_count_cache.rb +29 -19
  54. data/lib/rdkafka/producer.rb +168 -82
  55. data/lib/rdkafka/version.rb +6 -3
  56. data/lib/rdkafka.rb +3 -0
  57. data/package-lock.json +331 -0
  58. data/package.json +9 -0
  59. data/rdkafka.gemspec +57 -36
  60. data/renovate.json +29 -24
  61. metadata +29 -124
  62. data/.github/CODEOWNERS +0 -3
  63. data/.github/FUNDING.yml +0 -1
  64. data/.github/workflows/ci_linux_x86_64_gnu.yml +0 -271
  65. data/.github/workflows/ci_linux_x86_64_musl.yml +0 -194
  66. data/.github/workflows/ci_macos_arm64.yml +0 -284
  67. data/.github/workflows/push_linux_x86_64_gnu.yml +0 -65
  68. data/.github/workflows/push_linux_x86_64_musl.yml +0 -79
  69. data/.github/workflows/push_macos_arm64.yml +0 -54
  70. data/.github/workflows/push_ruby.yml +0 -37
  71. data/.github/workflows/verify-action-pins.yml +0 -16
  72. data/.gitignore +0 -14
  73. data/.rspec +0 -2
  74. data/.ruby-gemset +0 -1
  75. data/.ruby-version +0 -1
  76. data/.yardopts +0 -2
  77. data/ext/README.md +0 -19
  78. data/ext/build_common.sh +0 -361
  79. data/ext/build_linux_x86_64_gnu.sh +0 -306
  80. data/ext/build_linux_x86_64_musl.sh +0 -763
  81. data/ext/build_macos_arm64.sh +0 -550
  82. data/spec/rdkafka/abstract_handle_spec.rb +0 -117
  83. data/spec/rdkafka/admin/create_acl_handle_spec.rb +0 -56
  84. data/spec/rdkafka/admin/create_acl_report_spec.rb +0 -18
  85. data/spec/rdkafka/admin/create_topic_handle_spec.rb +0 -52
  86. data/spec/rdkafka/admin/create_topic_report_spec.rb +0 -16
  87. data/spec/rdkafka/admin/delete_acl_handle_spec.rb +0 -85
  88. data/spec/rdkafka/admin/delete_acl_report_spec.rb +0 -72
  89. data/spec/rdkafka/admin/delete_topic_handle_spec.rb +0 -52
  90. data/spec/rdkafka/admin/delete_topic_report_spec.rb +0 -16
  91. data/spec/rdkafka/admin/describe_acl_handle_spec.rb +0 -85
  92. data/spec/rdkafka/admin/describe_acl_report_spec.rb +0 -73
  93. data/spec/rdkafka/admin_spec.rb +0 -971
  94. data/spec/rdkafka/bindings_spec.rb +0 -199
  95. data/spec/rdkafka/callbacks_spec.rb +0 -20
  96. data/spec/rdkafka/config_spec.rb +0 -258
  97. data/spec/rdkafka/consumer/headers_spec.rb +0 -73
  98. data/spec/rdkafka/consumer/message_spec.rb +0 -139
  99. data/spec/rdkafka/consumer/partition_spec.rb +0 -57
  100. data/spec/rdkafka/consumer/topic_partition_list_spec.rb +0 -248
  101. data/spec/rdkafka/consumer_spec.rb +0 -1274
  102. data/spec/rdkafka/error_spec.rb +0 -89
  103. data/spec/rdkafka/metadata_spec.rb +0 -79
  104. data/spec/rdkafka/native_kafka_spec.rb +0 -130
  105. data/spec/rdkafka/producer/delivery_handle_spec.rb +0 -45
  106. data/spec/rdkafka/producer/delivery_report_spec.rb +0 -25
  107. data/spec/rdkafka/producer/partitions_count_cache_spec.rb +0 -359
  108. data/spec/rdkafka/producer_spec.rb +0 -1345
  109. data/spec/spec_helper.rb +0 -195
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rdkafka
4
+ # Callback handlers for librdkafka events
5
+ # @private
4
6
  module Callbacks
5
7
  # Extracts attributes of a rd_kafka_topic_result_t
6
8
  #
@@ -8,12 +10,16 @@ module Rdkafka
8
10
  class TopicResult
9
11
  attr_reader :result_error, :error_string, :result_name
10
12
 
13
+ # @param topic_result_pointer [FFI::Pointer] pointer to the topic result struct
11
14
  def initialize(topic_result_pointer)
12
15
  @result_error = Rdkafka::Bindings.rd_kafka_topic_result_error(topic_result_pointer)
13
16
  @error_string = Rdkafka::Bindings.rd_kafka_topic_result_error_string(topic_result_pointer)
14
17
  @result_name = Rdkafka::Bindings.rd_kafka_topic_result_name(topic_result_pointer)
15
18
  end
16
19
 
20
+ # @param count [Integer] number of results
21
+ # @param array_pointer [FFI::Pointer] pointer to the results array
22
+ # @return [Array<TopicResult>] array of topic results
17
23
  def self.create_topic_results_from_array(count, array_pointer)
18
24
  (1..count).map do |index|
19
25
  result_pointer = (array_pointer + (index - 1)).read_pointer
@@ -22,13 +28,18 @@ module Rdkafka
22
28
  end
23
29
  end
24
30
 
31
+ # Extracts attributes of rd_kafka_group_result_t
32
+ #
33
+ # @private
25
34
  class GroupResult
26
35
  attr_reader :result_error, :error_string, :result_name
36
+
37
+ # @param group_result_pointer [FFI::Pointer] pointer to the group result struct
27
38
  def initialize(group_result_pointer)
28
39
  native_error = Rdkafka::Bindings.rd_kafka_group_result_error(group_result_pointer)
29
40
 
30
41
  if native_error.null?
31
- @result_error = 0
42
+ @result_error = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
32
43
  @error_string = FFI::Pointer::NULL
33
44
  else
34
45
  @result_error = native_error[:code]
@@ -37,6 +48,10 @@ module Rdkafka
37
48
 
38
49
  @result_name = Rdkafka::Bindings.rd_kafka_group_result_name(group_result_pointer)
39
50
  end
51
+
52
+ # @param count [Integer] number of results
53
+ # @param array_pointer [FFI::Pointer] pointer to the results array
54
+ # @return [Array<GroupResult>] array of group results
40
55
  def self.create_group_results_from_array(count, array_pointer)
41
56
  (1..count).map do |index|
42
57
  result_pointer = (array_pointer + (index - 1)).read_pointer
@@ -51,12 +66,16 @@ module Rdkafka
51
66
  class CreateAclResult
52
67
  attr_reader :result_error, :error_string
53
68
 
69
+ # @param acl_result_pointer [FFI::Pointer] pointer to the ACL result struct
54
70
  def initialize(acl_result_pointer)
55
71
  rd_kafka_error_pointer = Bindings.rd_kafka_acl_result_error(acl_result_pointer)
56
72
  @result_error = Rdkafka::Bindings.rd_kafka_error_code(rd_kafka_error_pointer)
57
73
  @error_string = Rdkafka::Bindings.rd_kafka_error_string(rd_kafka_error_pointer)
58
74
  end
59
75
 
76
+ # @param count [Integer] number of results
77
+ # @param array_pointer [FFI::Pointer] pointer to the results array
78
+ # @return [Array<CreateAclResult>] array of ACL results
60
79
  def self.create_acl_results_from_array(count, array_pointer)
61
80
  (1..count).map do |index|
62
81
  result_pointer = (array_pointer + (index - 1)).read_pointer
@@ -71,19 +90,23 @@ module Rdkafka
71
90
  class DeleteAclResult
72
91
  attr_reader :result_error, :error_string, :matching_acls, :matching_acls_count
73
92
 
93
+ # @param acl_result_pointer [FFI::Pointer] pointer to the delete ACL result response struct
74
94
  def initialize(acl_result_pointer)
75
- @matching_acls=[]
95
+ @matching_acls = []
76
96
  rd_kafka_error_pointer = Rdkafka::Bindings.rd_kafka_DeleteAcls_result_response_error(acl_result_pointer)
77
97
  @result_error = Rdkafka::Bindings.rd_kafka_error_code(rd_kafka_error_pointer)
78
98
  @error_string = Rdkafka::Bindings.rd_kafka_error_string(rd_kafka_error_pointer)
79
- if @result_error == 0
80
- # Get the number of matching acls
81
- pointer_to_size_t = FFI::MemoryPointer.new(:int32)
82
- @matching_acls = Rdkafka::Bindings.rd_kafka_DeleteAcls_result_response_matching_acls(acl_result_pointer, pointer_to_size_t)
83
- @matching_acls_count = pointer_to_size_t.read_int
99
+ if @result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
100
+ # Get the number of matching acls
101
+ pointer_to_size_t = FFI::MemoryPointer.new(:int32)
102
+ @matching_acls = Rdkafka::Bindings.rd_kafka_DeleteAcls_result_response_matching_acls(acl_result_pointer, pointer_to_size_t)
103
+ @matching_acls_count = pointer_to_size_t.read_int
84
104
  end
85
105
  end
86
106
 
107
+ # @param count [Integer] number of results
108
+ # @param array_pointer [FFI::Pointer] pointer to the results array
109
+ # @return [Array<DeleteAclResult>] array of delete ACL results
87
110
  def self.delete_acl_results_from_array(count, array_pointer)
88
111
  (1..count).map do |index|
89
112
  result_pointer = (array_pointer + (index - 1)).read_pointer
@@ -98,11 +121,12 @@ module Rdkafka
98
121
  class DescribeAclResult
99
122
  attr_reader :result_error, :error_string, :matching_acls, :matching_acls_count
100
123
 
124
+ # @param event_ptr [FFI::Pointer] pointer to the event
101
125
  def initialize(event_ptr)
102
- @matching_acls=[]
126
+ @matching_acls = []
103
127
  @result_error = Rdkafka::Bindings.rd_kafka_event_error(event_ptr)
104
128
  @error_string = Rdkafka::Bindings.rd_kafka_event_error_string(event_ptr)
105
- if @result_error == 0
129
+ if @result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
106
130
  acl_describe_result = Rdkafka::Bindings.rd_kafka_event_DescribeAcls_result(event_ptr)
107
131
  # Get the number of matching acls
108
132
  pointer_to_size_t = FFI::MemoryPointer.new(:int32)
@@ -112,15 +136,19 @@ module Rdkafka
112
136
  end
113
137
  end
114
138
 
139
+ # Extracts attributes of rd_kafka_DescribeConfigs_result_t
140
+ #
141
+ # @private
115
142
  class DescribeConfigsResult
116
143
  attr_reader :result_error, :error_string, :results, :results_count
117
144
 
145
+ # @param event_ptr [FFI::Pointer] pointer to the event
118
146
  def initialize(event_ptr)
119
- @results=[]
147
+ @results = []
120
148
  @result_error = Rdkafka::Bindings.rd_kafka_event_error(event_ptr)
121
149
  @error_string = Rdkafka::Bindings.rd_kafka_event_error_string(event_ptr)
122
150
 
123
- if @result_error == 0
151
+ if @result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
124
152
  configs_describe_result = Rdkafka::Bindings.rd_kafka_event_DescribeConfigs_result(event_ptr)
125
153
  # Get the number of matching acls
126
154
  pointer_to_size_t = FFI::MemoryPointer.new(:int32)
@@ -130,15 +158,19 @@ module Rdkafka
130
158
  end
131
159
  end
132
160
 
161
+ # Extracts attributes of rd_kafka_IncrementalAlterConfigs_result_t
162
+ #
163
+ # @private
133
164
  class IncrementalAlterConfigsResult
134
165
  attr_reader :result_error, :error_string, :results, :results_count
135
166
 
167
+ # @param event_ptr [FFI::Pointer] pointer to the event
136
168
  def initialize(event_ptr)
137
- @results=[]
169
+ @results = []
138
170
  @result_error = Rdkafka::Bindings.rd_kafka_event_error(event_ptr)
139
171
  @error_string = Rdkafka::Bindings.rd_kafka_event_error_string(event_ptr)
140
172
 
141
- if @result_error == 0
173
+ if @result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
142
174
  incremental_alter_result = Rdkafka::Bindings.rd_kafka_event_IncrementalAlterConfigs_result(event_ptr)
143
175
  # Get the number of matching acls
144
176
  pointer_to_size_t = FFI::MemoryPointer.new(:int32)
@@ -148,9 +180,34 @@ module Rdkafka
148
180
  end
149
181
  end
150
182
 
183
+ # Extracts attributes of rd_kafka_ListOffsets_result_t
184
+ #
185
+ # @private
186
+ class ListOffsetsResult
187
+ attr_reader :result_error, :error_string, :result_infos, :result_count
188
+
189
+ # @param event_ptr [FFI::Pointer] pointer to the event
190
+ def initialize(event_ptr)
191
+ @result_infos = FFI::Pointer::NULL
192
+ @result_error = Rdkafka::Bindings.rd_kafka_event_error(event_ptr)
193
+ @error_string = Rdkafka::Bindings.rd_kafka_event_error_string(event_ptr)
194
+
195
+ if @result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
196
+ list_offsets_result = Rdkafka::Bindings.rd_kafka_event_ListOffsets_result(event_ptr)
197
+ pointer_to_size_t = FFI::MemoryPointer.new(:size_t)
198
+ @result_infos = Rdkafka::Bindings.rd_kafka_ListOffsets_result_infos(list_offsets_result, pointer_to_size_t)
199
+ @result_count = pointer_to_size_t.read(:size_t)
200
+ end
201
+ end
202
+ end
203
+
151
204
  # @private
152
205
  class BackgroundEventCallback
153
- def self.call(_, event_ptr, _)
206
+ # Handles background events from librdkafka
207
+ # @param _client_ptr [FFI::Pointer] unused client pointer
208
+ # @param event_ptr [FFI::Pointer] pointer to the event
209
+ # @param _opaque_ptr [FFI::Pointer] unused opaque pointer
210
+ def self.call(_client_ptr, event_ptr, _opaque_ptr)
154
211
  case Rdkafka::Bindings.rd_kafka_event_type(event_ptr)
155
212
  when Rdkafka::Bindings::RD_KAFKA_EVENT_CREATETOPICS_RESULT
156
213
  process_create_topic(event_ptr)
@@ -170,11 +227,15 @@ module Rdkafka
170
227
  process_describe_acl(event_ptr)
171
228
  when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETEGROUPS_RESULT
172
229
  process_delete_groups(event_ptr)
230
+ when Rdkafka::Bindings::RD_KAFKA_EVENT_LISTOFFSETS_RESULT
231
+ process_list_offsets(event_ptr)
173
232
  end
174
233
  end
175
234
 
176
235
  private
177
236
 
237
+ # Processes create topic result event
238
+ # @param event_ptr [FFI::Pointer] pointer to the event
178
239
  def self.process_create_topic(event_ptr)
179
240
  create_topics_result = Rdkafka::Bindings.rd_kafka_event_CreateTopics_result(event_ptr)
180
241
 
@@ -193,6 +254,8 @@ module Rdkafka
193
254
  end
194
255
  end
195
256
 
257
+ # Processes describe configs result event
258
+ # @param event_ptr [FFI::Pointer] pointer to the event
196
259
  def self.process_describe_configs(event_ptr)
197
260
  describe_configs = DescribeConfigsResult.new(event_ptr)
198
261
  describe_configs_handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
@@ -202,7 +265,7 @@ module Rdkafka
202
265
  describe_configs_handle[:response_string] = describe_configs.error_string
203
266
  describe_configs_handle[:pending] = false
204
267
 
205
- if describe_configs.result_error == 0
268
+ if describe_configs.result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
206
269
  describe_configs_handle[:config_entries] = describe_configs.results
207
270
  describe_configs_handle[:entry_count] = describe_configs.results_count
208
271
  end
@@ -211,6 +274,8 @@ module Rdkafka
211
274
  end
212
275
  end
213
276
 
277
+ # Processes incremental alter configs result event
278
+ # @param event_ptr [FFI::Pointer] pointer to the event
214
279
  def self.process_incremental_alter_configs(event_ptr)
215
280
  incremental_alter = IncrementalAlterConfigsResult.new(event_ptr)
216
281
  incremental_alter_handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
@@ -220,7 +285,7 @@ module Rdkafka
220
285
  incremental_alter_handle[:response_string] = incremental_alter.error_string
221
286
  incremental_alter_handle[:pending] = false
222
287
 
223
- if incremental_alter.result_error == 0
288
+ if incremental_alter.result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
224
289
  incremental_alter_handle[:config_entries] = incremental_alter.results
225
290
  incremental_alter_handle[:entry_count] = incremental_alter.results_count
226
291
  end
@@ -229,6 +294,8 @@ module Rdkafka
229
294
  end
230
295
  end
231
296
 
297
+ # Processes delete groups result event
298
+ # @param event_ptr [FFI::Pointer] pointer to the event
232
299
  def self.process_delete_groups(event_ptr)
233
300
  delete_groups_result = Rdkafka::Bindings.rd_kafka_event_DeleteGroups_result(event_ptr)
234
301
 
@@ -247,6 +314,8 @@ module Rdkafka
247
314
  end
248
315
  end
249
316
 
317
+ # Processes delete topic result event
318
+ # @param event_ptr [FFI::Pointer] pointer to the event
250
319
  def self.process_delete_topic(event_ptr)
251
320
  delete_topics_result = Rdkafka::Bindings.rd_kafka_event_DeleteTopics_result(event_ptr)
252
321
 
@@ -265,6 +334,8 @@ module Rdkafka
265
334
  end
266
335
  end
267
336
 
337
+ # Processes create partitions result event
338
+ # @param event_ptr [FFI::Pointer] pointer to the event
268
339
  def self.process_create_partitions(event_ptr)
269
340
  create_partitionss_result = Rdkafka::Bindings.rd_kafka_event_CreatePartitions_result(event_ptr)
270
341
 
@@ -283,6 +354,8 @@ module Rdkafka
283
354
  end
284
355
  end
285
356
 
357
+ # Processes create ACL result event
358
+ # @param event_ptr [FFI::Pointer] pointer to the event
286
359
  def self.process_create_acl(event_ptr)
287
360
  create_acls_result = Rdkafka::Bindings.rd_kafka_event_CreateAcls_result(event_ptr)
288
361
 
@@ -300,6 +373,8 @@ module Rdkafka
300
373
  end
301
374
  end
302
375
 
376
+ # Processes delete ACL result event
377
+ # @param event_ptr [FFI::Pointer] pointer to the event
303
378
  def self.process_delete_acl(event_ptr)
304
379
  delete_acls_result = Rdkafka::Bindings.rd_kafka_event_DeleteAcls_result(event_ptr)
305
380
 
@@ -313,7 +388,7 @@ module Rdkafka
313
388
  delete_acl_handle[:response] = delete_acl_results[0].result_error
314
389
  delete_acl_handle[:response_string] = delete_acl_results[0].error_string
315
390
 
316
- if delete_acl_results[0].result_error == 0
391
+ if delete_acl_results[0].result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
317
392
  delete_acl_handle[:matching_acls] = delete_acl_results[0].matching_acls
318
393
  delete_acl_handle[:matching_acls_count] = delete_acl_results[0].matching_acls_count
319
394
  end
@@ -322,6 +397,8 @@ module Rdkafka
322
397
  end
323
398
  end
324
399
 
400
+ # Processes describe ACL result event
401
+ # @param event_ptr [FFI::Pointer] pointer to the event
325
402
  def self.process_describe_acl(event_ptr)
326
403
  describe_acl = DescribeAclResult.new(event_ptr)
327
404
  describe_acl_handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
@@ -330,7 +407,7 @@ module Rdkafka
330
407
  describe_acl_handle[:response] = describe_acl.result_error
331
408
  describe_acl_handle[:response_string] = describe_acl.error_string
332
409
 
333
- if describe_acl.result_error == 0
410
+ if describe_acl.result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
334
411
  describe_acl_handle[:acls] = describe_acl.matching_acls
335
412
  describe_acl_handle[:acls_count] = describe_acl.matching_acls_count
336
413
  end
@@ -338,11 +415,35 @@ module Rdkafka
338
415
  describe_acl_handle.unlock
339
416
  end
340
417
  end
418
+
419
+ # Processes list offsets result event
420
+ # @param event_ptr [FFI::Pointer] pointer to the event
421
+ def self.process_list_offsets(event_ptr)
422
+ list_offsets = ListOffsetsResult.new(event_ptr)
423
+ list_offsets_handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
424
+
425
+ if list_offsets_handle = Rdkafka::Admin::ListOffsetsHandle.remove(list_offsets_handle_ptr.address)
426
+ list_offsets_handle[:response] = list_offsets.result_error
427
+ list_offsets_handle[:response_string] = list_offsets.error_string
428
+ list_offsets_handle[:pending] = false
429
+
430
+ if list_offsets.result_error == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
431
+ list_offsets_handle[:result_infos] = list_offsets.result_infos
432
+ list_offsets_handle[:result_count] = list_offsets.result_count
433
+ end
434
+
435
+ list_offsets_handle.unlock
436
+ end
437
+ end
341
438
  end
342
439
 
343
440
  # @private
344
441
  class DeliveryCallback
345
- def self.call(_, message_ptr, opaque_ptr)
442
+ # Handles message delivery callbacks
443
+ # @param _client_ptr [FFI::Pointer] unused client pointer
444
+ # @param message_ptr [FFI::Pointer] pointer to the delivered message
445
+ # @param opaque_ptr [FFI::Pointer] pointer to the opaque object for callback context
446
+ def self.call(_client_ptr, message_ptr, opaque_ptr)
346
447
  message = Rdkafka::Bindings::Message.new(message_ptr)
347
448
  delivery_handle_ptr_address = message[:_private].address
348
449
  if delivery_handle = Rdkafka::Producer::DeliveryHandle.remove(delivery_handle_ptr_address)
@@ -373,7 +474,9 @@ module Rdkafka
373
474
  end
374
475
  end
375
476
 
477
+ # @private
376
478
  @@mutex = Mutex.new
479
+ # @private
377
480
  @@current_pid = nil
378
481
 
379
482
  class << self
@@ -392,14 +495,14 @@ module Rdkafka
392
495
 
393
496
  # FFI Function used for Create Topic and Delete Topic callbacks
394
497
  background_event_callback_function = FFI::Function.new(
395
- :void, [:pointer, :pointer, :pointer]
498
+ :void, [:pointer, :pointer, :pointer]
396
499
  ) do |client_ptr, event_ptr, opaque_ptr|
397
500
  BackgroundEventCallback.call(client_ptr, event_ptr, opaque_ptr)
398
501
  end
399
502
 
400
503
  # FFI Function used for Message Delivery callbacks
401
504
  delivery_callback_function = FFI::Function.new(
402
- :void, [:pointer, :pointer, :pointer]
505
+ :void, [:pointer, :pointer, :pointer]
403
506
  ) do |client_ptr, message_ptr, opaque_ptr|
404
507
  DeliveryCallback.call(client_ptr, message_ptr, opaque_ptr)
405
508
  end
@@ -6,7 +6,7 @@ module Rdkafka
6
6
  # configuration options is available on https://github.com/confluentinc/librdkafka/blob/master/CONFIGURATION.md.
7
7
  class Config
8
8
  # @private
9
- @@logger = Logger.new(STDOUT)
9
+ @@logger = Logger.new($stdout)
10
10
  # @private
11
11
  @@statistics_callback = nil
12
12
  # @private
@@ -33,7 +33,7 @@ module Rdkafka
33
33
  # Makes sure that there is a thread for consuming logs
34
34
  # We do not spawn thread immediately and we need to check if it operates to support forking
35
35
  def self.ensure_log_thread
36
- return if @@log_thread && @@log_thread.alive?
36
+ return if @@log_thread&.alive?
37
37
 
38
38
  @@log_mutex.synchronize do
39
39
  # Restart if dead (fork, crash)
@@ -71,11 +71,10 @@ module Rdkafka
71
71
  # You can configure if and how often this happens using `statistics.interval.ms`.
72
72
  # The callback is called with a hash that's documented here: https://github.com/confluentinc/librdkafka/blob/master/STATISTICS.md
73
73
  #
74
- # @param callback [Proc, #call] The callback
75
- #
74
+ # @param callback [Proc, #call, nil] callable object or nil to clear
76
75
  # @return [nil]
77
76
  def self.statistics_callback=(callback)
78
- raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call) || callback == nil
77
+ raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call) || callback.nil?
79
78
  @@statistics_callback = callback
80
79
  end
81
80
 
@@ -90,11 +89,10 @@ module Rdkafka
90
89
  # If this callback is not set, global errors such as brokers becoming unavailable will only be sent to the logger, as defined by librdkafka.
91
90
  # The callback is called with an instance of RdKafka::Error.
92
91
  #
93
- # @param callback [Proc, #call] The callback
94
- #
92
+ # @param callback [Proc, #call, nil] callable object to handle errors or nil to clear
95
93
  # @return [nil]
96
94
  def self.error_callback=(callback)
97
- raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call)
95
+ raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call) || callback.nil?
98
96
  @@error_callback = callback
99
97
  end
100
98
 
@@ -108,11 +106,10 @@ module Rdkafka
108
106
  # Sets the SASL/OAUTHBEARER token refresh callback.
109
107
  # This callback will be triggered when it is time to refresh the client's OAUTHBEARER token
110
108
  #
111
- # @param callback [Proc, #call] The callback
112
- #
109
+ # @param callback [Proc, #call, nil] callable object to handle token refresh or nil to clear
113
110
  # @return [nil]
114
111
  def self.oauthbearer_token_refresh_callback=(callback)
115
- raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call) || callback == nil
112
+ raise TypeError.new("Callback has to be callable") unless callback.respond_to?(:call) || callback.nil?
116
113
  @@oauthbearer_token_refresh_callback = callback
117
114
  end
118
115
 
@@ -129,15 +126,12 @@ module Rdkafka
129
126
  end
130
127
 
131
128
  # Default config that can be overwritten.
132
- DEFAULT_CONFIG = {
133
- # Request api version so advanced features work
134
- :"api.version.request" => true
135
- }.freeze
129
+ DEFAULT_CONFIG = {}.freeze
136
130
 
137
131
  # Required config that cannot be overwritten.
138
132
  REQUIRED_CONFIG = {
139
133
  # Enable log queues so we get callbacks in our own Ruby threads
140
- :"log.queue" => true
134
+ "log.queue": true
141
135
  }.freeze
142
136
 
143
137
  # Returns a new config with the provided options which are merged with {DEFAULT_CONFIG}.
@@ -174,10 +168,8 @@ module Rdkafka
174
168
 
175
169
  # Get notifications on partition assignment/revocation for the subscribed topics
176
170
  #
177
- # @param listener [Object, #on_partitions_assigned, #on_partitions_revoked] listener instance
178
- def consumer_rebalance_listener=(listener)
179
- @consumer_rebalance_listener = listener
180
- end
171
+ # @return [Object, #on_partitions_assigned, #on_partitions_revoked] listener instance
172
+ attr_writer :consumer_rebalance_listener
181
173
 
182
174
  # Should we use a single queue for the underlying consumer and events.
183
175
  #
@@ -190,10 +182,8 @@ module Rdkafka
190
182
  # It is recommended to use the defaults and only set it to `false` in advance multi-threaded
191
183
  # and complex cases where granular events handling control is needed.
192
184
  #
193
- # @param poll_set [Boolean]
194
- def consumer_poll_set=(poll_set)
195
- @consumer_poll_set = poll_set
196
- end
185
+ # @return [Boolean]
186
+ attr_writer :consumer_poll_set
197
187
 
198
188
  # Creates a consumer with this configuration.
199
189
  #
@@ -234,11 +224,13 @@ module Rdkafka
234
224
  # @param native_kafka_auto_start [Boolean] should the native kafka operations be started
235
225
  # automatically. Defaults to true. Set to false only when doing complex initialization.
236
226
  # @param native_kafka_poll_timeout_ms [Integer] ms poll time of the native Kafka
227
+ # @param run_polling_thread [Boolean] should the background polling thread be started.
228
+ # Defaults to true. Set to false when using the FD API for fiber scheduler integration.
237
229
  # @return [Producer] The created producer
238
230
  #
239
231
  # @raise [ConfigError] When the configuration contains invalid options
240
232
  # @raise [ClientCreationError] When the native client cannot be created
241
- def producer(native_kafka_auto_start: true, native_kafka_poll_timeout_ms: 100)
233
+ def producer(native_kafka_auto_start: true, native_kafka_poll_timeout_ms: Defaults::NATIVE_KAFKA_POLL_TIMEOUT_MS, run_polling_thread: true)
242
234
  # Create opaque
243
235
  opaque = Opaque.new
244
236
  # Create Kafka config
@@ -253,7 +245,7 @@ module Rdkafka
253
245
  Rdkafka::Producer.new(
254
246
  Rdkafka::NativeKafka.new(
255
247
  kafka,
256
- run_polling_thread: true,
248
+ run_polling_thread: run_polling_thread,
257
249
  opaque: opaque,
258
250
  auto_start: native_kafka_auto_start,
259
251
  timeout_ms: native_kafka_poll_timeout_ms
@@ -269,11 +261,13 @@ module Rdkafka
269
261
  # @param native_kafka_auto_start [Boolean] should the native kafka operations be started
270
262
  # automatically. Defaults to true. Set to false only when doing complex initialization.
271
263
  # @param native_kafka_poll_timeout_ms [Integer] ms poll time of the native Kafka
264
+ # @param run_polling_thread [Boolean] should the background polling thread be started.
265
+ # Defaults to true. Set to false when using the FD API for fiber scheduler integration.
272
266
  # @return [Admin] The created admin instance
273
267
  #
274
268
  # @raise [ConfigError] When the configuration contains invalid options
275
269
  # @raise [ClientCreationError] When the native client cannot be created
276
- def admin(native_kafka_auto_start: true, native_kafka_poll_timeout_ms: 100)
270
+ def admin(native_kafka_auto_start: true, native_kafka_poll_timeout_ms: Defaults::NATIVE_KAFKA_POLL_TIMEOUT_MS, run_polling_thread: true)
277
271
  opaque = Opaque.new
278
272
  config = native_config(opaque)
279
273
  Rdkafka::Bindings.rd_kafka_conf_set_background_event_cb(config, Rdkafka::Callbacks::BackgroundEventCallbackFunction)
@@ -283,7 +277,7 @@ module Rdkafka
283
277
  Rdkafka::Admin.new(
284
278
  Rdkafka::NativeKafka.new(
285
279
  kafka,
286
- run_polling_thread: true,
280
+ run_polling_thread: run_polling_thread,
287
281
  opaque: opaque,
288
282
  auto_start: native_kafka_auto_start,
289
283
  timeout_ms: native_kafka_poll_timeout_ms
@@ -291,6 +285,46 @@ module Rdkafka
291
285
  )
292
286
  end
293
287
 
288
+ # Returns all configuration properties and their current values for this config.
289
+ #
290
+ # Uses `rd_kafka_conf_dump` to retrieve every property (including defaults and
291
+ # internal properties like `client.software.name`) as a flat Hash.
292
+ #
293
+ # @note The librdkafka C API does not distinguish between producer-only, consumer-only,
294
+ # and global properties at the configuration level. All properties are returned
295
+ # regardless of the intended client type.
296
+ #
297
+ # @note The returned Hash may include sensitive values such as authentication
298
+ # credentials and key passwords. Do not log or serialize the returned data
299
+ # unless you have explicitly redacted secret entries.
300
+ #
301
+ # @return [Hash{Symbol => String}] property names mapped to their current values
302
+ #
303
+ # @raise [ConfigError] When the configuration contains invalid options
304
+ def describe_properties
305
+ config = nil
306
+ dump_ptr = nil
307
+ count = 0
308
+
309
+ config = native_config
310
+ count_ptr = Rdkafka::Bindings::SizePtr.new
311
+ dump_ptr = Rdkafka::Bindings.rd_kafka_conf_dump(config, count_ptr)
312
+
313
+ count = count_ptr[:value]
314
+ result = {}
315
+
316
+ (0...count).step(2) do |i|
317
+ key = dump_ptr.get_pointer(i * FFI::Pointer.size).read_string
318
+ value = dump_ptr.get_pointer((i + 1) * FFI::Pointer.size).read_string
319
+ result[key.to_sym] = value
320
+ end
321
+
322
+ result
323
+ ensure
324
+ Rdkafka::Bindings.rd_kafka_conf_dump_free(dump_ptr, count) if dump_ptr
325
+ Rdkafka::Bindings.rd_kafka_conf_destroy(config) if config
326
+ end
327
+
294
328
  # Error that is returned by the underlying rdkafka error if an invalid configuration option is present.
295
329
  class ConfigError < RuntimeError; end
296
330
 
@@ -304,6 +338,9 @@ module Rdkafka
304
338
 
305
339
  # This method is only intended to be used to create a client,
306
340
  # using it in another way will leak memory.
341
+ #
342
+ # @param opaque [Object, nil] optional opaque pointer for callbacks
343
+ # @return [FFI::Pointer] native rdkafka configuration pointer
307
344
  def native_config(opaque = nil)
308
345
  Rdkafka::Bindings.rd_kafka_conf_new.tap do |config|
309
346
  # Create config
@@ -348,6 +385,11 @@ module Rdkafka
348
385
  end
349
386
  end
350
387
 
388
+ # Creates a native Kafka handle
389
+ # @param config [FFI::Pointer] pointer to the native config
390
+ # @param type [Symbol] type of client (:rd_kafka_producer or :rd_kafka_consumer)
391
+ # @return [FFI::Pointer] pointer to the native Kafka handle
392
+ # @private
351
393
  def native_kafka(config, type)
352
394
  error_buffer = FFI::MemoryPointer.from_string(" " * 256)
353
395
  handle = Rdkafka::Bindings.rd_kafka_new(
@@ -362,10 +404,9 @@ module Rdkafka
362
404
  end
363
405
 
364
406
  # Redirect log to handle's queue
365
- Rdkafka::Bindings.rd_kafka_set_log_queue(
366
- handle,
367
- Rdkafka::Bindings.rd_kafka_queue_get_main(handle)
368
- )
407
+ main_queue = Rdkafka::Bindings.rd_kafka_queue_get_main(handle)
408
+ Rdkafka::Bindings.rd_kafka_set_log_queue(handle, main_queue)
409
+ Rdkafka::Bindings.rd_kafka_queue_destroy(main_queue)
369
410
 
370
411
  # Return handle which should be closed using rd_kafka_destroy after usage.
371
412
  handle
@@ -377,10 +418,15 @@ module Rdkafka
377
418
  attr_accessor :producer
378
419
  attr_accessor :consumer_rebalance_listener
379
420
 
421
+ # Invokes the delivery callback on the producer if one is set
422
+ # @param delivery_report [Rdkafka::Producer::DeliveryReport] the delivery report
423
+ # @param delivery_handle [Rdkafka::Producer::DeliveryHandle] the delivery handle
380
424
  def call_delivery_callback(delivery_report, delivery_handle)
381
- producer.call_delivery_callback(delivery_report, delivery_handle) if producer
425
+ producer&.call_delivery_callback(delivery_report, delivery_handle)
382
426
  end
383
427
 
428
+ # Invokes the on_partitions_assigned callback on the rebalance listener if set
429
+ # @param list [Rdkafka::Consumer::TopicPartitionList] the assigned partitions
384
430
  def call_on_partitions_assigned(list)
385
431
  return unless consumer_rebalance_listener
386
432
  return unless consumer_rebalance_listener.respond_to?(:on_partitions_assigned)
@@ -388,6 +434,8 @@ module Rdkafka
388
434
  consumer_rebalance_listener.on_partitions_assigned(list)
389
435
  end
390
436
 
437
+ # Invokes the on_partitions_revoked callback on the rebalance listener if set
438
+ # @param list [Rdkafka::Consumer::TopicPartitionList] the revoked partitions
391
439
  def call_on_partitions_revoked(list)
392
440
  return unless consumer_rebalance_listener
393
441
  return unless consumer_rebalance_listener.respond_to?(:on_partitions_revoked)
@@ -4,6 +4,7 @@ module Rdkafka
4
4
  class Consumer
5
5
  # Interface to return headers for a consumer message
6
6
  module Headers
7
+ # Empty frozen hash used when there are no headers
7
8
  EMPTY_HEADERS = {}.freeze
8
9
 
9
10
  # Reads a librdkafka native message's headers and returns them as a Ruby Hash
@@ -12,8 +13,8 @@ module Rdkafka
12
13
  #
13
14
  # @private
14
15
  #
15
- # @param [Rdkafka::Bindings::Message] native_message
16
- # @return [Hash<String, String|Array<String>>] headers Hash for the native_message
16
+ # @param native_message [Rdkafka::Bindings::Message] the native message to read headers from
17
+ # @return [Hash{String => String, Array<String>}] headers Hash for the native_message
17
18
  # @raise [Rdkafka::RdkafkaError] when fail to read headers
18
19
  def self.from_native(native_message)
19
20
  headers_ptrptr = FFI::MemoryPointer.new(:pointer)