rdkafka 0.29.2-x86_64-linux-gnu → 0.31.0-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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -15
- data/README.md +2 -0
- data/docker-compose-ssl.yml +1 -0
- data/docker-compose.yml +1 -0
- data/ext/librdkafka.so +0 -0
- data/lib/rdkafka/admin/alter_consumer_group_offsets_handle.rb +31 -0
- data/lib/rdkafka/admin/alter_consumer_group_offsets_report.rb +50 -0
- data/lib/rdkafka/admin/delete_consumer_group_offsets_handle.rb +31 -0
- data/lib/rdkafka/admin/delete_consumer_group_offsets_report.rb +50 -0
- data/lib/rdkafka/admin/describe_cluster_handle.rb +31 -0
- data/lib/rdkafka/admin/describe_cluster_report.rb +48 -0
- data/lib/rdkafka/admin/describe_consumer_groups_handle.rb +31 -0
- data/lib/rdkafka/admin/describe_consumer_groups_report.rb +151 -0
- data/lib/rdkafka/admin/describe_result_parsing.rb +47 -0
- data/lib/rdkafka/admin/describe_topics_handle.rb +31 -0
- data/lib/rdkafka/admin/describe_topics_report.rb +118 -0
- data/lib/rdkafka/admin.rb +365 -0
- data/lib/rdkafka/bindings/admin/acls.rb +85 -0
- data/lib/rdkafka/bindings/admin/configs.rb +37 -0
- data/lib/rdkafka/bindings/admin/list_offsets.rb +26 -0
- data/lib/rdkafka/bindings/admin.rb +197 -0
- data/lib/rdkafka/bindings/callbacks.rb +100 -0
- data/lib/rdkafka/bindings/configuration.rb +50 -0
- data/lib/rdkafka/bindings/consumer.rb +83 -0
- data/lib/rdkafka/bindings/errors.rb +15 -0
- data/lib/rdkafka/bindings/handle.rb +16 -0
- data/lib/rdkafka/bindings/messages.rb +28 -0
- data/lib/rdkafka/bindings/metadata.rb +14 -0
- data/lib/rdkafka/bindings/polling.rb +17 -0
- data/lib/rdkafka/bindings/producer.rb +77 -0
- data/lib/rdkafka/bindings/structs.rb +62 -0
- data/lib/rdkafka/bindings.rb +12 -612
- data/lib/rdkafka/callbacks/alter_consumer_group_offsets_handler.rb +65 -0
- data/lib/rdkafka/callbacks/delete_consumer_group_offsets_handler.rb +65 -0
- data/lib/rdkafka/callbacks/describe_cluster_handler.rb +36 -0
- data/lib/rdkafka/callbacks/describe_consumer_groups_handler.rb +36 -0
- data/lib/rdkafka/callbacks/describe_topics_handler.rb +36 -0
- data/lib/rdkafka/callbacks.rb +24 -17
- data/lib/rdkafka/clients.rb +13 -46
- data/lib/rdkafka/config.rb +34 -0
- data/lib/rdkafka/consumer/message.rb +6 -9
- data/lib/rdkafka/producer/delivery_handle.rb +8 -2
- data/lib/rdkafka/producer/delivery_report.rb +51 -1
- data/lib/rdkafka/version.rb +3 -3
- data/lib/rdkafka.rb +16 -0
- data/package-lock.json +9 -9
- data/rdkafka.gemspec +1 -1
- data/renovate.json +3 -0
- metadata +33 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3813cf92e9503b7c057001845673c05518bb07d46f887e413c477a4a9398e283
|
|
4
|
+
data.tar.gz: 1f432135cb27bf676f9eb967375408eb25d1a2bb8c35d3b4b65147f6c711a164
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 196fcbf499c317c319ad109621816a6eed25914e7f46918542321871cbc0ea49df73fbb5f08904eea0852ba20bfe3927f01e5a67bdc06a1a7190dd4d21f74e62
|
|
7
|
+
data.tar.gz: 6f74d295573b9740f4f83d8c0373452dcc9325205c8954e82ec55a3de30d694108d612b3ee28d63ca3389b78ae1b68ba67f0036ba33e4919a61dea4b103fdb54
|
data/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,38 @@
|
|
|
1
1
|
# Rdkafka Changelog
|
|
2
2
|
|
|
3
|
+
## 0.31.0 (2026-10-09)
|
|
4
|
+
- **[EOL]** Drop Ruby 3.2 support; require Ruby 3.3+.
|
|
5
|
+
- [Feature] Add `status`, `latency` and `broker_id` to `Producer::DeliveryReport`, with `#persisted?`, `#possibly_persisted?` and `#not_persisted?`, so a failed delivery tells whether a retry may duplicate the message.
|
|
6
|
+
- [Feature] Add `Admin#describe_cluster` to get the cluster id, controller and broker nodes (with racks) without a full metadata request.
|
|
7
|
+
- [Feature] Add `Admin#describe_topics` to get topic ids, partition leaders and replicas, and per-topic errors. Both methods can also return the operations the client is authorized to perform.
|
|
8
|
+
- [Feature] Add `Admin#describe_consumer_groups` to read the state, type, coordinator, authorized operations and members (with their current and target partition assignments) of consumer groups.
|
|
9
|
+
- [Enhancement] Bump librdkafka to `2.15.1` for OpenSSL and libcurl security fixes. It changes how IPv6 addresses are formatted and validated against broker certificates.
|
|
10
|
+
- [Enhancement] Bump the bundled OpenSSL to `3.5.9` (LTS) for its security fixes.
|
|
11
|
+
- [Enhancement] Read each pointer field once in `Message#initialize`, saving 3 allocations per consumed message.
|
|
12
|
+
- [Change] Build the precompiled `macos_arm64` library on macOS 26 with the minimum macOS pinned to 14.0, instead of inheriting it from the build host.
|
|
13
|
+
- [Change] Build the precompiled `aarch64-linux-musl` gem on Alpine `3.24` (was `3.23`), matching `x86_64-linux-musl`.
|
|
14
|
+
- [Maintenance] Split `lib/rdkafka/bindings.rb` into per-area files under `lib/rdkafka/bindings/`. `Rdkafka::Bindings` is unchanged.
|
|
15
|
+
|
|
16
|
+
## 0.30.0 (2026-09-25)
|
|
17
|
+
- [Feature] Add `Admin#alter_consumer_group_offsets` and `Admin#delete_consumer_group_offsets` to set or clear a consumer group's committed offsets from the admin client.
|
|
18
|
+
- [Enhancement] Bump the bundled zlib to `1.3.2`.
|
|
19
|
+
- [Enhancement] Bump librdkafka to `2.15.0`.
|
|
20
|
+
- [Enhancement] Bump the bundled MIT Kerberos (krb5) to `1.22.2`.
|
|
21
|
+
- [Enhancement] Add `Rdkafka::Config.partitioner_key_uses_bytesize` to hash partition keys by byte length, matching other Kafka clients for multibyte keys. Defaults to `false`.
|
|
22
|
+
- [Maintenance] Derive the zlib `CHECKSUMS` entry from `ZLIB_VERSION`.
|
|
23
|
+
- [Maintenance] Drop the unused `dist/openssl-3.0.16.tar.gz` build cache.
|
|
24
|
+
- [Fix] Honor the `isolation_level:` argument to `Admin#list_offsets`, which was silently ignored.
|
|
25
|
+
- [Fix] Return correct results for every item of a multi-item topic, partition, group or ACL admin request, not just the first.
|
|
26
|
+
- [Maintenance] Fix a use-after-free in the multi-item admin integration test that read result-name pointers after librdkafka had destroyed the background event, which made it flaky on newer glibc (e.g. Debian trixie).
|
|
27
|
+
- [Maintenance] Stabilize consumer specs on slow CI runners: wait longer for a partition assignment, raise the `TestTopics.create` admin timeout and drop the fixed 60s limit of the long-running consumption spec.
|
|
28
|
+
- [Maintenance] Raise the `TestTopics.create` admin timeout to 30s so a loaded CI runner no longer hits `create topic timed out` (which also tripped the leaked-handle guard).
|
|
29
|
+
|
|
3
30
|
## 0.29.2 (2026-09-11)
|
|
4
31
|
- [Fix] Close live clients from an `at_exit` hook before Ruby's shutdown finalization, so librdkafka is no longer `dlclose`d while its native threads are still running (which could segfault on exit).
|
|
5
32
|
- [Fix] Make client construction exception-safe and destroy the native handle when setup fails after `rd_kafka_new`, so a later error no longer orphans the native client.
|
|
6
|
-
- [Fix]
|
|
7
|
-
- [Fix] Free
|
|
8
|
-
- [Fix] Raise
|
|
33
|
+
- [Fix] Fix a double-free in `Consumer#poll_batch`/`#poll_batch_nb` that could abort the process when building a message raised a non-`RdkafkaError`.
|
|
34
|
+
- [Fix] Free native resources when `Admin#describe_configs`/`#incremental_alter_configs` raise while building their request (e.g. a non-String resource name).
|
|
35
|
+
- [Fix] Raise `ConfigError` instead of segfaulting when `Admin#describe_configs`/`#incremental_alter_configs` get an empty resource name or a negative resource type.
|
|
9
36
|
|
|
10
37
|
## 0.29.1 (2026-09-04)
|
|
11
38
|
- [Enhancement] Add `Admin#delete_records` to delete all messages in a partition up to a given offset.
|
|
@@ -19,8 +46,8 @@
|
|
|
19
46
|
|
|
20
47
|
## 0.29.0 (2026-07-10)
|
|
21
48
|
- [Enhancement] Bump librdkafka to `2.14.2`
|
|
22
|
-
- [Enhancement] Add `Consumer#metadata` and `Producer#metadata`, mirroring `Admin#metadata`,
|
|
23
|
-
- [Enhancement] Support offset-commit metadata
|
|
49
|
+
- [Enhancement] Add `Consumer#metadata` and `Producer#metadata`, mirroring `Admin#metadata`, to fetch cluster metadata without a separate admin connection.
|
|
50
|
+
- [Enhancement] Support offset-commit metadata via `Consumer#store_offset(message, metadata)`, readable through `Consumer::Partition#metadata` and `Consumer#committed`.
|
|
24
51
|
- [Enhancement] Expose `replicas` and `isrs` (in-sync replica broker ids) on each partition in topic metadata; both were previously dropped from the `Metadata#topics` partition hashes.
|
|
25
52
|
- [Enhancement] Extract the admin background-event result handlers into one class per operation under `lib/rdkafka/callbacks/`. Internal reorganization with no API or behavior change.
|
|
26
53
|
- [Enhancement] Reuse per-thread scratch pointers in `Consumer::Headers.from_native`, removing the per-message native allocations from the consumer hot path.
|
|
@@ -28,32 +55,32 @@
|
|
|
28
55
|
- [Fix] Cache the partition count for a missing topic, so `produce` with a `partition_key` to a missing topic no longer re-runs a blocking metadata query on every message.
|
|
29
56
|
- [Fix] Let `PartitionsCountCache` adopt a lower partition count once the cached entry has expired, so a topic recreated with fewer partitions no longer fails `produce` until process restart.
|
|
30
57
|
- [Fix] Surface per-message build errors inline in `Consumer#poll_batch`/`#poll_batch_nb` instead of discarding the batch, preserving the messages that built successfully.
|
|
31
|
-
- [Fix] Bound the `Metadata` retry loop
|
|
58
|
+
- [Fix] Bound the `Metadata` retry loop to about 5 seconds, so a synchronous metadata fetch can no longer block for minutes.
|
|
32
59
|
- [Fix] Synchronize `AbstractHandle::REGISTRY` mutations with a mutex, which could otherwise lose a write on JRuby and leave a handle unregistered or leaked.
|
|
33
|
-
- [Fix]
|
|
60
|
+
- [Fix] Raise `ConfigError` instead of `NameError` in `Admin#delete_group`, `#delete_acl` and `#describe_acl` when the background queue is unavailable.
|
|
34
61
|
- [Fix] Actually populate `ConfigBindingResult#synonyms`. The synonym loop built the synonym binding objects and discarded them, so `#synonyms` was always an empty array; they are now appended.
|
|
35
62
|
- [Fix] Stop leaking the native `rd_kafka_topic_conf_t` in `Producer#set_topic_config` when a per-topic config value is rejected.
|
|
36
|
-
- [Fix]
|
|
63
|
+
- [Fix] Raise `ClosedConsumerError` from `Consumer#position` on a closed consumer, like the other consumer methods.
|
|
37
64
|
- [Fix] Guard the OAuthBearer token refresh callback against a NULL client pointer before calling `rd_kafka_name` on it.
|
|
38
|
-
- [Fix]
|
|
65
|
+
- [Fix] Fully close a consumer collected without an explicit `close`, preventing a hang or leak.
|
|
39
66
|
- [Fix] Free the librdkafka-allocated string in `Consumer#cluster_id` and `Consumer#member_id`, and fix the `rd_kafka_clusterid` arity. `Consumer#cluster_id` now accepts a `timeout_ms`.
|
|
40
67
|
- [Fix] Attach `rd_kafka_query_watermark_offsets` with `blocking: true` so it releases the GVL; it previously froze every other Ruby thread for up to `timeout_ms`.
|
|
41
68
|
- [Fix] Stop leaking the native `rd_kafka_conf_t` when client creation fails, a multi-KB leak per failed attempt for supervisors retrying on transient SASL/SSL misconfiguration.
|
|
42
69
|
- [Fix] Stop `Metadata` from leaking the native metadata struct on every retried fetch; each attempt now frees its own native resources.
|
|
43
70
|
- [Fix] Stop `Producer#produce` from orphaning the delivery handle in the process-global registry when it fails after registering it.
|
|
44
71
|
- [Fix] Guard the message delivery callback so a raising user `delivery_callback` can no longer leave the handle locked or crash the producer; exceptions are now logged and swallowed.
|
|
45
|
-
- [Fix] Stop `describe_configs`, `incremental_alter_configs` and `list_offsets` from leaking
|
|
46
|
-
- [Fix]
|
|
72
|
+
- [Fix] Stop `describe_configs`, `incremental_alter_configs` and `list_offsets` from leaking native resources when their arguments are rejected.
|
|
73
|
+
- [Fix] Stop `TopicPartitionList#to_native_tpl` from leaking the native list when building it fails partway.
|
|
47
74
|
- [Fix] Raise instead of silently dropping a rejected `incremental_alter_configs` entry, which previously left the alter request reporting success.
|
|
48
75
|
- [Fix] Allocate the admin result-count out-parameter as `:size_t` instead of `:int32`, fixing a 4-byte overflow on every admin result parse.
|
|
49
76
|
- [Fix] Stabilize the flaky `Consumer#lag` "calculates the consumer lag" spec on overloaded CI.
|
|
50
|
-
- [Fix]
|
|
51
|
-
- [Fix]
|
|
77
|
+
- [Fix] Return the real error from an admin operation that fails at the operation level (e.g. brokers unreachable) instead of blocking until `wait` times out.
|
|
78
|
+
- [Fix] Stop every admin operation from leaking its result event. The internal FFI struct fields on admin handles are removed; use `handle.wait` and the returned report objects, which are unchanged.
|
|
52
79
|
|
|
53
80
|
## 0.28.0 (2026-06-03)
|
|
54
81
|
- [Enhancement] Bump librdkafka to `2.14.1`
|
|
55
82
|
- [Enhancement] Replace version-gated `Warning[:performance]` with dynamic `Warning.categories` opt-in in spec helper so new Ruby warning categories are automatically enabled without manual updates.
|
|
56
|
-
- [Enhancement] `poll_batch` and `poll_batch_nb` now return
|
|
83
|
+
- [Enhancement] `poll_batch` and `poll_batch_nb` now return errors inline as `RdkafkaError` objects instead of raising on the first one. The return type is `Array<Message, RdkafkaError>`, so callers must handle errors in the result.
|
|
57
84
|
|
|
58
85
|
## 0.27.0 (2026-05-07)
|
|
59
86
|
- [Feature] Add `Consumer#poll_batch(timeout_ms, max_items:)` and `Consumer#poll_batch_nb(timeout_ms, max_items:)` for batch message polling via `rd_kafka_consume_batch_queue`.
|
|
@@ -75,7 +102,7 @@
|
|
|
75
102
|
- [Feature] Support `rd_kafka_ListOffsets` admin API for querying partition offsets by specification (earliest, latest, max_timestamp, or by timestamp) without requiring a consumer group (#613).
|
|
76
103
|
- [Enhancement] Use native ARM64 runners instead of QEMU emulation for Alpine musl aarch64 builds, improving build performance and reliability.
|
|
77
104
|
- [Enhancement] Enable parallel compilation (`make -j$(nproc)`) for ARM64 Alpine musl builds.
|
|
78
|
-
- [Enhancement] Add
|
|
105
|
+
- [Enhancement] Add `enable_queue_io_events` and `enable_background_queue_io_events` to `Consumer`, `Producer` and `Admin` for non-blocking file descriptor monitoring with fiber schedulers (Falcon, Async) and custom event loops.
|
|
79
106
|
- [Enhancement] Add non-blocking poll methods (`poll_nb`, `events_poll_nb`) on `Consumer` that skip GVL release for efficient fiber scheduler integration when using `poll(0)`.
|
|
80
107
|
- [Enhancement] Add `events_poll_nb_each` method on `Producer`, `Consumer`, and `Admin` for polling events in a single GVL/mutex session. Yields count after each iteration, caller returns `:stop` to break.
|
|
81
108
|
- [Enhancement] Add `poll_nb_each` method on `Consumer` for non-blocking message polling with proper resource cleanup, yielding each message and supporting early termination via `:stop` return value.
|
data/README.md
CHANGED
|
@@ -163,6 +163,8 @@ bundle exec rake produce_messages
|
|
|
163
163
|
|
|
164
164
|
| rdkafka-ruby | librdkafka | patches |
|
|
165
165
|
|-|-|-|
|
|
166
|
+
| 0.31.x (2026-10-09) | 2.15.1 (2026-09-09) | yes |
|
|
167
|
+
| 0.30.x (2026-09-25) | 2.15.0 (2026-06-30) | yes |
|
|
166
168
|
| 0.29.x (2026-07-10) | 2.14.2 (2026-06-03) | yes |
|
|
167
169
|
| 0.28.x (2026-06-03) | 2.14.1 (2026-04-15) | yes |
|
|
168
170
|
| 0.27.x (2026-05-07) | 2.14.0 (2026-04-01) | yes |
|
data/docker-compose-ssl.yml
CHANGED
|
@@ -17,6 +17,7 @@ services:
|
|
|
17
17
|
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL
|
|
18
18
|
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://127.0.0.1:9092,SSL://127.0.0.1:9093
|
|
19
19
|
KAFKA_BROKER_ID: 1
|
|
20
|
+
KAFKA_BROKER_RACK: rack-1
|
|
20
21
|
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@127.0.0.1:9094
|
|
21
22
|
ALLOW_PLAINTEXT_LISTENER: 'yes'
|
|
22
23
|
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'false'
|
data/docker-compose.yml
CHANGED
|
@@ -16,6 +16,7 @@ services:
|
|
|
16
16
|
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
|
|
17
17
|
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://127.0.0.1:9092
|
|
18
18
|
KAFKA_BROKER_ID: 1
|
|
19
|
+
KAFKA_BROKER_RACK: rack-1
|
|
19
20
|
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@127.0.0.1:9093
|
|
20
21
|
ALLOW_PLAINTEXT_LISTENER: 'yes'
|
|
21
22
|
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'false'
|
data/ext/librdkafka.so
CHANGED
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Handle for alter consumer group offsets operation
|
|
6
|
+
class AlterConsumerGroupOffsetsHandle < AbstractHandle
|
|
7
|
+
layout :pending, :bool,
|
|
8
|
+
:response, :int
|
|
9
|
+
|
|
10
|
+
# @return [String] the name of the operation
|
|
11
|
+
def operation_name
|
|
12
|
+
"alter consumer group offsets"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Creates the result report
|
|
16
|
+
# @return [AlterConsumerGroupOffsetsReport]
|
|
17
|
+
def create_result
|
|
18
|
+
prepared_result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Raises an error if the operation failed
|
|
22
|
+
# @raise [RdkafkaError]
|
|
23
|
+
def raise_error
|
|
24
|
+
raise RdkafkaError.new(
|
|
25
|
+
self[:response],
|
|
26
|
+
broker_message: broker_message
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Report for alter consumer group offsets operation result
|
|
6
|
+
class AlterConsumerGroupOffsetsReport
|
|
7
|
+
# @return [String, nil] the group the operation ran against
|
|
8
|
+
attr_reader :group_name
|
|
9
|
+
|
|
10
|
+
# @return [String, nil] group level error message, if any
|
|
11
|
+
attr_reader :error_string
|
|
12
|
+
|
|
13
|
+
# Per partition outcomes, each with `:topic`, `:partition`, `:offset` and `:error`
|
|
14
|
+
# (an `RdkafkaError` or nil). Partitions are reported individually because Kafka can
|
|
15
|
+
# fail some and accept others within a single request.
|
|
16
|
+
# @return [Array<Hash>]
|
|
17
|
+
attr_reader :partitions
|
|
18
|
+
|
|
19
|
+
# @param error_string [FFI::Pointer] pointer to the group level error string
|
|
20
|
+
# @param result_name [FFI::Pointer] pointer to the group name
|
|
21
|
+
# @param partitions_ptr [FFI::Pointer] pointer to the per partition result list
|
|
22
|
+
def initialize(error_string, result_name, partitions_ptr = FFI::Pointer::NULL)
|
|
23
|
+
@error_string = error_string.read_string if error_string != FFI::Pointer::NULL
|
|
24
|
+
@group_name = result_name.read_string if result_name != FFI::Pointer::NULL
|
|
25
|
+
@partitions = extract_partitions(partitions_ptr)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# @param partitions_ptr [FFI::Pointer] pointer to a native topic partition list
|
|
31
|
+
# @return [Array<Hash>]
|
|
32
|
+
def extract_partitions(partitions_ptr)
|
|
33
|
+
return [] if partitions_ptr.nil? || partitions_ptr.null?
|
|
34
|
+
|
|
35
|
+
native = Bindings::TopicPartitionList.new(partitions_ptr)
|
|
36
|
+
|
|
37
|
+
Array.new(native[:cnt]) do |i|
|
|
38
|
+
tp = Bindings::TopicPartition.new(native[:elems] + (i * Bindings::TopicPartition.size))
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
topic: tp[:topic],
|
|
42
|
+
partition: tp[:partition],
|
|
43
|
+
offset: tp[:offset],
|
|
44
|
+
error: tp[:err].zero? ? nil : RdkafkaError.new(tp[:err])
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Handle for delete consumer group offsets operation
|
|
6
|
+
class DeleteConsumerGroupOffsetsHandle < AbstractHandle
|
|
7
|
+
layout :pending, :bool,
|
|
8
|
+
:response, :int
|
|
9
|
+
|
|
10
|
+
# @return [String] the name of the operation
|
|
11
|
+
def operation_name
|
|
12
|
+
"delete consumer group offsets"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Creates the result report
|
|
16
|
+
# @return [DeleteConsumerGroupOffsetsReport]
|
|
17
|
+
def create_result
|
|
18
|
+
prepared_result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Raises an error if the operation failed
|
|
22
|
+
# @raise [RdkafkaError]
|
|
23
|
+
def raise_error
|
|
24
|
+
raise RdkafkaError.new(
|
|
25
|
+
self[:response],
|
|
26
|
+
broker_message: broker_message
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Report for delete consumer group offsets operation result
|
|
6
|
+
class DeleteConsumerGroupOffsetsReport
|
|
7
|
+
# @return [String, nil] the group the operation ran against
|
|
8
|
+
attr_reader :group_name
|
|
9
|
+
|
|
10
|
+
# @return [String, nil] group level error message, if any
|
|
11
|
+
attr_reader :error_string
|
|
12
|
+
|
|
13
|
+
# Per partition outcomes, each with `:topic`, `:partition`, `:offset` and `:error`
|
|
14
|
+
# (an `RdkafkaError` or nil). Partitions are reported individually because Kafka can
|
|
15
|
+
# fail some and accept others within a single request.
|
|
16
|
+
# @return [Array<Hash>]
|
|
17
|
+
attr_reader :partitions
|
|
18
|
+
|
|
19
|
+
# @param error_string [FFI::Pointer] pointer to the group level error string
|
|
20
|
+
# @param result_name [FFI::Pointer] pointer to the group name
|
|
21
|
+
# @param partitions_ptr [FFI::Pointer] pointer to the per partition result list
|
|
22
|
+
def initialize(error_string, result_name, partitions_ptr = FFI::Pointer::NULL)
|
|
23
|
+
@error_string = error_string.read_string if error_string != FFI::Pointer::NULL
|
|
24
|
+
@group_name = result_name.read_string if result_name != FFI::Pointer::NULL
|
|
25
|
+
@partitions = extract_partitions(partitions_ptr)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# @param partitions_ptr [FFI::Pointer] pointer to a native topic partition list
|
|
31
|
+
# @return [Array<Hash>]
|
|
32
|
+
def extract_partitions(partitions_ptr)
|
|
33
|
+
return [] if partitions_ptr.nil? || partitions_ptr.null?
|
|
34
|
+
|
|
35
|
+
native = Bindings::TopicPartitionList.new(partitions_ptr)
|
|
36
|
+
|
|
37
|
+
Array.new(native[:cnt]) do |i|
|
|
38
|
+
tp = Bindings::TopicPartition.new(native[:elems] + (i * Bindings::TopicPartition.size))
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
topic: tp[:topic],
|
|
42
|
+
partition: tp[:partition],
|
|
43
|
+
offset: tp[:offset],
|
|
44
|
+
error: tp[:err].zero? ? nil : RdkafkaError.new(tp[:err])
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Handle for describe cluster operation
|
|
6
|
+
class DescribeClusterHandle < AbstractHandle
|
|
7
|
+
layout :pending, :bool,
|
|
8
|
+
:response, :int
|
|
9
|
+
|
|
10
|
+
# @return [String] the name of the operation
|
|
11
|
+
def operation_name
|
|
12
|
+
"describe cluster"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [DescribeClusterReport] report prepared by the background event callback, with
|
|
16
|
+
# the cluster id, controller and broker nodes.
|
|
17
|
+
def create_result
|
|
18
|
+
prepared_result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Raises an error if the operation failed
|
|
22
|
+
# @raise [RdkafkaError]
|
|
23
|
+
def raise_error
|
|
24
|
+
raise RdkafkaError.new(
|
|
25
|
+
self[:response],
|
|
26
|
+
broker_message: broker_message
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Report for describe cluster operation result
|
|
6
|
+
class DescribeClusterReport
|
|
7
|
+
include DescribeResultParsing
|
|
8
|
+
|
|
9
|
+
# @return [String, nil] the cluster id
|
|
10
|
+
attr_reader :cluster_id
|
|
11
|
+
|
|
12
|
+
# The current controller node, as a hash with `:id` [Integer], `:host` [String],
|
|
13
|
+
# `:port` [Integer] and `:rack` [String, nil]
|
|
14
|
+
# @return [Hash, nil]
|
|
15
|
+
attr_reader :controller
|
|
16
|
+
|
|
17
|
+
# Broker nodes of the cluster, each in the same shape as {#controller}
|
|
18
|
+
# @return [Array<Hash>]
|
|
19
|
+
attr_reader :nodes
|
|
20
|
+
|
|
21
|
+
# Operations the client is authorized to perform on the cluster, as
|
|
22
|
+
# `Bindings::RD_KAFKA_ACL_OPERATION_*` codes. `nil` unless requested with
|
|
23
|
+
# `include_authorized_operations: true`.
|
|
24
|
+
# @return [Array<Integer>, nil]
|
|
25
|
+
attr_reader :authorized_operations
|
|
26
|
+
|
|
27
|
+
# @param result_ptr [FFI::Pointer] pointer to the `rd_kafka_DescribeCluster_result_t`
|
|
28
|
+
def initialize(result_ptr)
|
|
29
|
+
@nodes = []
|
|
30
|
+
|
|
31
|
+
return if result_ptr.null?
|
|
32
|
+
|
|
33
|
+
cluster_id_ptr = Bindings.rd_kafka_DescribeCluster_result_cluster_id(result_ptr)
|
|
34
|
+
@cluster_id = cluster_id_ptr.null? ? nil : cluster_id_ptr.read_string
|
|
35
|
+
@controller = extract_node(Bindings.rd_kafka_DescribeCluster_result_controller(result_ptr))
|
|
36
|
+
|
|
37
|
+
count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
38
|
+
@nodes = extract_nodes(Bindings.rd_kafka_DescribeCluster_result_nodes(result_ptr, count_ptr), count_ptr)
|
|
39
|
+
|
|
40
|
+
count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
41
|
+
@authorized_operations = extract_authorized_operations(
|
|
42
|
+
Bindings.rd_kafka_DescribeCluster_result_authorized_operations(result_ptr, count_ptr),
|
|
43
|
+
count_ptr
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Handle for describe consumer groups operation
|
|
6
|
+
class DescribeConsumerGroupsHandle < AbstractHandle
|
|
7
|
+
layout :pending, :bool,
|
|
8
|
+
:response, :int
|
|
9
|
+
|
|
10
|
+
# @return [String] the name of the operation
|
|
11
|
+
def operation_name
|
|
12
|
+
"describe consumer groups"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [DescribeConsumerGroupsReport] report prepared by the background event callback,
|
|
16
|
+
# with the described consumer groups.
|
|
17
|
+
def create_result
|
|
18
|
+
prepared_result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Raises an error if the operation failed
|
|
22
|
+
# @raise [RdkafkaError]
|
|
23
|
+
def raise_error
|
|
24
|
+
raise RdkafkaError.new(
|
|
25
|
+
self[:response],
|
|
26
|
+
broker_message: broker_message
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Report for describe consumer groups operation result
|
|
6
|
+
class DescribeConsumerGroupsReport
|
|
7
|
+
include DescribeResultParsing
|
|
8
|
+
|
|
9
|
+
# Described consumer groups, one per requested group id. Each entry is a hash with:
|
|
10
|
+
# - `:group_id` [String] the consumer group id
|
|
11
|
+
# - `:error` [RdkafkaError, nil] error describing this group, or `nil` on success. The
|
|
12
|
+
# other fields are not meaningful when it is set.
|
|
13
|
+
# - `:is_simple_consumer_group` [Boolean] `true` for a group that assigns partitions
|
|
14
|
+
# manually and uses Kafka only for offset storage
|
|
15
|
+
# - `:state` [Integer] the group state as a `Bindings::RD_KAFKA_CONSUMER_GROUP_STATE_*`
|
|
16
|
+
# code. A group id the cluster does not know is reported as
|
|
17
|
+
# `RD_KAFKA_CONSUMER_GROUP_STATE_DEAD` with no members.
|
|
18
|
+
# - `:state_name` [String] human-readable name of that state (e.g. `"Stable"`, `"Empty"`)
|
|
19
|
+
# - `:type` [Integer] the group protocol type as a
|
|
20
|
+
# `Bindings::RD_KAFKA_CONSUMER_GROUP_TYPE_*` code, one of:
|
|
21
|
+
# `RD_KAFKA_CONSUMER_GROUP_TYPE_CLASSIC`,
|
|
22
|
+
# `RD_KAFKA_CONSUMER_GROUP_TYPE_CONSUMER` (KIP-848),
|
|
23
|
+
# `RD_KAFKA_CONSUMER_GROUP_TYPE_UNKNOWN`
|
|
24
|
+
# - `:type_name` [String] human-readable name of that type (e.g. `"Classic"`, `"Consumer"`)
|
|
25
|
+
# - `:partition_assignor` [String, nil] partition assignor in use (e.g. `"range"`)
|
|
26
|
+
# - `:coordinator` [Hash, nil] the group coordinator broker as
|
|
27
|
+
# `{ id:, host:, port:, rack: }`
|
|
28
|
+
# - `:authorized_operations` [Array<Integer>, nil] ACL operations the client may perform on
|
|
29
|
+
# the group, as `Bindings::RD_KAFKA_ACL_OPERATION_*` codes. `nil` unless requested with
|
|
30
|
+
# `include_authorized_operations: true`.
|
|
31
|
+
# - `:members` [Array<Hash>] current group members, each with:
|
|
32
|
+
# - `:member_id` [String] the member (consumer) id
|
|
33
|
+
# - `:client_id` [String] the member's `client.id`
|
|
34
|
+
# - `:group_instance_id` [String, nil] the static membership instance id, if any
|
|
35
|
+
# - `:host` [String] the host the member connects from
|
|
36
|
+
# - `:assignment` [Hash{String => Array<Integer>}] assigned partitions, by topic
|
|
37
|
+
# - `:target_assignment` [Hash{String => Array<Integer>}, nil] partitions the member is
|
|
38
|
+
# moving to during a rebalance, by topic. Only `consumer` (KIP-848) groups have it;
|
|
39
|
+
# `nil` for `classic` groups.
|
|
40
|
+
# @return [Array<Hash>]
|
|
41
|
+
attr_reader :groups
|
|
42
|
+
|
|
43
|
+
# @param result_ptr [FFI::Pointer] pointer to the `rd_kafka_DescribeConsumerGroups_result_t`
|
|
44
|
+
def initialize(result_ptr)
|
|
45
|
+
@groups = []
|
|
46
|
+
|
|
47
|
+
return if result_ptr.null?
|
|
48
|
+
|
|
49
|
+
count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
50
|
+
array_ptr = Bindings.rd_kafka_DescribeConsumerGroups_result_groups(result_ptr, count_ptr)
|
|
51
|
+
|
|
52
|
+
return if array_ptr.null?
|
|
53
|
+
|
|
54
|
+
array_ptr.read_array_of_pointer(count_ptr.read(:size_t)).each do |group_ptr|
|
|
55
|
+
@groups << build_group(group_ptr)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# @param group_ptr [FFI::Pointer] pointer to the `rd_kafka_ConsumerGroupDescription_t`
|
|
62
|
+
# @return [Hash]
|
|
63
|
+
def build_group(group_ptr)
|
|
64
|
+
state = Bindings.rd_kafka_ConsumerGroupDescription_state(group_ptr)
|
|
65
|
+
type = Bindings.rd_kafka_ConsumerGroupDescription_type(group_ptr)
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
group_id: read_string(Bindings.rd_kafka_ConsumerGroupDescription_group_id(group_ptr)),
|
|
69
|
+
error: build_error(Bindings.rd_kafka_ConsumerGroupDescription_error(group_ptr)),
|
|
70
|
+
is_simple_consumer_group:
|
|
71
|
+
Bindings.rd_kafka_ConsumerGroupDescription_is_simple_consumer_group(group_ptr) != 0,
|
|
72
|
+
state: state,
|
|
73
|
+
state_name: read_string(Bindings.rd_kafka_consumer_group_state_name(state)),
|
|
74
|
+
type: type,
|
|
75
|
+
type_name: read_string(Bindings.rd_kafka_consumer_group_type_name(type)),
|
|
76
|
+
partition_assignor:
|
|
77
|
+
read_string(Bindings.rd_kafka_ConsumerGroupDescription_partition_assignor(group_ptr)),
|
|
78
|
+
coordinator: extract_node(Bindings.rd_kafka_ConsumerGroupDescription_coordinator(group_ptr)),
|
|
79
|
+
authorized_operations: build_authorized_operations(group_ptr),
|
|
80
|
+
members: Array.new(Bindings.rd_kafka_ConsumerGroupDescription_member_count(group_ptr)) do |index|
|
|
81
|
+
build_member(Bindings.rd_kafka_ConsumerGroupDescription_member(group_ptr, index))
|
|
82
|
+
end
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @param member_ptr [FFI::Pointer] pointer to the `rd_kafka_MemberDescription_t`
|
|
87
|
+
# @return [Hash]
|
|
88
|
+
def build_member(member_ptr)
|
|
89
|
+
{
|
|
90
|
+
member_id: read_string(Bindings.rd_kafka_MemberDescription_consumer_id(member_ptr)),
|
|
91
|
+
client_id: read_string(Bindings.rd_kafka_MemberDescription_client_id(member_ptr)),
|
|
92
|
+
group_instance_id: read_string(Bindings.rd_kafka_MemberDescription_group_instance_id(member_ptr)),
|
|
93
|
+
host: read_string(Bindings.rd_kafka_MemberDescription_host(member_ptr)),
|
|
94
|
+
assignment: build_assignment(Bindings.rd_kafka_MemberDescription_assignment(member_ptr)),
|
|
95
|
+
target_assignment: build_target_assignment(member_ptr)
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @param assignment_ptr [FFI::Pointer] pointer to the `rd_kafka_MemberAssignment_t`
|
|
100
|
+
# @return [Hash{String => Array<Integer>}]
|
|
101
|
+
def build_assignment(assignment_ptr)
|
|
102
|
+
return {} if assignment_ptr.null?
|
|
103
|
+
|
|
104
|
+
tpl_ptr = Bindings.rd_kafka_MemberAssignment_partitions(assignment_ptr)
|
|
105
|
+
|
|
106
|
+
return {} if tpl_ptr.null?
|
|
107
|
+
|
|
108
|
+
Consumer::TopicPartitionList
|
|
109
|
+
.from_native_tpl(tpl_ptr)
|
|
110
|
+
.to_h
|
|
111
|
+
.transform_values { |partitions| (partitions || []).map(&:partition) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @param member_ptr [FFI::Pointer] pointer to the `rd_kafka_MemberDescription_t`
|
|
115
|
+
# @return [Hash{String => Array<Integer>}, nil]
|
|
116
|
+
def build_target_assignment(member_ptr)
|
|
117
|
+
assignment_ptr = Bindings.rd_kafka_MemberDescription_target_assignment(member_ptr)
|
|
118
|
+
|
|
119
|
+
assignment_ptr.null? ? nil : build_assignment(assignment_ptr)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @param group_ptr [FFI::Pointer] pointer to the `rd_kafka_ConsumerGroupDescription_t`
|
|
123
|
+
# @return [Array<Integer>, nil]
|
|
124
|
+
def build_authorized_operations(group_ptr)
|
|
125
|
+
count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
126
|
+
|
|
127
|
+
extract_authorized_operations(
|
|
128
|
+
Bindings.rd_kafka_ConsumerGroupDescription_authorized_operations(group_ptr, count_ptr),
|
|
129
|
+
count_ptr
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# @param error_ptr [FFI::Pointer] pointer to the `rd_kafka_error_t`
|
|
134
|
+
# @return [RdkafkaError, nil]
|
|
135
|
+
def build_error(error_ptr)
|
|
136
|
+
return nil if error_ptr.null?
|
|
137
|
+
|
|
138
|
+
RdkafkaError.new(
|
|
139
|
+
Bindings.rd_kafka_error_code(error_ptr),
|
|
140
|
+
broker_message: read_string(Bindings.rd_kafka_error_string(error_ptr))
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# @param ptr [FFI::Pointer] pointer to a C string
|
|
145
|
+
# @return [String, nil]
|
|
146
|
+
def read_string(ptr)
|
|
147
|
+
ptr.null? ? nil : ptr.read_string
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Helpers shared by the describe-topics and describe-cluster reports to copy nodes and
|
|
6
|
+
# authorized operations out of event-owned memory.
|
|
7
|
+
# @private
|
|
8
|
+
module DescribeResultParsing
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# @param node_ptr [FFI::Pointer] pointer to a `rd_kafka_Node_t`
|
|
12
|
+
# @return [Hash, nil] node with `:id`, `:host`, `:port` and `:rack` or nil when NULL
|
|
13
|
+
def extract_node(node_ptr)
|
|
14
|
+
return nil if node_ptr.null?
|
|
15
|
+
|
|
16
|
+
host_ptr = Bindings.rd_kafka_Node_host(node_ptr)
|
|
17
|
+
rack_ptr = Bindings.rd_kafka_Node_rack(node_ptr)
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
id: Bindings.rd_kafka_Node_id(node_ptr),
|
|
21
|
+
host: host_ptr.null? ? nil : host_ptr.read_string,
|
|
22
|
+
port: Bindings.rd_kafka_Node_port(node_ptr),
|
|
23
|
+
rack: rack_ptr.null? ? nil : rack_ptr.read_string
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param array_ptr [FFI::Pointer] pointer to a `rd_kafka_Node_t*` array
|
|
28
|
+
# @param count_ptr [FFI::MemoryPointer] size_t holding the array length
|
|
29
|
+
# @return [Array<Hash>] nodes as returned by {#extract_node}
|
|
30
|
+
def extract_nodes(array_ptr, count_ptr)
|
|
31
|
+
return [] if array_ptr.null?
|
|
32
|
+
|
|
33
|
+
array_ptr.read_array_of_pointer(count_ptr.read(:size_t)).map { |node_ptr| extract_node(node_ptr) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param array_ptr [FFI::Pointer] pointer to a `rd_kafka_AclOperation_t` array
|
|
37
|
+
# @param count_ptr [FFI::MemoryPointer] size_t holding the array length
|
|
38
|
+
# @return [Array<Integer>, nil] `Bindings::RD_KAFKA_ACL_OPERATION_*` codes or nil when the
|
|
39
|
+
# authorized operations were not requested
|
|
40
|
+
def extract_authorized_operations(array_ptr, count_ptr)
|
|
41
|
+
return nil if array_ptr.null?
|
|
42
|
+
|
|
43
|
+
array_ptr.read_array_of_int(count_ptr.read(:size_t))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|