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,582 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rdkafka
4
+ # @private
5
+ #
6
+ # @note
7
+ # There are two types of responses related to errors:
8
+ # - rd_kafka_error_t - a C object that we need to remap into an error or null when no error
9
+ # - rd_kafka_resp_err_t - response error code (numeric) that we can use directly
10
+ #
11
+ # It is critical to ensure, that we handle them correctly. The result type should be:
12
+ # - rd_kafka_error_t - :pointer
13
+ # - rd_kafka_resp_err_t - :int
14
+ module Bindings
15
+ extend FFI::Library
16
+
17
+ def self.lib_extension
18
+ if RbConfig::CONFIG['host_os'] =~ /darwin/
19
+ 'dylib'
20
+ else
21
+ 'so'
22
+ end
23
+ end
24
+
25
+ ffi_lib File.join(__dir__, "../../ext/librdkafka.#{lib_extension}")
26
+
27
+ RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS = -175
28
+ RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS = -174
29
+ RD_KAFKA_RESP_ERR__STATE = -172
30
+ RD_KAFKA_RESP_ERR__NOENT = -156
31
+ RD_KAFKA_RESP_ERR_NO_ERROR = 0
32
+
33
+ RD_KAFKA_OFFSET_END = -1
34
+ RD_KAFKA_OFFSET_BEGINNING = -2
35
+ RD_KAFKA_OFFSET_STORED = -1000
36
+ RD_KAFKA_OFFSET_INVALID = -1001
37
+
38
+ EMPTY_HASH = {}.freeze
39
+
40
+ class SizePtr < FFI::Struct
41
+ layout :value, :size_t
42
+ end
43
+
44
+ # This function comes from our patch on top of librdkafka. It allows os to load all the
45
+ # librdkafka components without initializing the client
46
+ # @see https://github.com/confluentinc/librdkafka/issues/4590
47
+ attach_function :rd_kafka_global_init, [], :void
48
+
49
+ # Polling
50
+
51
+ attach_function :rd_kafka_flush, [:pointer, :int], :int, blocking: true
52
+ attach_function :rd_kafka_poll, [:pointer, :int], :int, blocking: true
53
+ attach_function :rd_kafka_outq_len, [:pointer], :int, blocking: true
54
+
55
+ # Metadata
56
+
57
+ attach_function :rd_kafka_name, [:pointer], :string
58
+ attach_function :rd_kafka_memberid, [:pointer], :string, blocking: true
59
+ attach_function :rd_kafka_clusterid, [:pointer], :string, blocking: true
60
+ attach_function :rd_kafka_metadata, [:pointer, :int, :pointer, :pointer, :int], :int, blocking: true
61
+ attach_function :rd_kafka_metadata_destroy, [:pointer], :void, blocking: true
62
+
63
+ # Message struct
64
+
65
+ class Message < FFI::Struct
66
+ layout :err, :int,
67
+ :rkt, :pointer,
68
+ :partition, :int32,
69
+ :payload, :pointer,
70
+ :len, :size_t,
71
+ :key, :pointer,
72
+ :key_len, :size_t,
73
+ :offset, :int64,
74
+ :_private, :pointer
75
+ end
76
+
77
+ attach_function :rd_kafka_message_destroy, [:pointer], :void
78
+ attach_function :rd_kafka_message_timestamp, [:pointer, :pointer], :int64
79
+ attach_function :rd_kafka_topic_new, [:pointer, :string, :pointer], :pointer
80
+ attach_function :rd_kafka_topic_destroy, [:pointer], :pointer
81
+ attach_function :rd_kafka_topic_name, [:pointer], :string
82
+
83
+ # TopicPartition ad TopicPartitionList structs
84
+
85
+ class TopicPartition < FFI::Struct
86
+ layout :topic, :string,
87
+ :partition, :int32,
88
+ :offset, :int64,
89
+ :metadata, :pointer,
90
+ :metadata_size, :size_t,
91
+ :opaque, :pointer,
92
+ :err, :int,
93
+ :_private, :pointer
94
+ end
95
+
96
+ class TopicPartitionList < FFI::Struct
97
+ layout :cnt, :int,
98
+ :size, :int,
99
+ :elems, :pointer
100
+ end
101
+
102
+ attach_function :rd_kafka_topic_partition_list_new, [:int32], :pointer
103
+ attach_function :rd_kafka_topic_partition_list_add, [:pointer, :string, :int32], :pointer
104
+ attach_function :rd_kafka_topic_partition_list_set_offset, [:pointer, :string, :int32, :int64], :void
105
+ attach_function :rd_kafka_topic_partition_list_destroy, [:pointer], :void
106
+ attach_function :rd_kafka_topic_partition_list_copy, [:pointer], :pointer
107
+
108
+ # Configs management
109
+ #
110
+ # Structs for management of configurations
111
+ # Each configuration is attached to a resource and one resource can have many configuration
112
+ # details. Each resource will also have separate errors results if obtaining configuration
113
+ # was not possible for any reason
114
+ class ConfigResource < FFI::Struct
115
+ layout :type, :int,
116
+ :name, :string
117
+ end
118
+
119
+ attach_function :rd_kafka_DescribeConfigs, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
120
+ attach_function :rd_kafka_ConfigResource_new, [:int32, :pointer], :pointer
121
+ attach_function :rd_kafka_ConfigResource_destroy_array, [:pointer, :int32], :void
122
+ attach_function :rd_kafka_event_DescribeConfigs_result, [:pointer], :pointer
123
+ attach_function :rd_kafka_DescribeConfigs_result_resources, [:pointer, :pointer], :pointer
124
+ attach_function :rd_kafka_ConfigResource_configs, [:pointer, :pointer], :pointer
125
+ attach_function :rd_kafka_ConfigEntry_name, [:pointer], :string
126
+ attach_function :rd_kafka_ConfigEntry_value, [:pointer], :string
127
+ attach_function :rd_kafka_ConfigEntry_is_read_only, [:pointer], :int
128
+ attach_function :rd_kafka_ConfigEntry_is_default, [:pointer], :int
129
+ attach_function :rd_kafka_ConfigEntry_is_sensitive, [:pointer], :int
130
+ attach_function :rd_kafka_ConfigEntry_is_synonym, [:pointer], :int
131
+ attach_function :rd_kafka_ConfigEntry_synonyms, [:pointer, :pointer], :pointer
132
+ attach_function :rd_kafka_ConfigResource_error, [:pointer], :int
133
+ attach_function :rd_kafka_ConfigResource_error_string, [:pointer], :string
134
+ attach_function :rd_kafka_IncrementalAlterConfigs, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
135
+ attach_function :rd_kafka_IncrementalAlterConfigs_result_resources, [:pointer, :pointer], :pointer
136
+ attach_function :rd_kafka_ConfigResource_add_incremental_config, [:pointer, :string, :int32, :string], :pointer
137
+ attach_function :rd_kafka_event_IncrementalAlterConfigs_result, [:pointer], :pointer
138
+
139
+ RD_KAFKA_ADMIN_OP_DESCRIBECONFIGS = 5
140
+ RD_KAFKA_EVENT_DESCRIBECONFIGS_RESULT = 104
141
+
142
+ RD_KAFKA_ADMIN_OP_INCREMENTALALTERCONFIGS = 16
143
+ RD_KAFKA_EVENT_INCREMENTALALTERCONFIGS_RESULT = 131072
144
+
145
+ RD_KAFKA_ALTER_CONFIG_OP_TYPE_SET = 0
146
+ RD_KAFKA_ALTER_CONFIG_OP_TYPE_DELETE = 1
147
+ RD_KAFKA_ALTER_CONFIG_OP_TYPE_APPEND = 2
148
+ RD_KAFKA_ALTER_CONFIG_OP_TYPE_SUBTRACT = 3
149
+
150
+ # Errors
151
+ class NativeErrorDesc < FFI::Struct
152
+ layout :code, :int,
153
+ :name, :pointer,
154
+ :desc, :pointer
155
+ end
156
+
157
+ attach_function :rd_kafka_err2name, [:int], :string
158
+ attach_function :rd_kafka_err2str, [:int], :string
159
+ attach_function :rd_kafka_error_is_fatal, [:pointer], :int
160
+ attach_function :rd_kafka_error_is_retriable, [:pointer], :int
161
+ attach_function :rd_kafka_error_txn_requires_abort, [:pointer], :int
162
+ attach_function :rd_kafka_error_destroy, [:pointer], :void
163
+ attach_function :rd_kafka_error_code, [:pointer], :int
164
+ attach_function :rd_kafka_get_err_descs, [:pointer, :pointer], :void
165
+
166
+ # Configuration
167
+
168
+ enum :kafka_config_response, [
169
+ :config_unknown, -2,
170
+ :config_invalid, -1,
171
+ :config_ok, 0
172
+ ]
173
+
174
+ attach_function :rd_kafka_conf_new, [], :pointer
175
+ attach_function :rd_kafka_conf_set, [:pointer, :string, :string, :pointer, :int], :kafka_config_response
176
+ callback :log_cb, [:pointer, :int, :string, :string], :void
177
+ attach_function :rd_kafka_conf_set_log_cb, [:pointer, :log_cb], :void
178
+ attach_function :rd_kafka_conf_set_opaque, [:pointer, :pointer], :void
179
+ callback :stats_cb, [:pointer, :string, :int, :pointer], :int
180
+ attach_function :rd_kafka_conf_set_stats_cb, [:pointer, :stats_cb], :void
181
+ callback :error_cb, [:pointer, :int, :string, :pointer], :void
182
+ attach_function :rd_kafka_conf_set_error_cb, [:pointer, :error_cb], :void
183
+ attach_function :rd_kafka_rebalance_protocol, [:pointer], :string
184
+ callback :oauthbearer_token_refresh_cb, [:pointer, :string, :pointer], :void
185
+ attach_function :rd_kafka_conf_set_oauthbearer_token_refresh_cb, [:pointer, :oauthbearer_token_refresh_cb], :void
186
+ attach_function :rd_kafka_oauthbearer_set_token, [:pointer, :string, :int64, :pointer, :pointer, :int, :pointer, :int], :int
187
+ attach_function :rd_kafka_oauthbearer_set_token_failure, [:pointer, :string], :int
188
+ # Log queue
189
+ attach_function :rd_kafka_set_log_queue, [:pointer, :pointer], :void
190
+ attach_function :rd_kafka_queue_get_main, [:pointer], :pointer
191
+ # Per topic configs
192
+ attach_function :rd_kafka_topic_conf_new, [], :pointer
193
+ attach_function :rd_kafka_topic_conf_set, [:pointer, :string, :string, :pointer, :int], :kafka_config_response
194
+
195
+ LogCallback = FFI::Function.new(
196
+ :void, [:pointer, :int, :string, :string]
197
+ ) do |_client_ptr, level, _level_string, line|
198
+ severity = case level
199
+ when 0, 1, 2
200
+ Logger::FATAL
201
+ when 3
202
+ Logger::ERROR
203
+ when 4
204
+ Logger::WARN
205
+ when 5, 6
206
+ Logger::INFO
207
+ when 7
208
+ Logger::DEBUG
209
+ else
210
+ Logger::UNKNOWN
211
+ end
212
+
213
+ Rdkafka::Config.ensure_log_thread
214
+ Rdkafka::Config.log_queue << [severity, "rdkafka: #{line}"]
215
+ end
216
+
217
+ StatsCallback = FFI::Function.new(
218
+ :int, [:pointer, :string, :int, :pointer]
219
+ ) do |_client_ptr, json, _json_len, _opaque|
220
+ if Rdkafka::Config.statistics_callback
221
+ stats = JSON.parse(json)
222
+
223
+ # If user requested statistics callbacks, we can use the statistics data to get the
224
+ # partitions count for each topic when this data is published. That way we do not have
225
+ # to query this information when user is using `partition_key`. This takes around 0.02ms
226
+ # every statistics interval period (most likely every 5 seconds) and saves us from making
227
+ # any queries to the cluster for the partition count.
228
+ #
229
+ # One edge case is if user would set the `statistics.interval.ms` much higher than the
230
+ # default current partition count refresh (30 seconds). This is taken care of as the lack
231
+ # of reporting to the partitions cache will cause cache expire and blocking refresh.
232
+ #
233
+ # If user sets `topic.metadata.refresh.interval.ms` too high this is on the user.
234
+ #
235
+ # Since this cache is shared, having few consumers and/or producers in one process will
236
+ # automatically improve the querying times even with low refresh times.
237
+ (stats['topics'] || EMPTY_HASH).each do |topic_name, details|
238
+ partitions_count = details['partitions'].keys.reject { |k| k == '-1' }.size
239
+
240
+ next unless partitions_count.positive?
241
+
242
+ Rdkafka::Producer.partitions_count_cache.set(topic_name, partitions_count)
243
+ end
244
+
245
+ Rdkafka::Config.statistics_callback.call(stats)
246
+ end
247
+
248
+ # Return 0 so librdkafka frees the json string
249
+ 0
250
+ end
251
+
252
+ ErrorCallback = FFI::Function.new(
253
+ :void, [:pointer, :int, :string, :pointer]
254
+ ) do |_client_prr, err_code, reason, _opaque|
255
+ if Rdkafka::Config.error_callback
256
+ error = Rdkafka::RdkafkaError.build(err_code, broker_message: reason)
257
+ error.set_backtrace(caller)
258
+ Rdkafka::Config.error_callback.call(error)
259
+ end
260
+ end
261
+
262
+ # The OAuth callback is currently global and contextless.
263
+ # This means that the callback will be called for all instances, and the callback must be able to determine to which instance it is associated.
264
+ # The instance name will be provided in the callback, allowing the callback to reference the correct instance.
265
+ #
266
+ # An example of how to use the instance name in the callback is given below.
267
+ # The `refresh_token` is configured as the `oauthbearer_token_refresh_callback`.
268
+ # `instances` is a map of client names to client instances, maintained by the user.
269
+ #
270
+ # ```
271
+ # def refresh_token(config, client_name)
272
+ # client = instances[client_name]
273
+ # client.oauthbearer_set_token(
274
+ # token: 'new-token-value',
275
+ # lifetime_ms: token-lifetime-ms,
276
+ # principal_name: 'principal-name'
277
+ # )
278
+ # end
279
+ # ```
280
+ OAuthbearerTokenRefreshCallback = FFI::Function.new(
281
+ :void, [:pointer, :string, :pointer]
282
+ ) do |client_ptr, config, _opaque|
283
+ if Rdkafka::Config.oauthbearer_token_refresh_callback && !client_ptr.null?
284
+ Rdkafka::Config.oauthbearer_token_refresh_callback.call(config, Rdkafka::Bindings.rd_kafka_name(client_ptr))
285
+ end
286
+ end
287
+
288
+ # Handle
289
+
290
+ enum :kafka_type, [
291
+ :rd_kafka_producer,
292
+ :rd_kafka_consumer
293
+ ]
294
+
295
+ attach_function :rd_kafka_new, [:kafka_type, :pointer, :pointer, :int], :pointer
296
+
297
+ attach_function :rd_kafka_destroy, [:pointer], :void
298
+
299
+ # Consumer
300
+
301
+ attach_function :rd_kafka_subscribe, [:pointer, :pointer], :int, blocking: true
302
+ attach_function :rd_kafka_unsubscribe, [:pointer], :int, blocking: true
303
+ attach_function :rd_kafka_subscription, [:pointer, :pointer], :int, blocking: true
304
+ attach_function :rd_kafka_assign, [:pointer, :pointer], :int, blocking: true
305
+ attach_function :rd_kafka_incremental_assign, [:pointer, :pointer], :int, blocking: true
306
+ attach_function :rd_kafka_incremental_unassign, [:pointer, :pointer], :int, blocking: true
307
+ attach_function :rd_kafka_assignment, [:pointer, :pointer], :int, blocking: true
308
+ attach_function :rd_kafka_assignment_lost, [:pointer], :int, blocking: true
309
+ attach_function :rd_kafka_committed, [:pointer, :pointer, :int], :int, blocking: true
310
+ attach_function :rd_kafka_commit, [:pointer, :pointer, :bool], :int, blocking: true
311
+ attach_function :rd_kafka_poll_set_consumer, [:pointer], :void, blocking: true
312
+ attach_function :rd_kafka_consumer_poll, [:pointer, :int], :pointer, blocking: true
313
+ attach_function :rd_kafka_consumer_close, [:pointer], :void, blocking: true
314
+ attach_function :rd_kafka_offsets_store, [:pointer, :pointer], :int, blocking: true
315
+ attach_function :rd_kafka_pause_partitions, [:pointer, :pointer], :int, blocking: true
316
+ attach_function :rd_kafka_resume_partitions, [:pointer, :pointer], :int, blocking: true
317
+ attach_function :rd_kafka_seek, [:pointer, :int32, :int64, :int], :int, blocking: true
318
+ attach_function :rd_kafka_offsets_for_times, [:pointer, :pointer, :int], :int, blocking: true
319
+ attach_function :rd_kafka_position, [:pointer, :pointer], :int, blocking: true
320
+ # those two are used for eos support
321
+ attach_function :rd_kafka_consumer_group_metadata, [:pointer], :pointer, blocking: true
322
+ attach_function :rd_kafka_consumer_group_metadata_destroy, [:pointer], :void, blocking: true
323
+
324
+ # Headers
325
+ attach_function :rd_kafka_header_get_all, [:pointer, :size_t, :pointer, :pointer, SizePtr], :int
326
+ attach_function :rd_kafka_message_headers, [:pointer, :pointer], :int
327
+
328
+ # Rebalance
329
+
330
+ callback :rebalance_cb_function, [:pointer, :int, :pointer, :pointer], :void
331
+ attach_function :rd_kafka_conf_set_rebalance_cb, [:pointer, :rebalance_cb_function], :void, blocking: true
332
+
333
+ RebalanceCallback = FFI::Function.new(
334
+ :void, [:pointer, :int, :pointer, :pointer]
335
+ ) do |client_ptr, code, partitions_ptr, opaque_ptr|
336
+ case code
337
+ when RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS
338
+ if Rdkafka::Bindings.rd_kafka_rebalance_protocol(client_ptr) == "COOPERATIVE"
339
+ Rdkafka::Bindings.rd_kafka_incremental_assign(client_ptr, partitions_ptr)
340
+ else
341
+ Rdkafka::Bindings.rd_kafka_assign(client_ptr, partitions_ptr)
342
+ end
343
+ else # RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS or errors
344
+ if Rdkafka::Bindings.rd_kafka_rebalance_protocol(client_ptr) == "COOPERATIVE"
345
+ Rdkafka::Bindings.rd_kafka_incremental_unassign(client_ptr, partitions_ptr)
346
+ else
347
+ Rdkafka::Bindings.rd_kafka_assign(client_ptr, FFI::Pointer::NULL)
348
+ end
349
+ end
350
+
351
+ opaque = Rdkafka::Config.opaques[opaque_ptr.to_i]
352
+ return unless opaque
353
+
354
+ tpl = Rdkafka::Consumer::TopicPartitionList.from_native_tpl(partitions_ptr).freeze
355
+ begin
356
+ case code
357
+ when RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS
358
+ opaque.call_on_partitions_assigned(tpl)
359
+ when RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS
360
+ opaque.call_on_partitions_revoked(tpl)
361
+ end
362
+ rescue Exception => err
363
+ Rdkafka::Config.logger.error("Unhandled exception: #{err.class} - #{err.message}")
364
+ end
365
+ end
366
+
367
+ # Stats
368
+
369
+ attach_function :rd_kafka_query_watermark_offsets, [:pointer, :string, :int, :pointer, :pointer, :int], :int
370
+
371
+ # Producer
372
+
373
+ RD_KAFKA_VTYPE_END = 0
374
+ RD_KAFKA_VTYPE_TOPIC = 1
375
+ RD_KAFKA_VTYPE_RKT = 2
376
+ RD_KAFKA_VTYPE_PARTITION = 3
377
+ RD_KAFKA_VTYPE_VALUE = 4
378
+ RD_KAFKA_VTYPE_KEY = 5
379
+ RD_KAFKA_VTYPE_OPAQUE = 6
380
+ RD_KAFKA_VTYPE_MSGFLAGS = 7
381
+ RD_KAFKA_VTYPE_TIMESTAMP = 8
382
+ RD_KAFKA_VTYPE_HEADER = 9
383
+ RD_KAFKA_VTYPE_HEADERS = 10
384
+ RD_KAFKA_PURGE_F_QUEUE = 1
385
+ RD_KAFKA_PURGE_F_INFLIGHT = 2
386
+
387
+ RD_KAFKA_MSG_F_COPY = 0x2
388
+
389
+ attach_function :rd_kafka_producev, [:pointer, :varargs], :int, blocking: true
390
+ attach_function :rd_kafka_purge, [:pointer, :int], :int, blocking: true
391
+ callback :delivery_cb, [:pointer, :pointer, :pointer], :void
392
+ attach_function :rd_kafka_conf_set_dr_msg_cb, [:pointer, :delivery_cb], :void
393
+ attach_function :rd_kafka_init_transactions, [:pointer, :int], :pointer, blocking: true
394
+ attach_function :rd_kafka_send_offsets_to_transaction, [:pointer, :pointer, :pointer, :int], :pointer, blocking: true
395
+ attach_function :rd_kafka_begin_transaction, [:pointer], :pointer, blocking: true
396
+ attach_function :rd_kafka_abort_transaction, [:pointer, :int], :pointer, blocking: true
397
+ attach_function :rd_kafka_commit_transaction, [:pointer, :int], :pointer, blocking: true
398
+
399
+ # Partitioner
400
+ PARTITIONERS = %w(random consistent consistent_random murmur2 murmur2_random fnv1a fnv1a_random).each_with_object({}) do |name, hsh|
401
+ method_name = "rd_kafka_msg_partitioner_#{name}".to_sym
402
+ attach_function method_name, [:pointer, :pointer, :size_t, :int32, :pointer, :pointer], :int32
403
+ hsh[name] = method_name
404
+ end
405
+
406
+ def self.partitioner(str, partition_count, partitioner_name = "consistent_random")
407
+ # Return RD_KAFKA_PARTITION_UA(unassigned partition) when partition count is nil/zero.
408
+ return -1 unless partition_count&.nonzero?
409
+
410
+ str_ptr = str.empty? ? FFI::MemoryPointer::NULL : FFI::MemoryPointer.from_string(str)
411
+ method_name = PARTITIONERS.fetch(partitioner_name) do
412
+ raise Rdkafka::Config::ConfigError.new("Unknown partitioner: #{partitioner_name}")
413
+ end
414
+ public_send(method_name, nil, str_ptr, str.size > 0 ? str.size : 1, partition_count, nil, nil)
415
+ end
416
+
417
+ # Create Topics
418
+
419
+ RD_KAFKA_ADMIN_OP_CREATETOPICS = 1 # rd_kafka_admin_op_t
420
+ RD_KAFKA_EVENT_CREATETOPICS_RESULT = 100 # rd_kafka_event_type_t
421
+
422
+ attach_function :rd_kafka_CreateTopics, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
423
+ attach_function :rd_kafka_NewTopic_new, [:pointer, :size_t, :size_t, :pointer, :size_t], :pointer, blocking: true
424
+ attach_function :rd_kafka_NewTopic_set_config, [:pointer, :string, :string], :int32, blocking: true
425
+ attach_function :rd_kafka_NewTopic_destroy, [:pointer], :void, blocking: true
426
+ attach_function :rd_kafka_event_CreateTopics_result, [:pointer], :pointer, blocking: true
427
+ attach_function :rd_kafka_CreateTopics_result_topics, [:pointer, :pointer], :pointer, blocking: true
428
+
429
+ # Delete Topics
430
+
431
+ RD_KAFKA_ADMIN_OP_DELETETOPICS = 2 # rd_kafka_admin_op_t
432
+ RD_KAFKA_EVENT_DELETETOPICS_RESULT = 101 # rd_kafka_event_type_t
433
+
434
+ attach_function :rd_kafka_DeleteTopics, [:pointer, :pointer, :size_t, :pointer, :pointer], :int32, blocking: true
435
+ attach_function :rd_kafka_DeleteTopic_new, [:pointer], :pointer, blocking: true
436
+ attach_function :rd_kafka_DeleteTopic_destroy, [:pointer], :void, blocking: true
437
+ attach_function :rd_kafka_event_DeleteTopics_result, [:pointer], :pointer, blocking: true
438
+ attach_function :rd_kafka_DeleteTopics_result_topics, [:pointer, :pointer], :pointer, blocking: true
439
+
440
+ # Create partitions
441
+ RD_KAFKA_ADMIN_OP_CREATEPARTITIONS = 3
442
+ RD_KAFKA_ADMIN_OP_CREATEPARTITIONS_RESULT = 102
443
+
444
+ attach_function :rd_kafka_CreatePartitions, [:pointer, :pointer, :size_t, :pointer, :pointer], :void
445
+ attach_function :rd_kafka_NewPartitions_new, %i[pointer size_t pointer size_t], :pointer
446
+ attach_function :rd_kafka_NewPartitions_destroy, [:pointer], :void
447
+ attach_function :rd_kafka_event_CreatePartitions_result, [:pointer], :pointer
448
+ attach_function :rd_kafka_CreatePartitions_result_topics, [:pointer, :pointer], :pointer
449
+
450
+ # Delete Group
451
+
452
+ RD_KAFKA_ADMIN_OP_DELETEGROUPS = 7 # rd_kafka_admin_op_t
453
+ RD_KAFKA_EVENT_DELETEGROUPS_RESULT = 106 # rd_kafka_event_type_t
454
+
455
+ attach_function :rd_kafka_DeleteGroups, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
456
+ attach_function :rd_kafka_DeleteGroup_new, [:pointer], :pointer, blocking: true
457
+ attach_function :rd_kafka_DeleteGroup_destroy, [:pointer], :void, blocking: true
458
+ attach_function :rd_kafka_event_DeleteGroups_result, [:pointer], :pointer, blocking: true # rd_kafka_event_t* => rd_kafka_DeleteGroups_result_t*
459
+ attach_function :rd_kafka_DeleteGroups_result_groups, [:pointer, :pointer], :pointer, blocking: true # rd_kafka_DeleteGroups_result_t*, size_t* => rd_kafka_group_result_t**
460
+
461
+ # Background Queue and Callback
462
+
463
+ attach_function :rd_kafka_queue_get_background, [:pointer], :pointer
464
+ attach_function :rd_kafka_conf_set_background_event_cb, [:pointer, :pointer], :void
465
+ attach_function :rd_kafka_queue_destroy, [:pointer], :void
466
+
467
+ # Admin Options
468
+
469
+ attach_function :rd_kafka_AdminOptions_new, [:pointer, :int32], :pointer
470
+ attach_function :rd_kafka_AdminOptions_set_opaque, [:pointer, :pointer], :void
471
+ attach_function :rd_kafka_AdminOptions_destroy, [:pointer], :void
472
+
473
+ # Extracting data from event types
474
+
475
+ attach_function :rd_kafka_event_type, [:pointer], :int32
476
+ attach_function :rd_kafka_event_opaque, [:pointer], :pointer
477
+
478
+ # Extracting data from topic results
479
+
480
+ attach_function :rd_kafka_topic_result_error, [:pointer], :int32
481
+ attach_function :rd_kafka_topic_result_error_string, [:pointer], :pointer
482
+ attach_function :rd_kafka_topic_result_name, [:pointer], :pointer
483
+
484
+ # Create Acls
485
+
486
+ RD_KAFKA_ADMIN_OP_CREATEACLS = 9
487
+ RD_KAFKA_EVENT_CREATEACLS_RESULT = 1024
488
+
489
+ attach_function :rd_kafka_CreateAcls, [:pointer, :pointer, :size_t, :pointer, :pointer], :void
490
+ attach_function :rd_kafka_event_CreateAcls_result, [:pointer], :pointer
491
+ attach_function :rd_kafka_CreateAcls_result_acls, [:pointer, :pointer], :pointer
492
+
493
+ # Delete Acls
494
+
495
+ RD_KAFKA_ADMIN_OP_DELETEACLS = 11
496
+ RD_KAFKA_EVENT_DELETEACLS_RESULT = 4096
497
+
498
+ attach_function :rd_kafka_DeleteAcls, [:pointer, :pointer, :size_t, :pointer, :pointer], :void
499
+ attach_function :rd_kafka_event_DeleteAcls_result, [:pointer], :pointer
500
+ attach_function :rd_kafka_DeleteAcls_result_responses, [:pointer, :pointer], :pointer
501
+ attach_function :rd_kafka_DeleteAcls_result_response_error, [:pointer], :pointer
502
+ attach_function :rd_kafka_DeleteAcls_result_response_matching_acls, [:pointer, :pointer], :pointer
503
+
504
+ # Describe Acls
505
+
506
+ RD_KAFKA_ADMIN_OP_DESCRIBEACLS = 10
507
+ RD_KAFKA_EVENT_DESCRIBEACLS_RESULT = 2048
508
+
509
+ attach_function :rd_kafka_DescribeAcls, [:pointer, :pointer, :pointer, :pointer], :void
510
+ attach_function :rd_kafka_event_DescribeAcls_result, [:pointer], :pointer
511
+ attach_function :rd_kafka_DescribeAcls_result_acls, [:pointer, :pointer], :pointer
512
+
513
+ # Acl Bindings
514
+
515
+ attach_function :rd_kafka_AclBinding_restype, [:pointer], :int32
516
+ attach_function :rd_kafka_AclBinding_name, [:pointer], :pointer
517
+ attach_function :rd_kafka_AclBinding_resource_pattern_type, [:pointer], :int32
518
+ attach_function :rd_kafka_AclBinding_principal, [:pointer], :pointer
519
+ attach_function :rd_kafka_AclBinding_host, [:pointer], :pointer
520
+ attach_function :rd_kafka_AclBinding_operation, [:pointer], :int32
521
+ attach_function :rd_kafka_AclBinding_permission_type, [:pointer], :int32
522
+ attach_function :rd_kafka_AclBinding_new, [:int32, :pointer, :int32, :pointer, :pointer, :int32, :int32, :pointer, :size_t ], :pointer
523
+ attach_function :rd_kafka_AclBindingFilter_new, [:int32, :pointer, :int32, :pointer, :pointer, :int32, :int32, :pointer, :size_t ], :pointer
524
+ attach_function :rd_kafka_AclBinding_destroy, [:pointer], :void
525
+
526
+ # rd_kafka_ResourceType_t - https://github.com/confluentinc/librdkafka/blob/292d2a66b9921b783f08147807992e603c7af059/src/rdkafka.h#L7307
527
+
528
+ RD_KAFKA_RESOURCE_ANY = 1
529
+ RD_KAFKA_RESOURCE_TOPIC = 2
530
+ RD_KAFKA_RESOURCE_GROUP = 3
531
+ RD_KAFKA_RESOURCE_BROKER = 4
532
+
533
+ # rd_kafka_ResourcePatternType_t - https://github.com/confluentinc/librdkafka/blob/292d2a66b9921b783f08147807992e603c7af059/src/rdkafka.h#L7320
534
+
535
+ RD_KAFKA_RESOURCE_PATTERN_ANY = 1
536
+ RD_KAFKA_RESOURCE_PATTERN_MATCH = 2
537
+ RD_KAFKA_RESOURCE_PATTERN_LITERAL = 3
538
+ RD_KAFKA_RESOURCE_PATTERN_PREFIXED = 4
539
+
540
+ # rd_kafka_AclOperation_t - https://github.com/confluentinc/librdkafka/blob/292d2a66b9921b783f08147807992e603c7af059/src/rdkafka.h#L8403
541
+
542
+ RD_KAFKA_ACL_OPERATION_ANY = 1
543
+ RD_KAFKA_ACL_OPERATION_ALL = 2
544
+ RD_KAFKA_ACL_OPERATION_READ = 3
545
+ RD_KAFKA_ACL_OPERATION_WRITE = 4
546
+ RD_KAFKA_ACL_OPERATION_CREATE = 5
547
+ RD_KAFKA_ACL_OPERATION_DELETE = 6
548
+ RD_KAFKA_ACL_OPERATION_ALTER = 7
549
+ RD_KAFKA_ACL_OPERATION_DESCRIBE = 8
550
+ RD_KAFKA_ACL_OPERATION_CLUSTER_ACTION = 9
551
+ RD_KAFKA_ACL_OPERATION_DESCRIBE_CONFIGS = 10
552
+ RD_KAFKA_ACL_OPERATION_ALTER_CONFIGS = 11
553
+ RD_KAFKA_ACL_OPERATION_IDEMPOTENT_WRITE = 12
554
+
555
+ # rd_kafka_AclPermissionType_t - https://github.com/confluentinc/librdkafka/blob/292d2a66b9921b783f08147807992e603c7af059/src/rdkafka.h#L8435
556
+
557
+ RD_KAFKA_ACL_PERMISSION_TYPE_ANY = 1
558
+ RD_KAFKA_ACL_PERMISSION_TYPE_DENY = 2
559
+ RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW = 3
560
+
561
+ # Extracting error details from Acl results
562
+ attach_function :rd_kafka_acl_result_error, [:pointer], :pointer
563
+ attach_function :rd_kafka_error_code, [:pointer], :int32
564
+ attach_function :rd_kafka_error_string, [:pointer], :pointer
565
+ attach_function :rd_kafka_event_error, [:pointer], :int32
566
+ attach_function :rd_kafka_event_error_string, [:pointer], :pointer
567
+ attach_function :rd_kafka_AclBinding_error, [:pointer], :pointer
568
+
569
+
570
+ # Extracting data from group results
571
+ class NativeError < FFI::Struct # rd_kafka_error_t
572
+ layout :code, :int32,
573
+ :errstr, :pointer,
574
+ :fatal, :u_int8_t,
575
+ :retriable, :u_int8_t,
576
+ :txn_requires_abort, :u_int8_t
577
+ end
578
+
579
+ attach_function :rd_kafka_group_result_error, [:pointer], NativeError.by_ref # rd_kafka_group_result_t* => rd_kafka_error_t*
580
+ attach_function :rd_kafka_group_result_name, [:pointer], :pointer
581
+ end
582
+ end