Class: Karafka::Admin
- Inherits:
-
Object
- Object
- Karafka::Admin
- Extended by:
- Core::Helpers::Time
- Defined in:
- lib/karafka/admin.rb,
lib/karafka/admin/acl.rb,
lib/karafka/admin/topics.rb,
lib/karafka/admin/configs.rb,
lib/karafka/admin/replication.rb,
lib/karafka/admin/configs/config.rb,
lib/karafka/admin/consumer_groups.rb,
lib/karafka/admin/configs/resource.rb,
lib/karafka/admin/isolation_levels.rb,
lib/karafka/admin/contracts/replication.rb
Overview
Each operation initializes its own dedicated instance as we want to ensure it is always closed. Since admin actions are not performed that often, that should be ok. When an external client is provided, it is reused instead and its lifecycle stays fully with its owner.
By default it uses the primary defined cluster. For multi-cluster operations, create
an Admin instance with custom kafka configuration:
Karafka::Admin.new(kafka: { 'bootstrap.servers': 'other:9092' })
Admin actions that we can perform via Karafka on our Kafka cluster
Direct Known Subclasses
Defined Under Namespace
Modules: Contracts, IsolationLevels Classes: Acl, Configs, ConsumerGroups, Replication, Topics
Constant Summary collapse
- Recovery =
We alias this for Pro users so we don't end up having two Admin namespaces from the end user perspective. This enhances the UX.
Karafka::Pro::Admin::Recovery
Class Method Summary collapse
-
.cluster_info ⇒ Rdkafka::Metadata
Cluster metadata info.
-
.copy_consumer_group(previous_name, new_name, topics) ⇒ Boolean
Takes consumer group and its topics and copies all the offsets to a new named group.
- .create_partitions(name, partitions) ⇒ Object
- .create_topic(name, partitions, replication_factor, topic_config = {}) ⇒ Object
-
.delete_consumer_group(group_id) ⇒ Object
Removes given group (if exists).
- .delete_topic(name) ⇒ Object
-
.plan_topic_replication(topic:, replication_factor:, brokers: nil) ⇒ Replication
Plans a replication factor increase for a topic that can be used with Kafka's reassignment tools.
-
.read_lags_with_offsets(groups_with_topics = {}, active_topics_only: true) ⇒ Hash{String => Hash{Integer => Hash{Integer => Object}}}
Reads lags and offsets for given topics in the context of groups defined in the routing.
- .read_partition_offsets(topic_partition_offsets, isolation_level: nil) ⇒ Object
- .read_topic(name, partition, count, start_offset = -1,, settings = {}) ⇒ Object
- .read_watermark_offsets(name_or_hash, partition = nil) ⇒ Object
-
.rename_consumer_group(previous_name, new_name, topics, delete_previous: true) ⇒ Boolean
Takes consumer group and its topics and migrates all the offsets to a new named group.
- .seek_consumer_group(group_id, topics_with_partitions_and_offsets) ⇒ Object
- .topic_info(topic_name) ⇒ Object
-
.trigger_rebalance(group_id) ⇒ Object
Triggers a rebalance for the specified group.
-
.with_admin ⇒ Object
Creates admin instance and yields it.
-
.with_consumer(settings = {}) ⇒ Object
Creates consumer instance and yields it.
Instance Method Summary collapse
-
#close ⇒ Object
No-op close to normalize the API surface.
-
#cluster_info ⇒ Rdkafka::Metadata
Cluster metadata info.
- #copy_consumer_group(previous_name, new_name, topics) ⇒ Object
- #create_partitions(name, partitions) ⇒ Object
- #create_topic(name, partitions, replication_factor, topic_config = {}) ⇒ Object
- #delete_consumer_group(group_id) ⇒ Object
- #delete_topic(name) ⇒ Object
-
#initialize(kafka: {}, external_client: nil) ⇒ Admin
constructor
Creates a new Admin instance.
- #plan_topic_replication(topic:, replication_factor:, brokers: nil) ⇒ Object
- #read_lags_with_offsets(groups_with_topics = {}, active_topics_only: true) ⇒ Object
- #read_partition_offsets(topic_partition_offsets, isolation_level: nil) ⇒ Object
- #read_topic(name, partition, count, start_offset = -1,, settings = {}) ⇒ Object
- #read_watermark_offsets(name_or_hash, partition = nil) ⇒ Object
- #rename_consumer_group(previous_name, new_name, topics, delete_previous: true) ⇒ Object
- #seek_consumer_group(group_id, topics_with_partitions_and_offsets) ⇒ Object
- #topic_info(topic_name) ⇒ Object
- #trigger_rebalance(group_id) ⇒ Object
-
#with_admin ⇒ Object
Creates admin instance and yields it.
-
#with_consumer(settings = {}) ⇒ Object
Creates consumer instance and yields it.
Constructor Details
#initialize(kafka: {}, external_client: nil) ⇒ Admin
Raw and proxied rdkafka instances are resolved once at construction, so the admin
instance should not outlive them. Karafka::Connection::Client instances are resolved
on each use instead, so the admin instance follows such a client across the underlying
connection recovery resets.
Creates a new Admin instance
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/karafka/admin.rb', line 55 def initialize(kafka: {}, external_client: nil) @custom_kafka = kafka @external_client = nil @external_admin = false @external_consumer = false return unless external_client # A running consumer connection client is kept as-is and its current handle is resolved # on each use: such clients swap their underlying rdkafka instance across recovery # resets, so a handle resolved once at construction could go stale while the client # itself remains fully operational if external_client.is_a?(Karafka::Connection::Client) @external_client = external_client @external_consumer = true return end # Raw and proxied rdkafka instances are wrapped upfront - the proxy itself prevents # double wrapping @external_client = Karafka::Connection::Proxy.new(external_client) # Capability based routing resolved once upfront: rdkafka admin instances serve # admin-based operations, any other external client is assumed consumer capable if @external_client.wrapped.is_a?(Rdkafka::Admin) @external_admin = true else @external_consumer = true end end |
Class Method Details
.cluster_info ⇒ Rdkafka::Metadata
Returns cluster metadata info.
278 279 280 |
# File 'lib/karafka/admin.rb', line 278 def cluster_info new.cluster_info end |
.copy_consumer_group(previous_name, new_name, topics) ⇒ Boolean
Takes consumer group and its topics and copies all the offsets to a new named group
174 175 176 |
# File 'lib/karafka/admin.rb', line 174 def copy_consumer_group(previous_name, new_name, topics) new.copy_consumer_group(previous_name, new_name, topics) end |
.create_partitions(name, partitions) ⇒ Object
135 136 137 |
# File 'lib/karafka/admin.rb', line 135 def create_partitions(name, partitions) new.create_partitions(name, partitions) end |
.create_topic(name, partitions, replication_factor, topic_config = {}) ⇒ Object
122 123 124 |
# File 'lib/karafka/admin.rb', line 122 def create_topic(name, partitions, replication_factor, topic_config = {}) new.create_topic(name, partitions, replication_factor, topic_config) end |
.delete_consumer_group(group_id) ⇒ Object
Removes given group (if exists)
201 202 203 |
# File 'lib/karafka/admin.rb', line 201 def delete_consumer_group(group_id) new.delete_consumer_group(group_id) end |
.delete_topic(name) ⇒ Object
128 129 130 |
# File 'lib/karafka/admin.rb', line 128 def delete_topic(name) new.delete_topic(name) end |
.plan_topic_replication(topic:, replication_factor:, brokers: nil) ⇒ Replication
Plans a replication factor increase for a topic that can be used with Kafka's reassignment tools. Since librdkafka does not support increasing replication factor directly, this method generates the necessary JSON and commands for manual execution.
269 270 271 272 273 274 275 |
# File 'lib/karafka/admin.rb', line 269 def plan_topic_replication(topic:, replication_factor:, brokers: nil) new.plan_topic_replication( topic: topic, replication_factor: replication_factor, brokers: brokers ) end |
.read_lags_with_offsets(groups_with_topics = {}, active_topics_only: true) ⇒ Hash{String => Hash{Integer => Hash{Integer => Object}}}
Reads lags and offsets for given topics in the context of groups defined in the routing
223 224 225 226 227 228 |
# File 'lib/karafka/admin.rb', line 223 def read_lags_with_offsets(groups_with_topics = {}, active_topics_only: true) new.read_lags_with_offsets( groups_with_topics, active_topics_only: active_topics_only ) end |
.read_partition_offsets(topic_partition_offsets, isolation_level: nil) ⇒ Object
149 150 151 |
# File 'lib/karafka/admin.rb', line 149 def read_partition_offsets(topic_partition_offsets, isolation_level: nil) new.read_partition_offsets(topic_partition_offsets, isolation_level: isolation_level) end |
.read_topic(name, partition, count, start_offset = -1,, settings = {}) ⇒ Object
113 114 115 |
# File 'lib/karafka/admin.rb', line 113 def read_topic(name, partition, count, start_offset = -1, settings = {}) new.read_topic(name, partition, count, start_offset, settings) end |
.read_watermark_offsets(name_or_hash, partition = nil) ⇒ Object
142 143 144 |
# File 'lib/karafka/admin.rb', line 142 def read_watermark_offsets(name_or_hash, partition = nil) new.read_watermark_offsets(name_or_hash, partition) end |
.rename_consumer_group(previous_name, new_name, topics, delete_previous: true) ⇒ Boolean
Takes consumer group and its topics and migrates all the offsets to a new named group
188 189 190 191 192 193 194 195 |
# File 'lib/karafka/admin.rb', line 188 def rename_consumer_group(previous_name, new_name, topics, delete_previous: true) new.rename_consumer_group( previous_name, new_name, topics, delete_previous: delete_previous ) end |
.seek_consumer_group(group_id, topics_with_partitions_and_offsets) ⇒ Object
163 164 165 |
# File 'lib/karafka/admin.rb', line 163 def seek_consumer_group(group_id, topics_with_partitions_and_offsets) new.seek_consumer_group(group_id, topics_with_partitions_and_offsets) end |
.topic_info(topic_name) ⇒ Object
155 156 157 |
# File 'lib/karafka/admin.rb', line 155 def topic_info(topic_name) new.topic_info(topic_name) end |
.trigger_rebalance(group_id) ⇒ Object
This API should be used only for development.
Triggers a rebalance for the specified group
210 211 212 |
# File 'lib/karafka/admin.rb', line 210 def trigger_rebalance(group_id) new.trigger_rebalance(group_id) end |
.with_admin ⇒ Object
Creates admin instance and yields it. After usage it closes the admin instance
294 295 296 |
# File 'lib/karafka/admin.rb', line 294 def with_admin(&) new.with_admin(&) end |
.with_consumer(settings = {}) ⇒ Object
We always ship and yield a proxied consumer because admin API performance is not that relevant. That is, there are no high frequency calls that would have to be delegated
Creates consumer instance and yields it. After usage it closes the consumer instance This API can be used in other pieces of code and allows for low-level consumer usage
289 290 291 |
# File 'lib/karafka/admin.rb', line 289 def with_consumer(settings = {}, &) new.with_consumer(settings, &) end |
Instance Method Details
#close ⇒ Object
No-op close to normalize the API surface.
Each admin operation opens and closes its own dedicated rdkafka instance internally,
while external clients are owned and closed by their providers, so there is nothing to
release at the Karafka::Admin level right now. This method exists so that callers who
hold an instance and call #close on it (matching the pattern of other closeable
resources) do not raise NoMethodError.
In the future, Karafka::Admin is planned to be refactored to reuse a single owned
rdkafka admin instance across multiple operations rather than creating and tearing one
down per call. When that happens, this method will need to release that shared owned
instance - though never an external client. The no-op is here now so that all callers are
already written against the correct API and require no changes when the real
implementation lands.
101 102 |
# File 'lib/karafka/admin.rb', line 101 def close end |
#cluster_info ⇒ Rdkafka::Metadata
Returns cluster metadata info.
421 422 423 |
# File 'lib/karafka/admin.rb', line 421 def cluster_info with_admin(&:metadata) end |
#copy_consumer_group(previous_name, new_name, topics) ⇒ Object
367 368 369 |
# File 'lib/karafka/admin.rb', line 367 def copy_consumer_group(previous_name, new_name, topics) consumer_groups_admin.copy(previous_name, new_name, topics) end |
#create_partitions(name, partitions) ⇒ Object
329 330 331 |
# File 'lib/karafka/admin.rb', line 329 def create_partitions(name, partitions) topics_admin.create_partitions(name, partitions) end |
#create_topic(name, partitions, replication_factor, topic_config = {}) ⇒ Object
316 317 318 |
# File 'lib/karafka/admin.rb', line 316 def create_topic(name, partitions, replication_factor, topic_config = {}) topics_admin.create(name, partitions, replication_factor, topic_config) end |
#delete_consumer_group(group_id) ⇒ Object
387 388 389 |
# File 'lib/karafka/admin.rb', line 387 def delete_consumer_group(group_id) consumer_groups_admin.delete(group_id) end |
#delete_topic(name) ⇒ Object
322 323 324 |
# File 'lib/karafka/admin.rb', line 322 def delete_topic(name) topics_admin.delete(name) end |
#plan_topic_replication(topic:, replication_factor:, brokers: nil) ⇒ Object
412 413 414 415 416 417 418 |
# File 'lib/karafka/admin.rb', line 412 def plan_topic_replication(topic:, replication_factor:, brokers: nil) Replication.new(kafka: @custom_kafka, external_client: @external_client).plan( topic: topic, to: replication_factor, brokers: brokers ) end |
#read_lags_with_offsets(groups_with_topics = {}, active_topics_only: true) ⇒ Object
401 402 403 404 405 406 |
# File 'lib/karafka/admin.rb', line 401 def read_lags_with_offsets(groups_with_topics = {}, active_topics_only: true) consumer_groups_admin.read_lags_with_offsets( groups_with_topics, active_topics_only: active_topics_only ) end |
#read_partition_offsets(topic_partition_offsets, isolation_level: nil) ⇒ Object
343 344 345 |
# File 'lib/karafka/admin.rb', line 343 def read_partition_offsets(topic_partition_offsets, isolation_level: nil) topics_admin.read_partition_offsets(topic_partition_offsets, isolation_level: isolation_level) end |
#read_topic(name, partition, count, start_offset = -1,, settings = {}) ⇒ Object
307 308 309 |
# File 'lib/karafka/admin.rb', line 307 def read_topic(name, partition, count, start_offset = -1, settings = {}) topics_admin.read(name, partition, count, start_offset, settings) end |
#read_watermark_offsets(name_or_hash, partition = nil) ⇒ Object
336 337 338 |
# File 'lib/karafka/admin.rb', line 336 def read_watermark_offsets(name_or_hash, partition = nil) topics_admin.read_watermark_offsets(name_or_hash, partition) end |
#rename_consumer_group(previous_name, new_name, topics, delete_previous: true) ⇒ Object
376 377 378 379 380 381 382 383 |
# File 'lib/karafka/admin.rb', line 376 def rename_consumer_group(previous_name, new_name, topics, delete_previous: true) consumer_groups_admin.rename( previous_name, new_name, topics, delete_previous: delete_previous ) end |
#seek_consumer_group(group_id, topics_with_partitions_and_offsets) ⇒ Object
356 357 358 359 360 361 |
# File 'lib/karafka/admin.rb', line 356 def seek_consumer_group(group_id, topics_with_partitions_and_offsets) consumer_groups_admin.seek( group_id, topics_with_partitions_and_offsets ) end |
#topic_info(topic_name) ⇒ Object
349 350 351 |
# File 'lib/karafka/admin.rb', line 349 def topic_info(topic_name) topics_admin.info(topic_name) end |
#trigger_rebalance(group_id) ⇒ Object
393 394 395 |
# File 'lib/karafka/admin.rb', line 393 def trigger_rebalance(group_id) consumer_groups_admin.trigger_rebalance(group_id) end |
#with_admin ⇒ Object
When the external client is an rdkafka admin instance, it is yielded wrapped with
Karafka::Connection::Proxy (which unwraps pre-proxied clients, so no double wrapping
occurs) and its lifecycle is not managed here in any way, same as with #with_consumer.
External
clients of other types (consumers, producers) are not capable of admin operations, thus
a dedicated admin instance is created for them as usual.
Creates admin instance and yields it. After usage it closes the admin instance
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 |
# File 'lib/karafka/admin.rb', line 478 def with_admin return yield(external_client_proxy) if @external_admin bind_id = SecureRandom.uuid admin = nil begin admin = config(:producer, {}).admin( native_kafka_auto_start: false, native_kafka_poll_timeout_ms: self.class.poll_timeout ) bind_oauth(bind_id, admin) admin.start proxy = Karafka::Connection::Proxy.new(admin) yield(proxy) ensure admin&.close unbind_oauth(bind_id) end end |
#with_consumer(settings = {}) ⇒ Object
We always ship and yield a proxied consumer because admin API performance is not that relevant. That is, there are no high frequency calls that would have to be delegated
When an external client is present, it is yielded wrapped with
Karafka::Connection::Proxy (which unwraps pre-proxied clients, so no double wrapping
occurs) and its lifecycle is not managed here in any way: no oauth binding, no start and
no closing. settings are
ignored as the external instance is already configured and operations run within its
identity, including its group.id. External rdkafka admin instances are not used here
as they are not capable of consumer operations - they are used by #with_admin instead.
Creates consumer instance and yields it. After usage it closes the consumer instance This API can be used in other pieces of code and allows for low-level consumer usage
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/karafka/admin.rb', line 440 def with_consumer(settings = {}) return yield(external_client_proxy) if @external_consumer bind_id = SecureRandom.uuid consumer = nil begin consumer = config(:consumer, settings).consumer(native_kafka_auto_start: false) bind_oauth(bind_id, consumer) consumer.start proxy = Karafka::Connection::Proxy.new(consumer) yield(proxy) ensure # Always unsubscribe consumer just to be sure, that no metadata requests are running # when we close the consumer. This in theory should prevent from some race-conditions # that originate from librdkafka begin consumer&.unsubscribe # Ignore any errors and continue to close consumer despite them rescue Rdkafka::RdkafkaError nil end consumer&.close unbind_oauth(bind_id) end end |