karafka-rdkafka 0.31.0-aarch64-linux-gnu → 0.32.0-aarch64-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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +83 -35
- data/README.md +2 -1
- data/ext/librdkafka.so +0 -0
- data/karafka-rdkafka.gemspec +4 -3
- data/lib/rdkafka/admin/acl_binding_result.rb +4 -16
- data/lib/rdkafka/admin/alter_consumer_group_offsets_report.rb +2 -2
- data/lib/rdkafka/admin/create_acl_report.rb +1 -3
- data/lib/rdkafka/admin/create_topic_report.rb +2 -6
- data/lib/rdkafka/admin/delete_consumer_group_offsets_report.rb +2 -2
- data/lib/rdkafka/admin/delete_groups_report.rb +2 -6
- data/lib/rdkafka/admin/delete_topic_report.rb +2 -6
- data/lib/rdkafka/admin/describe_cluster_report.rb +3 -0
- data/lib/rdkafka/admin/list_consumer_group_offsets_handle.rb +31 -0
- data/lib/rdkafka/admin/list_consumer_group_offsets_report.rb +52 -0
- data/lib/rdkafka/admin.rb +180 -10
- data/lib/rdkafka/bindings/admin/configs.rb +1 -1
- data/lib/rdkafka/bindings/admin.rb +14 -0
- data/lib/rdkafka/bindings/callbacks.rb +13 -6
- data/lib/rdkafka/bindings/configuration.rb +3 -3
- data/lib/rdkafka/bindings/consumer.rb +44 -2
- data/lib/rdkafka/bindings/errors.rb +1 -1
- data/lib/rdkafka/bindings/handle.rb +1 -1
- data/lib/rdkafka/bindings.rb +3 -0
- data/lib/rdkafka/callbacks/list_consumer_group_offsets_handler.rb +62 -0
- data/lib/rdkafka/callbacks.rb +5 -6
- data/lib/rdkafka/config.rb +70 -0
- data/lib/rdkafka/consumer/allocator.rb +0 -2
- data/lib/rdkafka/consumer/topic_partition_list.rb +7 -7
- data/lib/rdkafka/consumer.rb +76 -36
- data/lib/rdkafka/defaults.rb +6 -4
- data/lib/rdkafka/native_kafka.rb +45 -31
- data/lib/rdkafka/producer/partitions_count_cache.rb +109 -26
- data/lib/rdkafka/producer.rb +141 -71
- data/lib/rdkafka/share_consumer.rb +64 -34
- data/lib/rdkafka/version.rb +1 -1
- data/lib/rdkafka.rb +4 -1
- data/package-lock.json +3 -3
- data/renovate.json +7 -0
- metadata +5 -30
data/lib/rdkafka/admin.rb
CHANGED
|
@@ -95,7 +95,7 @@ module Rdkafka
|
|
|
95
95
|
|
|
96
96
|
# Polls for events in a non-blocking loop, yielding the count after each iteration.
|
|
97
97
|
#
|
|
98
|
-
# This method processes events (stats, errors, etc.) in a single GVL
|
|
98
|
+
# This method processes events (stats, errors, etc.) in a single GVL session,
|
|
99
99
|
# which is more efficient than repeated individual polls. It uses non-blocking polls
|
|
100
100
|
# internally (no GVL release between polls).
|
|
101
101
|
#
|
|
@@ -116,8 +116,9 @@ module Rdkafka
|
|
|
116
116
|
# admin.events_poll_nb_each do |_count|
|
|
117
117
|
# :stop if monotonic_now >= deadline
|
|
118
118
|
# end
|
|
119
|
-
# @note This method
|
|
120
|
-
# Other
|
|
119
|
+
# @note This method runs inside `with_inner` and does not hold a lock while it loops.
|
|
120
|
+
# Other operations on the same client run concurrently. `close` waits until this
|
|
121
|
+
# method returns.
|
|
121
122
|
# @note This method is thread-safe as it uses @native_kafka.with_inner synchronization
|
|
122
123
|
def events_poll_nb_each
|
|
123
124
|
closed_admin_check(__method__)
|
|
@@ -149,14 +150,35 @@ module Rdkafka
|
|
|
149
150
|
# @param partition_count [Integer] number of partitions for the topic
|
|
150
151
|
# @param replication_factor [Integer] replication factor for the topic
|
|
151
152
|
# @param topic_config [Hash] optional topic configuration settings
|
|
153
|
+
# @param validate_only [Boolean] when true, the broker only validates the request and does not
|
|
154
|
+
# create the topic
|
|
155
|
+
# @param operation_timeout_ms [Integer, nil] how long the broker waits for the topic creation
|
|
156
|
+
# to complete before it responds (librdkafka default: 60 seconds)
|
|
157
|
+
# @param request_timeout_ms [Integer, nil] overall request timeout (librdkafka default:
|
|
158
|
+
# `socket.timeout.ms`)
|
|
159
|
+
# @param braceless_topic_config [Hash] topic configuration given as a hash without braces,
|
|
160
|
+
# e.g. `create_topic("t", 1, 1, "retention.ms" => "1000")`, merged into `topic_config`
|
|
161
|
+
# @option braceless_topic_config [String] any topic configuration name, e.g. `"retention.ms"`
|
|
152
162
|
# @return [CreateTopicHandle] Create topic handle that can be used to wait for the result of
|
|
153
163
|
# creating the topic
|
|
154
164
|
# @raise [ConfigError] When the partition count or replication factor are out of valid range
|
|
155
165
|
# @raise [RdkafkaError] When the topic name is invalid or the topic already exists
|
|
156
166
|
# @raise [RdkafkaError] When the topic configuration is invalid
|
|
157
|
-
|
|
167
|
+
# @raise [RdkafkaError] When librdkafka rejects a timeout value
|
|
168
|
+
def create_topic(
|
|
169
|
+
topic_name,
|
|
170
|
+
partition_count,
|
|
171
|
+
replication_factor,
|
|
172
|
+
topic_config = {},
|
|
173
|
+
validate_only: false,
|
|
174
|
+
operation_timeout_ms: nil,
|
|
175
|
+
request_timeout_ms: nil,
|
|
176
|
+
**braceless_topic_config
|
|
177
|
+
)
|
|
158
178
|
closed_admin_check(__method__)
|
|
159
179
|
|
|
180
|
+
topic_config = (topic_config || {}).merge(braceless_topic_config) unless braceless_topic_config.empty?
|
|
181
|
+
|
|
160
182
|
# Create a rd_kafka_NewTopic_t representing the new topic
|
|
161
183
|
error_buffer = FFI::MemoryPointer.from_string(" " * 256)
|
|
162
184
|
new_topic_ptr = Rdkafka::Bindings.rd_kafka_NewTopic_new(
|
|
@@ -203,6 +225,13 @@ module Rdkafka
|
|
|
203
225
|
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, create_topic_handle.to_ptr)
|
|
204
226
|
|
|
205
227
|
begin
|
|
228
|
+
set_admin_options(
|
|
229
|
+
admin_options_ptr,
|
|
230
|
+
validate_only: validate_only,
|
|
231
|
+
operation_timeout_ms: operation_timeout_ms,
|
|
232
|
+
request_timeout_ms: request_timeout_ms
|
|
233
|
+
)
|
|
234
|
+
|
|
206
235
|
@native_kafka.with_inner do |inner|
|
|
207
236
|
Rdkafka::Bindings.rd_kafka_CreateTopics(
|
|
208
237
|
inner,
|
|
@@ -224,6 +253,51 @@ module Rdkafka
|
|
|
224
253
|
create_topic_handle
|
|
225
254
|
end
|
|
226
255
|
|
|
256
|
+
# Reads the committed offsets of a consumer group from outside the group.
|
|
257
|
+
#
|
|
258
|
+
# Unlike {Consumer#committed}, this works for any group and does not join it, so it does not
|
|
259
|
+
# trigger a rebalance of the group it inspects.
|
|
260
|
+
#
|
|
261
|
+
# While the group coordinator is loading, not known yet or has moved (e.g. on a fresh cluster
|
|
262
|
+
# or after a broker restart), the handle raises `coordinator_load_in_progress`,
|
|
263
|
+
# `not_coordinator` or `coordinator_not_available`. Retry the call on these codes;
|
|
264
|
+
# `RdkafkaError#retryable?` is false for them.
|
|
265
|
+
#
|
|
266
|
+
# @param group_id [String] the group whose committed offsets we want to read
|
|
267
|
+
# @param topic_partition_list [TopicPartitionList, nil] partitions to read the offsets for, or
|
|
268
|
+
# nil to read every partition the group has committed offsets for. List the partitions
|
|
269
|
+
# explicitly: a topic without partitions is not expanded to its partitions
|
|
270
|
+
# @param require_stable_offsets [Boolean] pending offsets of an open transaction are never
|
|
271
|
+
# returned. Without this option the last committed offset is returned for such a partition.
|
|
272
|
+
# With it, the call fails with `unstable_offset_commit` until that transaction is committed
|
|
273
|
+
# or aborted; retry on this code
|
|
274
|
+
# @return [ListConsumerGroupOffsetsHandle] handle that can be used to wait for the result
|
|
275
|
+
# @raise [RdkafkaError] when listing the offsets fails
|
|
276
|
+
def list_consumer_group_offsets(group_id, topic_partition_list = nil, require_stable_offsets: false)
|
|
277
|
+
closed_admin_check(__method__)
|
|
278
|
+
|
|
279
|
+
native_tpl = topic_partition_list&.to_native_tpl
|
|
280
|
+
|
|
281
|
+
begin
|
|
282
|
+
list_ptr = Rdkafka::Bindings.rd_kafka_ListConsumerGroupOffsets_new(
|
|
283
|
+
FFI::MemoryPointer.from_string(group_id),
|
|
284
|
+
native_tpl || FFI::Pointer::NULL
|
|
285
|
+
)
|
|
286
|
+
ensure
|
|
287
|
+
# librdkafka copies the list into the request object, so ours is no longer needed
|
|
288
|
+
Rdkafka::Bindings.rd_kafka_topic_partition_list_destroy(native_tpl) if native_tpl
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
enqueue_group_offsets_op(
|
|
292
|
+
request_ptr: list_ptr,
|
|
293
|
+
handle: ListConsumerGroupOffsetsHandle.new,
|
|
294
|
+
admin_op: Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_LISTCONSUMERGROUPOFFSETS,
|
|
295
|
+
destroy: Rdkafka::Bindings.method(:rd_kafka_ListConsumerGroupOffsets_destroy),
|
|
296
|
+
enqueue: Rdkafka::Bindings.method(:rd_kafka_ListConsumerGroupOffsets),
|
|
297
|
+
configure: ->(admin_options_ptr) { set_require_stable_offsets(admin_options_ptr) if require_stable_offsets }
|
|
298
|
+
)
|
|
299
|
+
end
|
|
300
|
+
|
|
227
301
|
# Sets the committed offsets of a consumer group from outside the group.
|
|
228
302
|
#
|
|
229
303
|
# Kafka requires the group to be empty (no active members) for this to be accepted; when it
|
|
@@ -748,10 +822,15 @@ module Rdkafka
|
|
|
748
822
|
# Deletes the named topic
|
|
749
823
|
#
|
|
750
824
|
# @param topic_name [String] name of the topic to delete
|
|
825
|
+
# @param operation_timeout_ms [Integer, nil] how long the broker waits for the topic deletion
|
|
826
|
+
# to complete before it responds (librdkafka default: 60 seconds)
|
|
827
|
+
# @param request_timeout_ms [Integer, nil] overall request timeout (librdkafka default:
|
|
828
|
+
# `socket.timeout.ms`)
|
|
751
829
|
# @return [DeleteTopicHandle] Delete topic handle that can be used to wait for the result of
|
|
752
830
|
# deleting the topic
|
|
753
831
|
# @raise [RdkafkaError] When the topic name is invalid or the topic does not exist
|
|
754
|
-
|
|
832
|
+
# @raise [RdkafkaError] When librdkafka rejects a timeout value
|
|
833
|
+
def delete_topic(topic_name, operation_timeout_ms: nil, request_timeout_ms: nil)
|
|
755
834
|
closed_admin_check(__method__)
|
|
756
835
|
|
|
757
836
|
# Create a rd_kafka_DeleteTopic_t representing the topic to be deleted
|
|
@@ -782,6 +861,12 @@ module Rdkafka
|
|
|
782
861
|
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, delete_topic_handle.to_ptr)
|
|
783
862
|
|
|
784
863
|
begin
|
|
864
|
+
set_admin_options(
|
|
865
|
+
admin_options_ptr,
|
|
866
|
+
operation_timeout_ms: operation_timeout_ms,
|
|
867
|
+
request_timeout_ms: request_timeout_ms
|
|
868
|
+
)
|
|
869
|
+
|
|
785
870
|
@native_kafka.with_inner do |inner|
|
|
786
871
|
Rdkafka::Bindings.rd_kafka_DeleteTopics(
|
|
787
872
|
inner,
|
|
@@ -807,12 +892,25 @@ module Rdkafka
|
|
|
807
892
|
#
|
|
808
893
|
# @param topic_name [String] name of the topic
|
|
809
894
|
# @param partition_count [Integer] how many partitions we want to end up with for given topic
|
|
895
|
+
# @param validate_only [Boolean] when true, the broker only validates the request and does not
|
|
896
|
+
# add the partitions
|
|
897
|
+
# @param operation_timeout_ms [Integer, nil] how long the broker waits for the partitions to be
|
|
898
|
+
# created before it responds (librdkafka default: 60 seconds)
|
|
899
|
+
# @param request_timeout_ms [Integer, nil] overall request timeout (librdkafka default:
|
|
900
|
+
# `socket.timeout.ms`)
|
|
810
901
|
# @return [CreatePartitionsHandle] Create partitions handle that can be used to wait for the
|
|
811
902
|
# result
|
|
812
903
|
# @raise [ConfigError] When the partition count or replication factor are out of valid range
|
|
813
904
|
# @raise [RdkafkaError] When the topic name is invalid or the topic already exists
|
|
814
905
|
# @raise [RdkafkaError] When the topic configuration is invalid
|
|
815
|
-
|
|
906
|
+
# @raise [RdkafkaError] When librdkafka rejects a timeout value
|
|
907
|
+
def create_partitions(
|
|
908
|
+
topic_name,
|
|
909
|
+
partition_count,
|
|
910
|
+
validate_only: false,
|
|
911
|
+
operation_timeout_ms: nil,
|
|
912
|
+
request_timeout_ms: nil
|
|
913
|
+
)
|
|
816
914
|
closed_admin_check(__method__)
|
|
817
915
|
|
|
818
916
|
@native_kafka.with_inner do |inner|
|
|
@@ -847,6 +945,13 @@ module Rdkafka
|
|
|
847
945
|
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, create_partitions_handle.to_ptr)
|
|
848
946
|
|
|
849
947
|
begin
|
|
948
|
+
set_admin_options(
|
|
949
|
+
admin_options_ptr,
|
|
950
|
+
validate_only: validate_only,
|
|
951
|
+
operation_timeout_ms: operation_timeout_ms,
|
|
952
|
+
request_timeout_ms: request_timeout_ms
|
|
953
|
+
)
|
|
954
|
+
|
|
850
955
|
Rdkafka::Bindings.rd_kafka_CreatePartitions(
|
|
851
956
|
inner,
|
|
852
957
|
topics_array_ptr,
|
|
@@ -1431,8 +1536,8 @@ module Rdkafka
|
|
|
1431
1536
|
|
|
1432
1537
|
private
|
|
1433
1538
|
|
|
1434
|
-
# Shared enqueue path for the
|
|
1435
|
-
#
|
|
1539
|
+
# Shared enqueue path for the consumer group offset admin operations. They differ only in
|
|
1540
|
+
# their C entry points, so keeping one body avoids near identical copies of the
|
|
1436
1541
|
# queue/handle/options dance and its cleanup.
|
|
1437
1542
|
#
|
|
1438
1543
|
# @param request_ptr [FFI::Pointer] the op specific request object
|
|
@@ -1440,8 +1545,13 @@ module Rdkafka
|
|
|
1440
1545
|
# @param admin_op [Integer] the RD_KAFKA_ADMIN_OP_* constant for this operation
|
|
1441
1546
|
# @param destroy [Method] bound function used to free the request object
|
|
1442
1547
|
# @param enqueue [Method] bound function used to enqueue the request
|
|
1548
|
+
# @param configure [Proc, nil] called with the `rd_kafka_AdminOptions_t` pointer before the
|
|
1549
|
+
# request is enqueued, to set an option only this operation supports. For example
|
|
1550
|
+
# `#list_consumer_group_offsets` always passes a lambda that takes `admin_options_ptr` and
|
|
1551
|
+
# calls `set_require_stable_offsets(admin_options_ptr)` only when
|
|
1552
|
+
# `require_stable_offsets: true`. nil (alter and delete) sets no extra option
|
|
1443
1553
|
# @return [AbstractHandle] the registered handle
|
|
1444
|
-
def enqueue_group_offsets_op(request_ptr:, handle:, admin_op:, destroy:, enqueue:)
|
|
1554
|
+
def enqueue_group_offsets_op(request_ptr:, handle:, admin_op:, destroy:, enqueue:, configure: nil)
|
|
1445
1555
|
array_ptr = FFI::MemoryPointer.new(:pointer)
|
|
1446
1556
|
array_ptr.write_array_of_pointer([request_ptr])
|
|
1447
1557
|
|
|
@@ -1464,6 +1574,8 @@ module Rdkafka
|
|
|
1464
1574
|
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, handle.to_ptr)
|
|
1465
1575
|
|
|
1466
1576
|
begin
|
|
1577
|
+
configure&.call(admin_options_ptr)
|
|
1578
|
+
|
|
1467
1579
|
@native_kafka.with_inner do |inner|
|
|
1468
1580
|
enqueue.call(inner, array_ptr, 1, admin_options_ptr, queue_ptr)
|
|
1469
1581
|
end
|
|
@@ -1483,8 +1595,25 @@ module Rdkafka
|
|
|
1483
1595
|
# @param admin_options_ptr [FFI::Pointer] the admin options to update
|
|
1484
1596
|
# @raise [RdkafkaError] when librdkafka rejects the option
|
|
1485
1597
|
def set_include_authorized_operations(admin_options_ptr)
|
|
1486
|
-
|
|
1598
|
+
raise_admin_option_error(
|
|
1599
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_include_authorized_operations(admin_options_ptr, 1)
|
|
1600
|
+
)
|
|
1601
|
+
end
|
|
1602
|
+
|
|
1603
|
+
# Makes the broker fail a partition with `unstable_offset_commit` while a transaction with
|
|
1604
|
+
# pending offsets for it is open, instead of returning its last committed offset
|
|
1605
|
+
# @param admin_options_ptr [FFI::Pointer] the admin options to update
|
|
1606
|
+
# @raise [RdkafkaError] when librdkafka rejects the option
|
|
1607
|
+
def set_require_stable_offsets(admin_options_ptr)
|
|
1608
|
+
raise_admin_option_error(
|
|
1609
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_require_stable_offsets(admin_options_ptr, 1)
|
|
1610
|
+
)
|
|
1611
|
+
end
|
|
1487
1612
|
|
|
1613
|
+
# Raises and releases the error librdkafka returned from setting an admin option
|
|
1614
|
+
# @param error_ptr [FFI::Pointer] the returned `rd_kafka_error_t*`, NULL on success
|
|
1615
|
+
# @raise [RdkafkaError] when the error pointer is not NULL
|
|
1616
|
+
def raise_admin_option_error(error_ptr)
|
|
1488
1617
|
return if error_ptr.null?
|
|
1489
1618
|
|
|
1490
1619
|
begin
|
|
@@ -1499,6 +1628,47 @@ module Rdkafka
|
|
|
1499
1628
|
end
|
|
1500
1629
|
end
|
|
1501
1630
|
|
|
1631
|
+
# Applies the optional request settings to admin options
|
|
1632
|
+
# @param admin_options_ptr [FFI::Pointer] the admin options to update
|
|
1633
|
+
# @param validate_only [Boolean] whether the broker should only validate the request
|
|
1634
|
+
# @param operation_timeout_ms [Integer, nil] broker operation timeout, librdkafka default if nil
|
|
1635
|
+
# @param request_timeout_ms [Integer, nil] request timeout, librdkafka default if nil
|
|
1636
|
+
# @raise [RdkafkaError] when librdkafka rejects a value
|
|
1637
|
+
def set_admin_options(admin_options_ptr, validate_only: false, operation_timeout_ms: nil, request_timeout_ms: nil)
|
|
1638
|
+
error_buffer = FFI::MemoryPointer.new(:char, 256)
|
|
1639
|
+
|
|
1640
|
+
if validate_only
|
|
1641
|
+
check_admin_option(
|
|
1642
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_validate_only(admin_options_ptr, 1, error_buffer, 256),
|
|
1643
|
+
error_buffer
|
|
1644
|
+
)
|
|
1645
|
+
end
|
|
1646
|
+
|
|
1647
|
+
if operation_timeout_ms
|
|
1648
|
+
check_admin_option(
|
|
1649
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_operation_timeout(admin_options_ptr, operation_timeout_ms, error_buffer, 256),
|
|
1650
|
+
error_buffer
|
|
1651
|
+
)
|
|
1652
|
+
end
|
|
1653
|
+
|
|
1654
|
+
if request_timeout_ms
|
|
1655
|
+
check_admin_option(
|
|
1656
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_request_timeout(admin_options_ptr, request_timeout_ms, error_buffer, 256),
|
|
1657
|
+
error_buffer
|
|
1658
|
+
)
|
|
1659
|
+
end
|
|
1660
|
+
end
|
|
1661
|
+
|
|
1662
|
+
# Raises when an admin options setter failed
|
|
1663
|
+
# @param response [Integer] result code of an admin options setter
|
|
1664
|
+
# @param error_buffer [FFI::MemoryPointer] buffer the setter wrote its error into
|
|
1665
|
+
# @raise [RdkafkaError] when the setter failed
|
|
1666
|
+
def check_admin_option(response, error_buffer)
|
|
1667
|
+
return if response == Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
1668
|
+
|
|
1669
|
+
raise RdkafkaError.new(response, broker_message: error_buffer.read_string)
|
|
1670
|
+
end
|
|
1671
|
+
|
|
1502
1672
|
# Checks if the admin is closed and raises an error if so
|
|
1503
1673
|
# @param method [Symbol] name of the calling method for error context
|
|
1504
1674
|
# @raise [ClosedAdminError] when the admin is closed
|
|
@@ -5,7 +5,7 @@ module Rdkafka
|
|
|
5
5
|
attach_function :rd_kafka_DescribeConfigs, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
|
|
6
6
|
attach_function :rd_kafka_ConfigResource_new, [:int32, :pointer], :pointer
|
|
7
7
|
attach_function :rd_kafka_ConfigResource_destroy, [:pointer], :void
|
|
8
|
-
attach_function :rd_kafka_ConfigResource_destroy_array, [:pointer, :
|
|
8
|
+
attach_function :rd_kafka_ConfigResource_destroy_array, [:pointer, :size_t], :void
|
|
9
9
|
attach_function :rd_kafka_event_DescribeConfigs_result, [:pointer], :pointer
|
|
10
10
|
attach_function :rd_kafka_DescribeConfigs_result_resources, [:pointer, :pointer], :pointer
|
|
11
11
|
attach_function :rd_kafka_ConfigResource_configs, [:pointer, :pointer], :pointer
|
|
@@ -53,6 +53,17 @@ module Rdkafka
|
|
|
53
53
|
attach_function :rd_kafka_event_AlterConsumerGroupOffsets_result, [:pointer], :pointer, blocking: true # rd_kafka_event_t* => rd_kafka_AlterConsumerGroupOffsets_result_t*
|
|
54
54
|
attach_function :rd_kafka_AlterConsumerGroupOffsets_result_groups, [:pointer, :pointer], :pointer, blocking: true # rd_kafka_AlterConsumerGroupOffsets_result_t*, size_t* => rd_kafka_group_result_t**
|
|
55
55
|
|
|
56
|
+
# List Consumer Group Offsets
|
|
57
|
+
RD_KAFKA_ADMIN_OP_LISTCONSUMERGROUPOFFSETS = 14 # rd_kafka_admin_op_t
|
|
58
|
+
RD_KAFKA_EVENT_LISTCONSUMERGROUPOFFSETS_RESULT = 0x8000 # rd_kafka_event_type_t
|
|
59
|
+
|
|
60
|
+
attach_function :rd_kafka_ListConsumerGroupOffsets, [:pointer, :pointer, :size_t, :pointer, :pointer], :void, blocking: true
|
|
61
|
+
attach_function :rd_kafka_ListConsumerGroupOffsets_new, [:pointer, :pointer], :pointer, blocking: true # const char*, const rd_kafka_topic_partition_list_t* => rd_kafka_ListConsumerGroupOffsets_t*
|
|
62
|
+
attach_function :rd_kafka_ListConsumerGroupOffsets_destroy, [:pointer], :void, blocking: true
|
|
63
|
+
attach_function :rd_kafka_event_ListConsumerGroupOffsets_result, [:pointer], :pointer, blocking: true # rd_kafka_event_t* => rd_kafka_ListConsumerGroupOffsets_result_t*
|
|
64
|
+
attach_function :rd_kafka_ListConsumerGroupOffsets_result_groups, [:pointer, :pointer], :pointer, blocking: true # rd_kafka_ListConsumerGroupOffsets_result_t*, size_t* => rd_kafka_group_result_t**
|
|
65
|
+
attach_function :rd_kafka_AdminOptions_set_require_stable_offsets, [:pointer, :int], :pointer, blocking: true # => rd_kafka_error_t*
|
|
66
|
+
|
|
56
67
|
# Delete Consumer Group Offsets
|
|
57
68
|
RD_KAFKA_ADMIN_OP_DELETECONSUMERGROUPOFFSETS = 8 # rd_kafka_admin_op_t
|
|
58
69
|
RD_KAFKA_EVENT_DELETECONSUMERGROUPOFFSETS_RESULT = 107 # rd_kafka_event_type_t
|
|
@@ -171,6 +182,9 @@ module Rdkafka
|
|
|
171
182
|
attach_function :rd_kafka_AdminOptions_new, [:pointer, :int32], :pointer
|
|
172
183
|
attach_function :rd_kafka_AdminOptions_set_opaque, [:pointer, :pointer], :void
|
|
173
184
|
attach_function :rd_kafka_AdminOptions_destroy, [:pointer], :void
|
|
185
|
+
attach_function :rd_kafka_AdminOptions_set_request_timeout, [:pointer, :int, :pointer, :size_t], :int32
|
|
186
|
+
attach_function :rd_kafka_AdminOptions_set_operation_timeout, [:pointer, :int, :pointer, :size_t], :int32
|
|
187
|
+
attach_function :rd_kafka_AdminOptions_set_validate_only, [:pointer, :int, :pointer, :size_t], :int32
|
|
174
188
|
|
|
175
189
|
# Extracting data from event types
|
|
176
190
|
|
|
@@ -27,7 +27,10 @@ module Rdkafka
|
|
|
27
27
|
StatsCallback = FFI::Function.new(
|
|
28
28
|
:int, [:pointer, :string, :int, :pointer]
|
|
29
29
|
) do |_client_ptr, json, _json_len, _opaque|
|
|
30
|
-
|
|
30
|
+
# Read the callback once: another thread may clear it while this one runs
|
|
31
|
+
statistics_callback = Rdkafka::Config.statistics_callback
|
|
32
|
+
|
|
33
|
+
if statistics_callback
|
|
31
34
|
stats = JSON.parse(json)
|
|
32
35
|
|
|
33
36
|
# If user requested statistics callbacks, we can use the statistics data to get the
|
|
@@ -52,7 +55,7 @@ module Rdkafka
|
|
|
52
55
|
Rdkafka::Producer.partitions_count_cache.set(topic_name, partitions_count)
|
|
53
56
|
end
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
statistics_callback.call(stats)
|
|
56
59
|
end
|
|
57
60
|
|
|
58
61
|
# Return 0 so librdkafka frees the json string
|
|
@@ -88,7 +91,9 @@ module Rdkafka
|
|
|
88
91
|
ErrorCallback = FFI::Function.new(
|
|
89
92
|
:void, [:pointer, :int, :string, :pointer]
|
|
90
93
|
) do |client_ptr, err_code, reason, _opaque|
|
|
91
|
-
|
|
94
|
+
error_callback = Rdkafka::Config.error_callback
|
|
95
|
+
|
|
96
|
+
if error_callback
|
|
92
97
|
instance_name = client_ptr.null? ? nil : Rdkafka::Bindings.rd_kafka_name(client_ptr)
|
|
93
98
|
|
|
94
99
|
# Handle fatal errors according to librdkafka documentation:
|
|
@@ -106,7 +111,7 @@ module Rdkafka
|
|
|
106
111
|
end
|
|
107
112
|
|
|
108
113
|
error.set_backtrace(caller)
|
|
109
|
-
|
|
114
|
+
error_callback.call(error)
|
|
110
115
|
end
|
|
111
116
|
end
|
|
112
117
|
|
|
@@ -132,7 +137,9 @@ module Rdkafka
|
|
|
132
137
|
OAuthbearerTokenRefreshCallback = FFI::Function.new(
|
|
133
138
|
:void, [:pointer, :string, :pointer]
|
|
134
139
|
) do |client_ptr, config, opaque_ptr|
|
|
135
|
-
|
|
140
|
+
refresh_callback = Rdkafka::Config.oauthbearer_token_refresh_callback
|
|
141
|
+
|
|
142
|
+
if refresh_callback && !client_ptr.null?
|
|
136
143
|
client_name = Rdkafka::Bindings.rd_kafka_name(client_ptr)
|
|
137
144
|
|
|
138
145
|
# Share consumers have no way to query their librdkafka client name (the share handle
|
|
@@ -141,7 +148,7 @@ module Rdkafka
|
|
|
141
148
|
# ShareConsumer#name.
|
|
142
149
|
Rdkafka::Config.opaques[opaque_ptr.to_i]&.capture_client_name(client_name)
|
|
143
150
|
|
|
144
|
-
|
|
151
|
+
refresh_callback.call(config, client_name)
|
|
145
152
|
end
|
|
146
153
|
end
|
|
147
154
|
end
|
|
@@ -11,7 +11,7 @@ module Rdkafka
|
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
attach_function :rd_kafka_conf_new, [], :pointer
|
|
14
|
-
attach_function :rd_kafka_conf_set, [:pointer, :string, :string, :pointer, :
|
|
14
|
+
attach_function :rd_kafka_conf_set, [:pointer, :string, :string, :pointer, :size_t], :kafka_config_response
|
|
15
15
|
attach_function :rd_kafka_conf_get, [:pointer, :string, :pointer, :pointer], :kafka_config_response
|
|
16
16
|
attach_function :rd_kafka_conf, [:pointer], :pointer
|
|
17
17
|
attach_function :rd_kafka_conf_dump, [:pointer, :pointer], :pointer
|
|
@@ -27,7 +27,7 @@ module Rdkafka
|
|
|
27
27
|
attach_function :rd_kafka_rebalance_protocol, [:pointer], :string
|
|
28
28
|
callback :oauthbearer_token_refresh_cb, [:pointer, :string, :pointer], :void
|
|
29
29
|
attach_function :rd_kafka_conf_set_oauthbearer_token_refresh_cb, [:pointer, :oauthbearer_token_refresh_cb], :void
|
|
30
|
-
attach_function :rd_kafka_oauthbearer_set_token, [:pointer, :string, :int64, :pointer, :pointer, :
|
|
30
|
+
attach_function :rd_kafka_oauthbearer_set_token, [:pointer, :string, :int64, :pointer, :pointer, :size_t, :pointer, :size_t], :int
|
|
31
31
|
attach_function :rd_kafka_oauthbearer_set_token_failure, [:pointer, :string], :int
|
|
32
32
|
# Log queue
|
|
33
33
|
attach_function :rd_kafka_set_log_queue, [:pointer, :pointer], :void
|
|
@@ -45,6 +45,6 @@ module Rdkafka
|
|
|
45
45
|
# Per topic configs
|
|
46
46
|
attach_function :rd_kafka_topic_conf_new, [], :pointer
|
|
47
47
|
attach_function :rd_kafka_topic_conf_destroy, [:pointer], :void
|
|
48
|
-
attach_function :rd_kafka_topic_conf_set, [:pointer, :string, :string, :pointer, :
|
|
48
|
+
attach_function :rd_kafka_topic_conf_set, [:pointer, :string, :string, :pointer, :size_t], :kafka_config_response
|
|
49
49
|
end
|
|
50
50
|
end
|
|
@@ -19,7 +19,7 @@ module Rdkafka
|
|
|
19
19
|
# Non-blocking consumer poll variant (does not release GVL)
|
|
20
20
|
# More efficient for poll(0) calls in fiber schedulers.
|
|
21
21
|
attach_function :rd_kafka_consumer_poll_nb, :rd_kafka_consumer_poll, [:pointer, :int], :pointer, blocking: false
|
|
22
|
-
attach_function :rd_kafka_consumer_close, [:pointer], :
|
|
22
|
+
attach_function :rd_kafka_consumer_close, [:pointer], :int, blocking: true
|
|
23
23
|
attach_function :rd_kafka_queue_get_consumer, [:pointer], :pointer
|
|
24
24
|
attach_function :rd_kafka_consume_batch_queue, [:pointer, :int, :pointer, :size_t], :ssize_t, blocking: true
|
|
25
25
|
attach_function :rd_kafka_consume_batch_queue_nb, :rd_kafka_consume_batch_queue, [:pointer, :int, :pointer, :size_t], :ssize_t, blocking: false
|
|
@@ -61,7 +61,7 @@ module Rdkafka
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
opaque = Rdkafka::Config.opaques[opaque_ptr.to_i]
|
|
64
|
-
|
|
64
|
+
next unless opaque
|
|
65
65
|
|
|
66
66
|
tpl = Rdkafka::Consumer::TopicPartitionList.from_native_tpl(partitions_ptr).freeze
|
|
67
67
|
begin
|
|
@@ -76,6 +76,48 @@ module Rdkafka
|
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
# Offset commit
|
|
80
|
+
|
|
81
|
+
callback :offset_commit_cb_function, [:pointer, :int, :pointer, :pointer], :void
|
|
82
|
+
attach_function :rd_kafka_conf_set_offset_commit_cb, [:pointer, :offset_commit_cb_function], :void, blocking: true
|
|
83
|
+
|
|
84
|
+
# Served from the consumer poll (or the events poll when `consumer_poll_set` is false) and
|
|
85
|
+
# from the consumer close, on the thread that calls them
|
|
86
|
+
OffsetCommitCallback = FFI::Function.new(
|
|
87
|
+
:void, [:pointer, :int, :pointer, :pointer]
|
|
88
|
+
) do |_client_ptr, code, offsets_ptr, opaque_ptr|
|
|
89
|
+
opaque = Rdkafka::Config.opaques[opaque_ptr.to_i]
|
|
90
|
+
next unless opaque
|
|
91
|
+
|
|
92
|
+
# Success and `no_offset` (a commit with nothing new to commit) are not failures
|
|
93
|
+
failed = code != RD_KAFKA_RESP_ERR_NO_ERROR && code != RD_KAFKA_RESP_ERR__NO_OFFSET
|
|
94
|
+
every = opaque.offsets_committed_listened?
|
|
95
|
+
on_failure = failed && opaque.offsets_commit_failed_listened?
|
|
96
|
+
|
|
97
|
+
# The list has an entry for every assigned partition, so build it only when it is used
|
|
98
|
+
if every || on_failure
|
|
99
|
+
error = code.zero? ? nil : Rdkafka::RdkafkaError.new(code)
|
|
100
|
+
tpl = if offsets_ptr.null?
|
|
101
|
+
Rdkafka::Consumer::TopicPartitionList.new
|
|
102
|
+
else
|
|
103
|
+
Rdkafka::Consumer::TopicPartitionList.from_native_tpl(offsets_ptr)
|
|
104
|
+
end
|
|
105
|
+
tpl.freeze
|
|
106
|
+
|
|
107
|
+
begin
|
|
108
|
+
opaque.call_on_offsets_committed(error, tpl) if every
|
|
109
|
+
rescue Exception => err
|
|
110
|
+
Rdkafka::Config.logger.error("Unhandled exception: #{err.class} - #{err.message}")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
begin
|
|
114
|
+
opaque.call_on_offsets_commit_failed(error, tpl) if on_failure
|
|
115
|
+
rescue Exception => err
|
|
116
|
+
Rdkafka::Config.logger.error("Unhandled exception: #{err.class} - #{err.message}")
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
79
121
|
# Watermark offsets
|
|
80
122
|
|
|
81
123
|
attach_function :rd_kafka_query_watermark_offsets, [:pointer, :string, :int, :pointer, :pointer, :int], :int, blocking: true
|
|
@@ -11,7 +11,7 @@ module Rdkafka
|
|
|
11
11
|
attach_function :rd_kafka_error_code, [:pointer], :int32
|
|
12
12
|
attach_function :rd_kafka_error_string, [:pointer], :pointer
|
|
13
13
|
attach_function :rd_kafka_get_err_descs, [:pointer, :pointer], :void
|
|
14
|
-
attach_function :rd_kafka_fatal_error, [:pointer, :pointer, :
|
|
14
|
+
attach_function :rd_kafka_fatal_error, [:pointer, :pointer, :size_t], :int
|
|
15
15
|
attach_function :rd_kafka_test_fatal_error, [:pointer, :int, :string], :int
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -9,7 +9,7 @@ module Rdkafka
|
|
|
9
9
|
:rd_kafka_consumer
|
|
10
10
|
]
|
|
11
11
|
|
|
12
|
-
attach_function :rd_kafka_new, [:kafka_type, :pointer, :pointer, :
|
|
12
|
+
attach_function :rd_kafka_new, [:kafka_type, :pointer, :pointer, :size_t], :pointer
|
|
13
13
|
|
|
14
14
|
attach_function :rd_kafka_destroy, [:pointer], :void
|
|
15
15
|
end
|
data/lib/rdkafka/bindings.rb
CHANGED
|
@@ -58,13 +58,16 @@ module Rdkafka
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
+
RD_KAFKA_RESP_ERR__DESTROY = -197
|
|
61
62
|
RD_KAFKA_RESP_ERR__TIMED_OUT = -185
|
|
62
63
|
RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS = -175
|
|
63
64
|
RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS = -174
|
|
64
65
|
RD_KAFKA_RESP_ERR__STATE = -172
|
|
66
|
+
RD_KAFKA_RESP_ERR__NO_OFFSET = -168
|
|
65
67
|
RD_KAFKA_RESP_ERR__NOENT = -156
|
|
66
68
|
RD_KAFKA_RESP_ERR__FATAL = -150
|
|
67
69
|
RD_KAFKA_RESP_ERR_NO_ERROR = 0
|
|
70
|
+
RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH = 47
|
|
68
71
|
|
|
69
72
|
# Buffer size for fatal error strings, matches librdkafka expectations
|
|
70
73
|
FATAL_ERROR_BUFFER_SIZE = 256
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
module Callbacks
|
|
5
|
+
# Handles `RD_KAFKA_EVENT_LISTCONSUMERGROUPOFFSETS_RESULT` events
|
|
6
|
+
# @private
|
|
7
|
+
class ListConsumerGroupOffsetsHandler < BaseHandler
|
|
8
|
+
class << self
|
|
9
|
+
# Resolves the list consumer group offsets handle from its result event
|
|
10
|
+
# @param event_ptr [FFI::Pointer] pointer to the event
|
|
11
|
+
# @return [void]
|
|
12
|
+
def call(event_ptr)
|
|
13
|
+
result = Rdkafka::Bindings.rd_kafka_event_ListConsumerGroupOffsets_result(event_ptr)
|
|
14
|
+
|
|
15
|
+
pointer_to_size_t = FFI::MemoryPointer.new(:size_t)
|
|
16
|
+
group_result_array = Rdkafka::Bindings.rd_kafka_ListConsumerGroupOffsets_result_groups(result, pointer_to_size_t)
|
|
17
|
+
group_results = GroupResult.create_group_results_from_array(pointer_to_size_t.read_int, group_result_array)
|
|
18
|
+
handle_ptr = Rdkafka::Bindings.rd_kafka_event_opaque(event_ptr)
|
|
19
|
+
|
|
20
|
+
if (handle = Rdkafka::Admin::ListConsumerGroupOffsetsHandle.remove(handle_ptr.address))
|
|
21
|
+
unless resolve_operation_error(event_ptr, handle)
|
|
22
|
+
group_result = group_results[0]
|
|
23
|
+
|
|
24
|
+
# librdkafka only supports one group per invocation
|
|
25
|
+
if group_result.nil?
|
|
26
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
27
|
+
handle.result = Rdkafka::Admin::ListConsumerGroupOffsetsReport.new(
|
|
28
|
+
FFI::Pointer::NULL,
|
|
29
|
+
FFI::Pointer::NULL
|
|
30
|
+
)
|
|
31
|
+
else
|
|
32
|
+
report = Rdkafka::Admin::ListConsumerGroupOffsetsReport.new(
|
|
33
|
+
group_result.error_string,
|
|
34
|
+
group_result.result_name,
|
|
35
|
+
Rdkafka::Bindings.rd_kafka_group_result_partitions(group_result_array.read_pointer)
|
|
36
|
+
)
|
|
37
|
+
handle.result = report
|
|
38
|
+
|
|
39
|
+
# The group level error can be clean while a partition failed (e.g.
|
|
40
|
+
# `unstable_offset_commit` with `require_stable_offsets`), which must not look
|
|
41
|
+
# like a committed offset of nil.
|
|
42
|
+
failed = report.partitions.find { |partition| partition[:error] }
|
|
43
|
+
|
|
44
|
+
if !group_result.result_error.zero?
|
|
45
|
+
handle[:response] = group_result.result_error
|
|
46
|
+
handle.broker_message = report.error_string
|
|
47
|
+
elsif failed
|
|
48
|
+
handle[:response] = failed[:error].rdkafka_response
|
|
49
|
+
handle.broker_message = "Failed for partition #{failed[:partition]} of '#{failed[:topic]}'"
|
|
50
|
+
else
|
|
51
|
+
handle[:response] = Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
handle.unlock
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/rdkafka/callbacks.rb
CHANGED
|
@@ -216,6 +216,7 @@ module Rdkafka
|
|
|
216
216
|
when Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_CREATEPARTITIONS_RESULT then CreatePartitionsHandler
|
|
217
217
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETEGROUPS_RESULT then DeleteGroupsHandler
|
|
218
218
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_ALTERCONSUMERGROUPOFFSETS_RESULT then AlterConsumerGroupOffsetsHandler
|
|
219
|
+
when Rdkafka::Bindings::RD_KAFKA_EVENT_LISTCONSUMERGROUPOFFSETS_RESULT then ListConsumerGroupOffsetsHandler
|
|
219
220
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETECONSUMERGROUPOFFSETS_RESULT then DeleteConsumerGroupOffsetsHandler
|
|
220
221
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_DELETERECORDS_RESULT then DeleteRecordsHandler
|
|
221
222
|
when Rdkafka::Bindings::RD_KAFKA_EVENT_CREATEACLS_RESULT then CreateAclHandler
|
|
@@ -250,8 +251,6 @@ module Rdkafka
|
|
|
250
251
|
message = Rdkafka::Bindings::Message.new(message_ptr)
|
|
251
252
|
delivery_handle_ptr_address = message[:_private].address
|
|
252
253
|
if delivery_handle = Rdkafka::Producer::DeliveryHandle.remove(delivery_handle_ptr_address)
|
|
253
|
-
topic_name = Rdkafka::Bindings.rd_kafka_topic_name(message[:rkt])
|
|
254
|
-
|
|
255
254
|
# Update delivery handle
|
|
256
255
|
delivery_handle[:response] = message[:err]
|
|
257
256
|
delivery_handle[:partition] = message[:partition]
|
|
@@ -259,9 +258,9 @@ module Rdkafka
|
|
|
259
258
|
delivery_handle[:status] = Rdkafka::Bindings.rd_kafka_message_status(message_ptr)
|
|
260
259
|
delivery_handle[:latency] = Rdkafka::Bindings.rd_kafka_message_latency(message_ptr)
|
|
261
260
|
delivery_handle[:broker_id] = Rdkafka::Bindings.rd_kafka_message_broker_id(message_ptr)
|
|
262
|
-
# The topic is
|
|
263
|
-
#
|
|
264
|
-
#
|
|
261
|
+
# The topic is taken from the handle's `topic` Ruby attribute set during produce, not
|
|
262
|
+
# read back from librdkafka, which spares an FFI call and a native string copy for
|
|
263
|
+
# every message.
|
|
265
264
|
|
|
266
265
|
begin
|
|
267
266
|
# Call delivery callback on opaque
|
|
@@ -270,7 +269,7 @@ module Rdkafka
|
|
|
270
269
|
Rdkafka::Producer::DeliveryReport.new(
|
|
271
270
|
message[:partition],
|
|
272
271
|
message[:offset],
|
|
273
|
-
|
|
272
|
+
delivery_handle.topic,
|
|
274
273
|
message[:err],
|
|
275
274
|
delivery_handle.label,
|
|
276
275
|
status: delivery_handle[:status],
|