karafka-rdkafka 0.30.1-aarch64-linux-gnu → 0.31.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 +46 -27
- data/README.md +1 -0
- data/docker-compose-ssl.yml +1 -0
- data/docker-compose.yml +1 -0
- data/ext/librdkafka.so +0 -0
- data/karafka-rdkafka.gemspec +1 -1
- 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 +245 -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 +148 -0
- data/lib/rdkafka/bindings/configuration.rb +50 -0
- data/lib/rdkafka/bindings/consumer.rb +83 -0
- data/lib/rdkafka/bindings/errors.rb +17 -0
- data/lib/rdkafka/bindings/handle.rb +16 -0
- data/lib/rdkafka/bindings/messages.rb +28 -0
- data/lib/rdkafka/bindings/metadata.rb +17 -0
- data/lib/rdkafka/bindings/polling.rb +17 -0
- data/lib/rdkafka/bindings/producer.rb +82 -0
- data/lib/rdkafka/bindings/share_consumer.rb +67 -0
- data/lib/rdkafka/bindings/structs.rb +62 -0
- data/lib/rdkafka/bindings.rb +13 -775
- 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 +10 -1
- data/lib/rdkafka/clients.rb +13 -46
- data/lib/rdkafka/consumer/message.rb +6 -9
- data/lib/rdkafka/defaults.rb +4 -0
- data/lib/rdkafka/producer/delivery_handle.rb +18 -16
- data/lib/rdkafka/producer/delivery_report.rb +51 -1
- data/lib/rdkafka/share_consumer.rb +54 -3
- data/lib/rdkafka/version.rb +3 -3
- data/lib/rdkafka.rb +10 -0
- data/package-lock.json +9 -9
- data/renovate.json +3 -0
- metadata +27 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 31a31d86d0d0ee5888adc297e5bfc9338f18b453b4834095fd4159f6562458ad
|
|
4
|
+
data.tar.gz: 1da9da2a1268fb3a9256475dd5cb7e8cccaf8dbaaa061436ae4a1174ba1f3f44
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 70d99fbf5bf5d225f51b461266c8aac8a4f21e7d5ea860ffd17aa94429ec44bfb52e84e7d770e37b9297218e38147610ecdac24181d93a8c6910c4e791e8a52c
|
|
7
|
+
data.tar.gz: 52271992a6802a31a8032264d9a7b61b8f890cdfc479c22402ea9c59c22193f77e5f637318a1701dea71e9c442d03162c1401b3383331d3702b4676f7f978a01
|
data/CHANGELOG.md
CHANGED
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
# Rdkafka Changelog
|
|
2
2
|
|
|
3
|
+
## 0.31.0 (2026-10-12)
|
|
4
|
+
- [Feature] Add `Admin#describe_cluster` to get the cluster id, controller and broker nodes (with racks) without a full metadata request. Ported from rdkafka-ruby (#1015).
|
|
5
|
+
- [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. Ported from rdkafka-ruby (#1015).
|
|
6
|
+
- [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. Ported from rdkafka-ruby (#1016).
|
|
7
|
+
- [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. Ported from rdkafka-ruby (#1019).
|
|
8
|
+
- [Enhancement] Bump librdkafka to `2.16.0` for OpenSSL and libcurl security fixes. Admin requests in flight on a decommissioned broker now fail with a retryable transport error. Ported from rdkafka-ruby (#1020).
|
|
9
|
+
- [Enhancement] Bump the bundled OpenSSL to `3.5.9` (LTS) for its security fixes. Ported from rdkafka-ruby (#1007).
|
|
10
|
+
- [Enhancement] Read each pointer field once in `Message#initialize`, saving 3 allocations per consumed message. Ported from rdkafka-ruby (#1026).
|
|
11
|
+
- [Change] Drop Ruby 3.2 support; require Ruby 3.3+.
|
|
12
|
+
- [Change] Build the precompiled `aarch64-linux-musl` gem on Alpine `3.24` (was `3.23`), matching `x86_64-linux-musl`. Ported from rdkafka-ruby (#1012).
|
|
13
|
+
- [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. Ported from rdkafka-ruby (#1013).
|
|
14
|
+
- [Maintenance] Split `lib/rdkafka/bindings.rb` into per-area files under `lib/rdkafka/bindings/`. `Rdkafka::Bindings` is unchanged. Ported from rdkafka-ruby (#1022).
|
|
15
|
+
- [Maintenance] Stabilize the flaky partitions count cache "multiple topics with different TTLs" spec by driving its clock instead of sleeping. Ported from rdkafka-ruby (#1028).
|
|
16
|
+
- [Fix] Sort merged share-group async acknowledgements so the broker does not reject them with `invalid_request` (librdkafka patch).
|
|
17
|
+
- [Fix] Release share records acquired by an in-flight fetch on unsubscribe instead of keeping them locked (librdkafka patch). `ShareConsumer#unsubscribe` now closes the share sessions like `#close` does and waits for that (up to `fetch.wait.max.ms` with a fetch in flight).
|
|
18
|
+
|
|
19
|
+
## 0.30.2 (2026-10-01)
|
|
20
|
+
- [Feature] Add `ShareConsumer#events_poll` (and `#events_poll_nb`) to service the statistics, error, log and OAuthBearer callbacks without acquiring records.
|
|
21
|
+
|
|
3
22
|
## 0.30.1 (2026-09-30)
|
|
4
|
-
- [Fix]
|
|
23
|
+
- [Fix] Set `ShareConsumer#name` from the native handle at creation, so name-based callback routing (e.g. Karafka) no longer drops share-consumer statistics and errors.
|
|
5
24
|
|
|
6
25
|
## 0.30.0 (2026-09-25)
|
|
7
|
-
- [Fix] Register share consumers in `Rdkafka::Clients` and destroy the native handle when `Config#share_consumer` fails part-way
|
|
8
|
-
- [Feature] Add preview support for KIP-932 share groups ("Queues for Kafka") via `Rdkafka::ShareConsumer
|
|
9
|
-
- [Enhancement] `RdkafkaError.build_from_c` now uses the human
|
|
10
|
-
- [Enhancement]
|
|
11
|
-
- [Fix] Make `ShareConsumer#close` safe
|
|
12
|
-
- [Fix] Keep the `ShareConsumer` native handle and its GC finalizer when `close`/`destroy` report an error
|
|
13
|
-
- [Fix] Pin the acknowledgement commit callback `FFI::Function`
|
|
14
|
-
- [Fix] Make `ShareConsumer` fork-aware
|
|
15
|
-
- [Fix]
|
|
16
|
-
- [Fix] Raise `ArgumentError` for `ShareConsumer#subscribe` without topics
|
|
17
|
-
- [Fix] Capture the librdkafka client name onto `ShareConsumer#name` from the OAuthBearer token refresh callback
|
|
26
|
+
- [Fix] Register share consumers in `Rdkafka::Clients` and destroy the native handle when `Config#share_consumer` fails part-way, so a share consumer is no longer missed by the `at_exit` shutdown hook.
|
|
27
|
+
- [Feature] Add preview support for KIP-932 share groups ("Queues for Kafka") via `Rdkafka::ShareConsumer` (`Config#share_consumer`), with per-record acknowledgements (`:accept`, `:release`, `:reject`). Requires Apache Kafka 4.2.0+ with share groups enabled. Not production-ready.
|
|
28
|
+
- [Enhancement] `RdkafkaError.build_from_c` now uses the human-readable string librdkafka attaches to the `rd_kafka_error_t` instead of `rd_kafka_err2str`, improving diagnostics for every error-pointer path.
|
|
29
|
+
- [Enhancement] Refactor the `Helpers::OAuth` token plumbing around two private hooks so the share consumer overrides only those instead of duplicating both public methods.
|
|
30
|
+
- [Fix] Make `ShareConsumer#close` thread-safe: it now waits for in-flight operations (mirroring `NativeKafka`), preventing a double destroy when two threads race `close`.
|
|
31
|
+
- [Fix] Keep the `ShareConsumer` native handle and its GC finalizer when `close`/`destroy` report an error, so the client is no longer leaked and `close` stays retriable.
|
|
32
|
+
- [Fix] Pin the acknowledgement commit callback `FFI::Function` for the lifetime of the `ShareConsumer`, preventing a use-after-free when an unclosed consumer is garbage collected.
|
|
33
|
+
- [Fix] Make `ShareConsumer` fork-aware (mirroring `NativeKafka`): it reports `closed?` in a forked child, which no longer segfaults on `close` or finalization.
|
|
34
|
+
- [Fix] Rescue `ClosedConsumerError` in `ShareConsumer#each` only around `poll`, so a `ClosedConsumerError` raised inside the block propagates instead of silently ending iteration. Like `#poll`, `#each` can also yield an `RdkafkaError` for a record that fails to build.
|
|
35
|
+
- [Fix] Raise `ArgumentError` for `ShareConsumer#subscribe` without topics, which librdkafka otherwise treats as an unsubscribe without error, silently dropping the subscription.
|
|
36
|
+
- [Fix] Capture the librdkafka client name onto `ShareConsumer#name` from the OAuthBearer token refresh callback, since the share handle has no native name accessor for the documented name-based oauth callback routing.
|
|
18
37
|
- [Fix] Update `ext/build_common.sh` (precompiled builds) to librdkafka `2.15.0` and its tarball checksum; it still pinned `2.14.1`, whose tarball is no longer vendored.
|
|
19
|
-
- [Fix] Stop the `share_consumer_multi_member` integration spec from failing on
|
|
20
|
-
- [Fix] Apply the same at-least-once tolerance to the `share_consumer_explicit_ack` integration spec
|
|
21
|
-
- [Fix] Add the `nofile` ulimit to `docker-compose-ssl.yml
|
|
22
|
-
- [Fix] Enable KIP-932 share groups on the macOS CI broker, whose KRaft
|
|
23
|
-
- [Enhancement] Add
|
|
24
|
-
- [Fix] Stop the `statistics_unassigned_producer` integration spec from flaking on a metadata-propagation race
|
|
25
|
-
- [Fix] Also stabilize the `statistics_unassigned_producer` integration spec against a delivery wait that outlives its budget
|
|
26
|
-
- [Fix] Stabilize the `consumer_memberid_clusterid_leak` integration spec against RSS measurement noise on Ruby 4.0
|
|
27
|
-
- [Note] Share consumers emit the regular consumer statistics
|
|
38
|
+
- [Fix] Stop the `share_consumer_multi_member` integration spec from failing on an at-least-once redelivery; it now tracks distinct payloads and fails only when a record is never delivered.
|
|
39
|
+
- [Fix] Apply the same at-least-once tolerance to the `share_consumer_explicit_ack` integration spec: require only the released record to be redelivered, forbid the rejected one, and tolerate an accepted record reappearing.
|
|
40
|
+
- [Fix] Add the `nofile` ulimit to `docker-compose-ssl.yml` (mirroring `docker-compose.yml`) so the SSL CI broker does not exhaust file descriptors under the topic-heavy share-consumer suite.
|
|
41
|
+
- [Fix] Enable KIP-932 share groups on the macOS CI broker, whose KRaft config never opted into the share rebalance protocol, so its share-consumer spec no longer receives zero records. `CONFLUENT_VERSION` is bumped to `8.3.0`.
|
|
42
|
+
- [Enhancement] Add `share_consumer_implicit_ack` and `share_consumer_multi_topic` integration specs.
|
|
43
|
+
- [Fix] Stop the `statistics_unassigned_producer` integration spec from flaking on a metadata-propagation race by retrying the produce on `unknown_topic_or_part` and `leader_not_available`.
|
|
44
|
+
- [Fix] Also stabilize the `statistics_unassigned_producer` integration spec against a delivery wait that outlives its budget: it now uses the default 60s handle wait and retries on `WaitTimeoutError`.
|
|
45
|
+
- [Fix] Stabilize the `consumer_memberid_clusterid_leak` integration spec against RSS measurement noise on Ruby 4.0 by compacting the heap before sampling and raising the ceiling to 3 MB.
|
|
46
|
+
- [Note] Share consumers emit the regular consumer statistics through `statistics_callback`. librdkafka 2.15.0 has no share-specific metrics, so the `topics` section has no partition entries.
|
|
28
47
|
- [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. Ported from rdkafka-ruby (#983).
|
|
29
48
|
- [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.
|
|
30
49
|
- [Enhancement] Bump the bundled zlib to `1.3.2`.
|
|
@@ -34,8 +53,8 @@
|
|
|
34
53
|
- [Maintenance] Drop the unused `dist/openssl-3.0.16.tar.gz` build cache.
|
|
35
54
|
- [Fix] Honor the `isolation_level:` argument to `Admin#list_offsets`, which was silently ignored. Ported from rdkafka-ruby (#983).
|
|
36
55
|
- [Fix] Return correct results for every item of a multi-item topic, partition, group or ACL admin request, not just the first. Ported from rdkafka-ruby (#987).
|
|
37
|
-
- [Maintenance] Fix a use-after-free in the multi-item admin integration test that
|
|
38
|
-
- [Maintenance] Stabilize consumer specs on slow CI runners
|
|
56
|
+
- [Maintenance] Fix a use-after-free in the multi-item admin integration test that made it flaky on newer glibc. Ported from rdkafka-ruby (#991).
|
|
57
|
+
- [Maintenance] Stabilize consumer specs on slow CI runners. The assignment wait is tunable via `RDKAFKA_TEST_ASSIGNMENT_TIMEOUT`. Ported from rdkafka-ruby (#990).
|
|
39
58
|
|
|
40
59
|
## 0.29.0 (2026-09-14)
|
|
41
60
|
- [Enhancement] Bump librdkafka to `2.15.0` (staying on `2.15.0` rather than `2.15.1` so users hitting a regression in `2.15.1` have a stable fallback).
|
|
@@ -123,8 +142,8 @@
|
|
|
123
142
|
- [Fix] Prevent cascading test failures in admin specs when a single handle leaks into the registry (from upstream).
|
|
124
143
|
|
|
125
144
|
## 0.24.0 (2026-02-25)
|
|
126
|
-
- **[Feature]** Add `Producer#queue_size` (and `#queue_length` alias) to report the number of messages waiting in the librdkafka output queue.
|
|
127
|
-
- **[Feature]** Add
|
|
145
|
+
- **[Feature]** Add `Producer#queue_size` (and the `#queue_length` alias) to report the number of messages waiting in the librdkafka output queue.
|
|
146
|
+
- **[Feature]** Add `enable_queue_io_events` and `enable_background_queue_io_events` to `Consumer`, `Producer` and `Admin` for fiber schedulers (Falcon, Async) and custom event loops (from upstream).
|
|
128
147
|
- **[Deprecation]** `AbstractHandle#wait` parameter `max_wait_timeout` (seconds) is deprecated in favor of `max_wait_timeout_ms` (milliseconds). The old parameter still works with backwards compatibility but will be removed in v1.0.0.
|
|
129
148
|
- **[Deprecation]** `PartitionsCountCache` constructor parameter `ttl` (seconds) is deprecated in favor of `ttl_ms` (milliseconds). The old parameter still works with backwards compatibility but will be removed in v1.0.0.
|
|
130
149
|
- [Enhancement] Add Ruby 4.0 support.
|
|
@@ -151,7 +170,7 @@
|
|
|
151
170
|
- [Enhancement] Add `RdkafkaError.build_fatal` class method for centralized fatal error construction.
|
|
152
171
|
- [Enhancement] Add comprehensive tests for fatal error handling including unit tests and integration tests.
|
|
153
172
|
- [Enhancement] Add `RD_KAFKA_PARTITION_UA` constant for unassigned partition (-1).
|
|
154
|
-
- [Enhancement] Replace magic numbers with
|
|
173
|
+
- [Enhancement] Replace magic numbers with the `RD_KAFKA_RESP_ERR_NO_ERROR` and `RD_KAFKA_PARTITION_UA` constants.
|
|
155
174
|
- [Enhancement] Add `Rdkafka::Testing` module for testing fatal error scenarios on both producers and consumers.
|
|
156
175
|
- [Deprecated] `RdkafkaError.validate_fatal!` - use `validate!` with `client_ptr` parameter instead.
|
|
157
176
|
|
|
@@ -165,7 +184,7 @@
|
|
|
165
184
|
|
|
166
185
|
## 0.22.1 (2025-10-09)
|
|
167
186
|
- [Enhancement] Optimize header processing to eliminate double hash lookups and method checking overhead.
|
|
168
|
-
- [Enhancement]
|
|
187
|
+
- [Enhancement] Speed up producer header processing (69% faster for nil headers, 41% for empty headers, 12-32% when headers are present).
|
|
169
188
|
|
|
170
189
|
## 0.22.0 (2025-09-26)
|
|
171
190
|
- **[EOL]** Drop support for Ruby 3.1 to move forward with the fiber scheduler work.
|
data/README.md
CHANGED
|
@@ -63,6 +63,7 @@ Contributions should generally be made to the upstream [rdkafka-ruby repository]
|
|
|
63
63
|
|
|
64
64
|
| karafka-rdkafka | librdkafka | patches |
|
|
65
65
|
|-|-|-|
|
|
66
|
+
| 0.31.x (2026-10-12) | 2.16.0 (2026-10-06) | yes |
|
|
66
67
|
| 0.30.x (2026-09-25) | 2.15.1 (2026-09-09) | yes |
|
|
67
68
|
| 0.29.x (2026-09-14) | 2.15.0 (2026-06-30) | yes |
|
|
68
69
|
| 0.28.x (2026-07-12) | 2.14.2 (2026-06-03) | yes |
|
data/docker-compose-ssl.yml
CHANGED
|
@@ -25,6 +25,7 @@ services:
|
|
|
25
25
|
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL
|
|
26
26
|
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://127.0.0.1:9092,SSL://127.0.0.1:9093
|
|
27
27
|
KAFKA_BROKER_ID: 1
|
|
28
|
+
KAFKA_BROKER_RACK: rack-1
|
|
28
29
|
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@127.0.0.1:9094
|
|
29
30
|
ALLOW_PLAINTEXT_LISTENER: 'yes'
|
|
30
31
|
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'false'
|
data/docker-compose.yml
CHANGED
|
@@ -23,6 +23,7 @@ services:
|
|
|
23
23
|
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
|
|
24
24
|
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://127.0.0.1:9092
|
|
25
25
|
KAFKA_BROKER_ID: 1
|
|
26
|
+
KAFKA_BROKER_RACK: rack-1
|
|
26
27
|
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@127.0.0.1:9093
|
|
27
28
|
ALLOW_PLAINTEXT_LISTENER: 'yes'
|
|
28
29
|
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'false'
|
data/ext/librdkafka.so
CHANGED
|
Binary file
|
data/karafka-rdkafka.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |gem|
|
|
|
12
12
|
gem.name = "karafka-rdkafka"
|
|
13
13
|
gem.require_paths = ["lib"]
|
|
14
14
|
gem.version = Rdkafka::VERSION
|
|
15
|
-
gem.required_ruby_version = ">= 3.
|
|
15
|
+
gem.required_ruby_version = ">= 3.3"
|
|
16
16
|
|
|
17
17
|
files = `git ls-files`.split($\)
|
|
18
18
|
files = files.reject do |file|
|
|
@@ -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
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Handle for describe topics operation
|
|
6
|
+
class DescribeTopicsHandle < AbstractHandle
|
|
7
|
+
layout :pending, :bool,
|
|
8
|
+
:response, :int
|
|
9
|
+
|
|
10
|
+
# @return [String] the name of the operation
|
|
11
|
+
def operation_name
|
|
12
|
+
"describe topics"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [DescribeTopicsReport] report prepared by the background event callback, with
|
|
16
|
+
# the described topics.
|
|
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,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class Admin
|
|
5
|
+
# Report for describe topics operation result
|
|
6
|
+
class DescribeTopicsReport
|
|
7
|
+
include DescribeResultParsing
|
|
8
|
+
|
|
9
|
+
# Described topics, one per requested topic name. Each entry is a hash with:
|
|
10
|
+
# - `:name` [String] the topic name
|
|
11
|
+
# - `:topic_id` [String, nil] the topic id (KIP-516), base64 encoded, or `nil` when the
|
|
12
|
+
# topic could not be described
|
|
13
|
+
# - `:is_internal` [Boolean] whether the topic is internal (e.g. `__consumer_offsets`)
|
|
14
|
+
# - `:partitions` [Array<Hash>] each with `:partition` [Integer], `:leader` [Hash, nil] and
|
|
15
|
+
# `:replicas` / `:isr` [Array<Hash>]; nodes have `:id`, `:host`, `:port` and `:rack`
|
|
16
|
+
# - `:authorized_operations` [Array<Integer>, nil] `Bindings::RD_KAFKA_ACL_OPERATION_*`
|
|
17
|
+
# codes, `nil` unless requested with `include_authorized_operations: true`
|
|
18
|
+
# - `:error` [RdkafkaError, nil] per-topic error (e.g. `unknown_topic_or_part`), `nil`
|
|
19
|
+
# on success
|
|
20
|
+
# @return [Array<Hash>]
|
|
21
|
+
attr_reader :topics
|
|
22
|
+
|
|
23
|
+
# @param result_ptr [FFI::Pointer] pointer to the `rd_kafka_DescribeTopics_result_t`
|
|
24
|
+
def initialize(result_ptr)
|
|
25
|
+
@topics = []
|
|
26
|
+
|
|
27
|
+
return if result_ptr.null?
|
|
28
|
+
|
|
29
|
+
count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
30
|
+
array_ptr = Bindings.rd_kafka_DescribeTopics_result_topics(result_ptr, count_ptr)
|
|
31
|
+
|
|
32
|
+
return if array_ptr.null?
|
|
33
|
+
|
|
34
|
+
array_ptr.read_array_of_pointer(count_ptr.read(:size_t)).each do |topic_ptr|
|
|
35
|
+
@topics << extract_topic(topic_ptr)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# @param topic_ptr [FFI::Pointer] pointer to a `rd_kafka_TopicDescription_t`
|
|
42
|
+
# @return [Hash]
|
|
43
|
+
def extract_topic(topic_ptr)
|
|
44
|
+
name_ptr = Bindings.rd_kafka_TopicDescription_name(topic_ptr)
|
|
45
|
+
operations_count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
46
|
+
|
|
47
|
+
{
|
|
48
|
+
name: name_ptr.null? ? nil : name_ptr.read_string,
|
|
49
|
+
topic_id: extract_topic_id(topic_ptr),
|
|
50
|
+
is_internal: Bindings.rd_kafka_TopicDescription_is_internal(topic_ptr) != 0,
|
|
51
|
+
partitions: extract_partitions(topic_ptr),
|
|
52
|
+
authorized_operations: extract_authorized_operations(
|
|
53
|
+
Bindings.rd_kafka_TopicDescription_authorized_operations(topic_ptr, operations_count_ptr),
|
|
54
|
+
operations_count_ptr
|
|
55
|
+
),
|
|
56
|
+
error: extract_error(topic_ptr)
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# librdkafka reports the zero UUID for a topic it could not describe
|
|
61
|
+
# @param topic_ptr [FFI::Pointer] pointer to a `rd_kafka_TopicDescription_t`
|
|
62
|
+
# @return [String, nil]
|
|
63
|
+
def extract_topic_id(topic_ptr)
|
|
64
|
+
uuid_ptr = Bindings.rd_kafka_TopicDescription_topic_id(topic_ptr)
|
|
65
|
+
|
|
66
|
+
return nil if uuid_ptr.null?
|
|
67
|
+
return nil if Bindings.rd_kafka_Uuid_most_significant_bits(uuid_ptr).zero? &&
|
|
68
|
+
Bindings.rd_kafka_Uuid_least_significant_bits(uuid_ptr).zero?
|
|
69
|
+
|
|
70
|
+
string_ptr = Bindings.rd_kafka_Uuid_base64str(uuid_ptr)
|
|
71
|
+
string_ptr.null? ? nil : string_ptr.read_string
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @param topic_ptr [FFI::Pointer] pointer to a `rd_kafka_TopicDescription_t`
|
|
75
|
+
# @return [Array<Hash>]
|
|
76
|
+
def extract_partitions(topic_ptr)
|
|
77
|
+
count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
78
|
+
array_ptr = Bindings.rd_kafka_TopicDescription_partitions(topic_ptr, count_ptr)
|
|
79
|
+
|
|
80
|
+
return [] if array_ptr.null?
|
|
81
|
+
|
|
82
|
+
array_ptr.read_array_of_pointer(count_ptr.read(:size_t)).map do |partition_ptr|
|
|
83
|
+
replicas_count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
84
|
+
isr_count_ptr = FFI::MemoryPointer.new(:size_t)
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
partition: Bindings.rd_kafka_TopicPartitionInfo_partition(partition_ptr),
|
|
88
|
+
leader: extract_node(Bindings.rd_kafka_TopicPartitionInfo_leader(partition_ptr)),
|
|
89
|
+
replicas: extract_nodes(
|
|
90
|
+
Bindings.rd_kafka_TopicPartitionInfo_replicas(partition_ptr, replicas_count_ptr),
|
|
91
|
+
replicas_count_ptr
|
|
92
|
+
),
|
|
93
|
+
isr: extract_nodes(
|
|
94
|
+
Bindings.rd_kafka_TopicPartitionInfo_isr(partition_ptr, isr_count_ptr),
|
|
95
|
+
isr_count_ptr
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The error is owned by the result, so it is copied and not destroyed here.
|
|
102
|
+
# @param topic_ptr [FFI::Pointer] pointer to a `rd_kafka_TopicDescription_t`
|
|
103
|
+
# @return [RdkafkaError, nil]
|
|
104
|
+
def extract_error(topic_ptr)
|
|
105
|
+
error_ptr = Bindings.rd_kafka_TopicDescription_error(topic_ptr)
|
|
106
|
+
|
|
107
|
+
return nil if error_ptr.null?
|
|
108
|
+
|
|
109
|
+
code = Bindings.rd_kafka_error_code(error_ptr)
|
|
110
|
+
|
|
111
|
+
return nil if code == Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
|
|
112
|
+
|
|
113
|
+
string_ptr = Bindings.rd_kafka_error_string(error_ptr)
|
|
114
|
+
RdkafkaError.new(code, broker_message: string_ptr.null? ? nil : string_ptr.read_string)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|