use std::future::Future;
use std::sync::Arc;
use std::time::Duration;
use tokio::runtime::{Builder, Runtime};
use crate::producer::{BufferedProducer, BufferedProducerHandle, Producer, ProducerDelivery};
use crate::streams::{
StreamsGroupConfig, StreamsGroupHeartbeatResponseV0, StreamsGroupHeartbeatTask,
StreamsGroupHeartbeatTaskOffset, StreamsGroupSession, StreamsGroupSessionAssignment,
};
use crate::{
AdminClient, AlterConfigsOptions, AlterConfigsResult, AlterConsumerGroupOffsetsResult,
ClientConfig, ClusterDescription, Consumer, ConsumerAssignment, ConsumerConfig, ConsumerGroup,
ConsumerGroupConfig, ConsumerGroupDescription, ConsumerGroupMetadata, ConsumerGroupOffset,
ConsumerGroupOffsetQuery, ConsumerGroupProtocol, ConsumerRecord, CreatePartitionsOptions,
CreatePartitionsResult, CreateTopicsOptions, CreateTopicsResult, DeleteConsumerGroupResult,
DeleteRecordsOptions, DeleteRecordsResult, DeleteTopicsOptions, DeleteTopicsResult,
DescribeConfigsOptions, DescribeConfigsResult, Error, FeatureMetadata, GroupListing,
LeaderEpochOffset, ListConsumerGroupOffsetsResult, ListGroupsOptions,
ModernConsumerGroupDescription, NewPartitions, PartitionWatermarks, ProducerBatchReport,
ProducerConfig, ProducerRecord, RecordMetadata, Result, ShareAcknowledgementType,
ShareConsumer, ShareConsumerConfig, ShareRecord, TopicConfigAlteration, TopicConfigResource,
TopicConfigUpdate, TopicListing, TransactionStatus, UpdateFeaturesOptions,
UpdateFeaturesResult,
};
const NESTED_RUNTIME_MESSAGE: &str =
"blocking kafrust clients cannot run inside a Tokio runtime; use the async API instead";
fn build_runtime() -> Result<Runtime> {
if tokio::runtime::Handle::try_current().is_ok() {
return Err(Error::Unsupported(NESTED_RUNTIME_MESSAGE));
}
Builder::new_multi_thread()
.worker_threads(2)
.enable_all()
.build()
.map_err(Error::Io)
}
fn block_on<T>(runtime: &Runtime, future: impl Future<Output = Result<T>>) -> Result<T> {
if tokio::runtime::Handle::try_current().is_ok() {
return Err(Error::Unsupported(NESTED_RUNTIME_MESSAGE));
}
runtime.block_on(future)
}
pub struct BlockingAdminClient {
admin: AdminClient,
runtime: Runtime,
}
impl BlockingAdminClient {
pub fn build(config: ClientConfig) -> Result<Self> {
let runtime = build_runtime()?;
let admin = AdminClient::new(config).build_config()?;
Ok(Self { admin, runtime })
}
pub fn describe_cluster_with_options(
&self,
options: crate::DescribeClusterOptions,
) -> Result<ClusterDescription> {
block_on(
&self.runtime,
self.admin.describe_cluster_with_options(options),
)
}
pub fn add_raft_voter(
&self,
options: crate::AddRaftVoterOptions,
) -> Result<crate::RaftVoterResult> {
block_on(&self.runtime, self.admin.add_raft_voter(options))
}
pub fn remove_raft_voter(
&self,
options: crate::RemoveRaftVoterOptions,
) -> Result<crate::RaftVoterResult> {
block_on(&self.runtime, self.admin.remove_raft_voter(options))
}
pub fn unregister_broker(&self, broker_id: i32) -> Result<crate::UnregisterBrokerResult> {
block_on(&self.runtime, self.admin.unregister_broker(broker_id))
}
pub fn describe_topic_partitions(
&self,
topics: &[String],
options: crate::DescribeTopicPartitionsOptions,
) -> Result<crate::DescribeTopicPartitionsResult> {
block_on(
&self.runtime,
self.admin.describe_topic_partitions(topics, options),
)
}
pub fn describe_quorum(
&self,
topics: &[crate::DescribeQuorumTopic],
) -> Result<crate::DescribeQuorumResult> {
block_on(&self.runtime, self.admin.describe_quorum(topics))
}
pub fn describe_acls(&self, filter: &crate::AclFilter) -> Result<crate::DescribeAclsResult> {
block_on(&self.runtime, self.admin.describe_acls(filter))
}
pub fn create_acls(&self, bindings: &[crate::AclBinding]) -> Result<crate::CreateAclsResult> {
block_on(&self.runtime, self.admin.create_acls(bindings))
}
pub fn delete_acls(&self, filters: &[crate::AclFilter]) -> Result<crate::DeleteAclsResult> {
block_on(&self.runtime, self.admin.delete_acls(filters))
}
pub fn describe_client_quotas(
&self,
filter: &crate::ClientQuotaFilter,
) -> Result<crate::DescribeClientQuotasResult> {
block_on(&self.runtime, self.admin.describe_client_quotas(filter))
}
pub fn alter_client_quotas(
&self,
alterations: &[crate::ClientQuotaAlteration],
validate_only: bool,
) -> Result<crate::AlterClientQuotasResult> {
block_on(
&self.runtime,
self.admin.alter_client_quotas(alterations, validate_only),
)
}
pub fn describe_user_scram_credentials(
&self,
users: Option<&[String]>,
) -> Result<crate::DescribeUserScramCredentialsResult> {
block_on(
&self.runtime,
self.admin.describe_user_scram_credentials(users),
)
}
pub fn alter_user_scram_credentials(
&self,
deletions: &[crate::ScramCredentialDeletion],
upsertions: &[crate::ScramCredentialUpsertion],
) -> Result<crate::AlterUserScramCredentialsResult> {
block_on(
&self.runtime,
self.admin
.alter_user_scram_credentials(deletions, upsertions),
)
}
pub fn create_delegation_token(
&self,
options: crate::CreateDelegationTokenOptions,
) -> Result<crate::CreatedDelegationToken> {
block_on(&self.runtime, self.admin.create_delegation_token(options))
}
pub fn describe_delegation_tokens(
&self,
owners: Option<&[crate::DelegationTokenPrincipal]>,
) -> Result<crate::DescribeDelegationTokensResult> {
block_on(&self.runtime, self.admin.describe_delegation_tokens(owners))
}
pub fn renew_delegation_token(
&self,
hmac: &[u8],
renew_period: Duration,
) -> Result<crate::DelegationTokenOperationResult> {
block_on(
&self.runtime,
self.admin.renew_delegation_token(hmac, renew_period),
)
}
pub fn expire_delegation_token(
&self,
hmac: &[u8],
expiry_time_period: Duration,
) -> Result<crate::DelegationTokenOperationResult> {
block_on(
&self.runtime,
self.admin.expire_delegation_token(hmac, expiry_time_period),
)
}
pub fn elect_leaders(
&self,
elections: Option<&[crate::LeaderElection]>,
election_type: crate::ElectionType,
options: crate::ElectLeadersOptions,
) -> Result<crate::ElectLeadersResult> {
block_on(
&self.runtime,
self.admin.elect_leaders(elections, election_type, options),
)
}
pub fn alter_partition_reassignments(
&self,
reassignments: &[crate::PartitionReassignment],
options: crate::PartitionReassignmentOptions,
) -> Result<crate::AlterPartitionReassignmentsResult> {
block_on(
&self.runtime,
self.admin
.alter_partition_reassignments(reassignments, options),
)
}
pub fn list_partition_reassignments(
&self,
topics: Option<&[crate::PartitionReassignmentQuery]>,
options: crate::PartitionReassignmentOptions,
) -> Result<crate::ListPartitionReassignmentsResult> {
block_on(
&self.runtime,
self.admin.list_partition_reassignments(topics, options),
)
}
pub fn list_config_resources(
&self,
options: crate::ListConfigResourcesOptions,
) -> Result<crate::ListConfigResourcesResult> {
block_on(&self.runtime, self.admin.list_config_resources(options))
}
pub fn describe_share_groups(
&self,
group_ids: &[String],
include_authorized_operations: bool,
) -> Result<Vec<crate::ShareGroupDescription>> {
block_on(
&self.runtime,
self.admin
.describe_share_groups(group_ids, include_authorized_operations),
)
}
pub fn describe_streams_groups(
&self,
group_ids: &[String],
include_authorized_operations: bool,
) -> Result<Vec<crate::StreamsGroupDescription>> {
block_on(
&self.runtime,
self.admin
.describe_streams_groups(group_ids, include_authorized_operations),
)
}
pub fn delete_share_groups(
&self,
group_ids: &[String],
) -> Result<Vec<crate::DeleteShareGroupResult>> {
block_on(&self.runtime, self.admin.delete_share_groups(group_ids))
}
pub fn initialize_share_group_state(
&self,
group_id: &str,
topics: &[crate::ShareGroupStateInitializeTopic],
) -> Result<crate::ShareGroupStateResult> {
block_on(
&self.runtime,
self.admin.initialize_share_group_state(group_id, topics),
)
}
pub fn read_share_group_state(
&self,
group_id: &str,
topics: &[crate::ShareGroupStateReadTopic],
) -> Result<crate::ReadShareGroupStateResult> {
block_on(
&self.runtime,
self.admin.read_share_group_state(group_id, topics),
)
}
pub fn write_share_group_state(
&self,
group_id: &str,
topics: &[crate::ShareGroupStateWriteTopic],
) -> Result<crate::ShareGroupStateResult> {
block_on(
&self.runtime,
self.admin.write_share_group_state(group_id, topics),
)
}
pub fn delete_share_group_state(
&self,
group_id: &str,
topics: &[crate::ShareGroupStateDeleteTopic],
) -> Result<crate::ShareGroupStateResult> {
block_on(
&self.runtime,
self.admin.delete_share_group_state(group_id, topics),
)
}
pub fn read_share_group_state_summary(
&self,
group_id: &str,
topics: &[crate::ShareGroupStateReadTopic],
) -> Result<crate::ReadShareGroupStateSummaryResult> {
block_on(
&self.runtime,
self.admin.read_share_group_state_summary(group_id, topics),
)
}
pub fn alter_share_group_offsets(
&self,
group_id: &str,
offsets: &[crate::ShareGroupOffset],
) -> Result<crate::AlterShareGroupOffsetsResult> {
block_on(
&self.runtime,
self.admin.alter_share_group_offsets(group_id, offsets),
)
}
pub fn delete_share_group_offsets(
&self,
group_id: &str,
topics: &[String],
) -> Result<crate::DeleteShareGroupOffsetsResult> {
block_on(
&self.runtime,
self.admin.delete_share_group_offsets(group_id, topics),
)
}
pub fn list_share_group_offsets(
&self,
group_id: &str,
topics: Option<&[crate::ShareGroupOffsetQuery]>,
) -> Result<crate::ListShareGroupOffsetsResult> {
block_on(
&self.runtime,
self.admin.list_share_group_offsets(group_id, topics),
)
}
pub fn describe_log_dirs(
&self,
broker_ids: Option<&[i32]>,
topics: Option<&[crate::LogDirTopic]>,
) -> Result<Vec<crate::DescribeLogDirsBrokerResult>> {
block_on(
&self.runtime,
self.admin.describe_log_dirs(broker_ids, topics),
)
}
pub fn alter_replica_log_dirs(
&self,
broker_id: i32,
assignments: &[crate::ReplicaLogDirAssignment],
) -> Result<crate::AlterReplicaLogDirsResult> {
block_on(
&self.runtime,
self.admin.alter_replica_log_dirs(broker_id, assignments),
)
}
pub fn delete_consumer_group_offsets(
&self,
group_id: &str,
topics: &[crate::ConsumerGroupOffsetDelete],
) -> Result<crate::DeleteConsumerGroupOffsetsResult> {
block_on(
&self.runtime,
self.admin.delete_consumer_group_offsets(group_id, topics),
)
}
pub fn describe_producers(
&self,
topics: &[crate::DescribeProducersTopic],
) -> Result<crate::DescribeProducersResult> {
block_on(&self.runtime, self.admin.describe_producers(topics))
}
pub fn describe_transactions(
&self,
transactional_ids: &[String],
) -> Result<crate::DescribeTransactionsResult> {
block_on(
&self.runtime,
self.admin.describe_transactions(transactional_ids),
)
}
pub fn list_transactions(
&self,
options: crate::ListTransactionsOptions,
) -> Result<crate::ListTransactionsResult> {
block_on(&self.runtime, self.admin.list_transactions(options))
}
pub fn describe_cluster(&self) -> Result<ClusterDescription> {
block_on(&self.runtime, self.admin.describe_cluster())
}
pub fn list_topics(&self) -> Result<Vec<TopicListing>> {
block_on(&self.runtime, self.admin.list_topics())
}
pub fn list_groups(&self) -> Result<Vec<GroupListing>> {
block_on(&self.runtime, self.admin.list_groups())
}
pub fn list_groups_with_options(
&self,
options: ListGroupsOptions,
) -> Result<Vec<GroupListing>> {
block_on(&self.runtime, self.admin.list_groups_with_options(options))
}
pub fn create_topics(
&self,
topics: &[crate::NewTopic],
options: CreateTopicsOptions,
) -> Result<CreateTopicsResult> {
block_on(&self.runtime, self.admin.create_topics(topics, options))
}
pub fn delete_topics(
&self,
topic_names: &[String],
options: DeleteTopicsOptions,
) -> Result<DeleteTopicsResult> {
block_on(
&self.runtime,
self.admin.delete_topics(topic_names, options),
)
}
pub fn create_partitions(
&self,
topics: &[NewPartitions],
options: CreatePartitionsOptions,
) -> Result<CreatePartitionsResult> {
block_on(&self.runtime, self.admin.create_partitions(topics, options))
}
pub fn delete_records(
&self,
topics: &[crate::DeleteRecordsTopic],
options: DeleteRecordsOptions,
) -> Result<DeleteRecordsResult> {
block_on(&self.runtime, self.admin.delete_records(topics, options))
}
pub fn describe_features(&self) -> Result<FeatureMetadata> {
block_on(&self.runtime, self.admin.describe_features())
}
pub fn update_features(
&self,
updates: &[crate::FeatureUpdate],
options: UpdateFeaturesOptions,
) -> Result<UpdateFeaturesResult> {
block_on(&self.runtime, self.admin.update_features(updates, options))
}
pub fn describe_topic_configs(
&self,
resources: &[TopicConfigResource],
options: DescribeConfigsOptions,
) -> Result<DescribeConfigsResult> {
block_on(
&self.runtime,
self.admin.describe_topic_configs(resources, options),
)
}
pub fn incremental_alter_topic_configs(
&self,
resources: &[TopicConfigAlteration],
options: AlterConfigsOptions,
) -> Result<AlterConfigsResult> {
block_on(
&self.runtime,
self.admin
.incremental_alter_topic_configs(resources, options),
)
}
pub fn alter_topic_configs(
&self,
resources: &[TopicConfigUpdate],
options: AlterConfigsOptions,
) -> Result<AlterConfigsResult> {
block_on(
&self.runtime,
self.admin.alter_topic_configs(resources, options),
)
}
pub fn describe_consumer_groups(
&self,
group_ids: &[String],
) -> Result<Vec<ConsumerGroupDescription>> {
block_on(
&self.runtime,
self.admin.describe_consumer_groups(group_ids),
)
}
pub fn describe_consumer_groups_modern(
&self,
group_ids: &[String],
include_authorized_operations: bool,
) -> Result<Vec<ModernConsumerGroupDescription>> {
block_on(
&self.runtime,
self.admin
.describe_consumer_groups_modern(group_ids, include_authorized_operations),
)
}
pub fn delete_consumer_groups(
&self,
group_ids: &[String],
) -> Result<Vec<DeleteConsumerGroupResult>> {
block_on(&self.runtime, self.admin.delete_consumer_groups(group_ids))
}
pub fn list_consumer_group_offsets(
&self,
group_id: &str,
topics: Option<&[ConsumerGroupOffsetQuery]>,
) -> Result<ListConsumerGroupOffsetsResult> {
block_on(
&self.runtime,
self.admin.list_consumer_group_offsets(group_id, topics),
)
}
pub fn list_consumer_group_offsets_with_member(
&self,
group_id: &str,
member_id: Option<&str>,
member_epoch: i32,
topics: Option<&[ConsumerGroupOffsetQuery]>,
require_stable: bool,
) -> Result<ListConsumerGroupOffsetsResult> {
block_on(
&self.runtime,
self.admin.list_consumer_group_offsets_with_member(
group_id,
member_id,
member_epoch,
topics,
require_stable,
),
)
}
pub fn alter_consumer_group_offsets(
&self,
group_id: &str,
offsets: &[ConsumerGroupOffset],
) -> Result<AlterConsumerGroupOffsetsResult> {
block_on(
&self.runtime,
self.admin.alter_consumer_group_offsets(group_id, offsets),
)
}
pub fn alter_consumer_group_offsets_with_member(
&self,
group_id: &str,
member_id: &str,
member_epoch: i32,
group_instance_id: Option<&str>,
offsets: &[ConsumerGroupOffset],
) -> Result<AlterConsumerGroupOffsetsResult> {
block_on(
&self.runtime,
self.admin.alter_consumer_group_offsets_with_member(
group_id,
member_id,
member_epoch,
group_instance_id,
offsets,
),
)
}
}
pub struct BlockingProducer {
producer: Producer,
runtime: Runtime,
}
pub struct BlockingBufferedProducer {
producer: BufferedProducer,
runtime: Arc<Runtime>,
}
impl BlockingBufferedProducer {
pub fn build(config: ProducerConfig) -> Result<Self> {
let runtime = Arc::new(build_runtime()?);
let producer = block_on(runtime.as_ref(), config.build_buffered())?;
Ok(Self { producer, runtime })
}
pub fn send(&mut self, record: ProducerRecord) -> Result<ProducerDelivery> {
block_on(self.runtime.as_ref(), self.producer.send(record))
}
pub fn wait_delivery(&self, delivery: ProducerDelivery) -> Result<RecordMetadata> {
block_on(self.runtime.as_ref(), delivery.wait())
}
pub fn handle(&self) -> Result<BlockingBufferedProducerHandle> {
Ok(BlockingBufferedProducerHandle {
handle: self.producer.handle()?,
runtime: Arc::clone(&self.runtime),
})
}
pub fn begin_transaction(&mut self) -> Result<()> {
block_on(self.runtime.as_ref(), self.producer.begin_transaction())
}
pub fn send_group_offsets_to_transaction(
&mut self,
metadata: &ConsumerGroupMetadata,
assignments: &[ConsumerAssignment],
) -> Result<()> {
block_on(
self.runtime.as_ref(),
self.producer
.send_group_offsets_to_transaction(metadata, assignments),
)
}
pub fn commit_transaction(&mut self) -> Result<()> {
block_on(self.runtime.as_ref(), self.producer.commit_transaction())
}
pub fn abort_transaction(&mut self) -> Result<()> {
block_on(self.runtime.as_ref(), self.producer.abort_transaction())
}
pub fn flush(&mut self) -> Result<()> {
block_on(self.runtime.as_ref(), self.producer.flush())
}
pub fn close(&mut self) -> Result<()> {
block_on(self.runtime.as_ref(), self.producer.close())
}
pub fn in_transaction(&self) -> bool {
self.producer.in_transaction()
}
pub fn transaction_status(&self) -> Option<TransactionStatus> {
self.producer.transaction_status()
}
pub fn is_closed(&self) -> bool {
self.producer.is_closed()
}
}
#[derive(Clone, Debug)]
pub struct BlockingBufferedProducerHandle {
handle: BufferedProducerHandle,
runtime: Arc<Runtime>,
}
impl BlockingBufferedProducerHandle {
pub fn send(&self, record: ProducerRecord) -> Result<ProducerDelivery> {
block_on(self.runtime.as_ref(), self.handle.send(record))
}
pub fn wait_delivery(&self, delivery: ProducerDelivery) -> Result<RecordMetadata> {
block_on(self.runtime.as_ref(), delivery.wait())
}
}
impl BlockingProducer {
pub fn build(config: ProducerConfig) -> Result<Self> {
let runtime = build_runtime()?;
let producer = block_on(&runtime, config.build())?;
Ok(Self { producer, runtime })
}
pub fn send(&mut self, record: ProducerRecord) -> Result<RecordMetadata> {
block_on(&self.runtime, self.producer.send(record))
}
pub fn send_batch(
&mut self,
records: impl IntoIterator<Item = ProducerRecord>,
) -> Result<Vec<RecordMetadata>> {
block_on(&self.runtime, self.producer.send_batch(records))
}
pub fn send_batch_report(
&mut self,
records: impl IntoIterator<Item = ProducerRecord>,
) -> Result<ProducerBatchReport> {
block_on(&self.runtime, self.producer.send_batch_report(records))
}
pub fn begin_transaction(&mut self) -> Result<()> {
self.producer.begin_transaction()
}
pub fn send_group_offsets_to_transaction(
&mut self,
metadata: &crate::ConsumerGroupMetadata,
assignments: &[ConsumerAssignment],
) -> Result<()> {
block_on(
&self.runtime,
self.producer
.send_group_offsets_to_transaction(metadata, assignments),
)
}
pub fn commit_transaction(&mut self) -> Result<()> {
block_on(&self.runtime, self.producer.commit_transaction())
}
pub fn abort_transaction(&mut self) -> Result<()> {
block_on(&self.runtime, self.producer.abort_transaction())
}
pub fn in_transaction(&self) -> bool {
self.producer.in_transaction()
}
pub fn transaction_status(&self) -> Option<TransactionStatus> {
self.producer.transaction_status()
}
}
pub struct BlockingConsumer {
consumer: Consumer,
runtime: Runtime,
}
impl BlockingConsumer {
pub fn build(config: ConsumerConfig) -> Result<Self> {
let runtime = build_runtime()?;
let consumer = block_on(&runtime, config.build())?;
Ok(Self { consumer, runtime })
}
pub fn assign(&mut self, topic: impl Into<String>, partition: i32, offset: i64) {
self.consumer.assign(topic, partition, offset);
}
pub fn assignments(&self) -> &[ConsumerAssignment] {
self.consumer.assignments()
}
pub fn position(&self, topic: &str, partition: i32) -> Option<i64> {
self.consumer.position(topic, partition)
}
pub fn seek(&mut self, topic: &str, partition: i32, offset: i64) -> Result<()> {
self.consumer.seek(topic, partition, offset)
}
pub fn pause(&mut self, topic: &str, partition: i32) -> Result<()> {
self.consumer.pause(topic, partition)
}
pub fn resume(&mut self, topic: &str, partition: i32) -> Result<()> {
self.consumer.resume(topic, partition)
}
pub fn poll(&mut self) -> Result<Vec<ConsumerRecord>> {
block_on(&self.runtime, self.consumer.poll())
}
pub fn fetch(
&mut self,
topic: impl Into<String>,
partition: i32,
offset: i64,
) -> Result<Vec<ConsumerRecord>> {
block_on(&self.runtime, self.consumer.fetch(topic, partition, offset))
}
pub fn fetch_watermarks(
&mut self,
topic: impl Into<String>,
partition: i32,
) -> Result<PartitionWatermarks> {
block_on(
&self.runtime,
self.consumer.fetch_watermarks(topic, partition),
)
}
pub fn offset_for_leader_epoch(
&mut self,
topic: impl Into<String>,
partition: i32,
current_leader_epoch: i32,
leader_epoch: i32,
) -> Result<LeaderEpochOffset> {
block_on(
&self.runtime,
self.consumer.offset_for_leader_epoch(
topic,
partition,
current_leader_epoch,
leader_epoch,
),
)
}
}
pub struct BlockingConsumerGroup {
group: ConsumerGroup,
runtime: Runtime,
}
impl BlockingConsumerGroup {
pub fn join(config: ConsumerGroupConfig) -> Result<Self> {
let runtime = build_runtime()?;
let group = block_on(&runtime, config.join())?;
Ok(Self { group, runtime })
}
pub fn group_id(&self) -> &str {
self.group.group_id()
}
pub fn group_protocol(&self) -> ConsumerGroupProtocol {
self.group.group_protocol()
}
pub fn member_id(&self) -> &str {
self.group.member_id()
}
pub fn generation_id(&self) -> i32 {
self.group.generation_id()
}
pub fn metadata(&self) -> ConsumerGroupMetadata {
self.group.metadata()
}
pub fn assignments(&self) -> &[ConsumerAssignment] {
self.group.assignments()
}
pub fn topic_id(&self, topic: &str) -> Option<[u8; 16]> {
self.group.topic_id(topic)
}
pub fn commit_record(&mut self, record: &ConsumerRecord) -> Result<()> {
self.group.commit_record(record)
}
pub fn pending_commit_count(&self) -> usize {
self.group.pending_commit_count()
}
pub fn position(&self, topic: &str, partition: i32) -> Option<i64> {
self.group.position(topic, partition)
}
pub fn seek(&mut self, topic: &str, partition: i32, offset: i64) -> Result<()> {
self.group.seek(topic, partition, offset)
}
pub fn pause(&mut self, topic: &str, partition: i32) -> Result<()> {
self.group.pause(topic, partition)
}
pub fn resume(&mut self, topic: &str, partition: i32) -> Result<()> {
self.group.resume(topic, partition)
}
pub fn poll(&mut self) -> Result<Vec<ConsumerRecord>> {
block_on(&self.runtime, self.group.poll())
}
pub fn heartbeat(&mut self) -> Result<()> {
block_on(&self.runtime, self.group.heartbeat())
}
pub fn commit_offsets(&mut self) -> Result<()> {
block_on(&self.runtime, self.group.commit_offsets())
}
pub fn commit_queued_offsets(&mut self) -> Result<()> {
block_on(&self.runtime, self.group.commit_queued_offsets())
}
pub fn fetch_watermarks(
&mut self,
topic: impl Into<String>,
partition: i32,
) -> Result<PartitionWatermarks> {
block_on(&self.runtime, self.group.fetch_watermarks(topic, partition))
}
pub fn offset_for_leader_epoch(
&mut self,
topic: impl Into<String>,
partition: i32,
current_leader_epoch: i32,
leader_epoch: i32,
) -> Result<LeaderEpochOffset> {
block_on(
&self.runtime,
self.group.offset_for_leader_epoch(
topic,
partition,
current_leader_epoch,
leader_epoch,
),
)
}
pub fn leave(self) -> Result<()> {
let Self { group, runtime } = self;
block_on(&runtime, group.leave())
}
}
pub struct BlockingShareConsumer {
consumer: ShareConsumer,
runtime: Runtime,
}
impl BlockingShareConsumer {
pub fn build(config: ShareConsumerConfig) -> Result<Self> {
let runtime = build_runtime()?;
let consumer = block_on(&runtime, config.build())?;
Ok(Self { consumer, runtime })
}
pub fn group_id(&self) -> &str {
self.consumer.group_id()
}
pub fn member_id(&self) -> &str {
self.consumer.member_id()
}
pub fn member_epoch(&self) -> i32 {
self.consumer.member_epoch()
}
pub fn assignment_count(&self) -> usize {
self.consumer.assignment_count()
}
pub fn acquisition_lock_timeout_ms(&self) -> Option<i32> {
self.consumer.acquisition_lock_timeout_ms()
}
pub fn pending_acknowledgement_reconciliation_count(&self) -> usize {
self.consumer.pending_acknowledgement_reconciliation_count()
}
pub fn reconcile_acknowledgement_outcomes(&mut self) -> Result<()> {
block_on(
&self.runtime,
self.consumer.reconcile_acknowledgement_outcomes(),
)
}
pub fn heartbeat(&mut self) -> Result<()> {
block_on(&self.runtime, self.consumer.heartbeat())
}
pub fn poll(&mut self) -> Result<Vec<ShareRecord>> {
block_on(&self.runtime, self.consumer.poll())
}
pub fn acknowledge(
&mut self,
record: &ShareRecord,
acknowledgement: ShareAcknowledgementType,
) -> Result<()> {
self.consumer.acknowledge(record, acknowledgement)
}
pub fn commit(&mut self) -> Result<()> {
block_on(&self.runtime, self.consumer.commit())
}
pub fn close(&mut self) -> Result<()> {
block_on(&self.runtime, self.consumer.close())
}
}
pub struct BlockingStreamsGroupSession {
session: StreamsGroupSession,
runtime: Runtime,
}
impl BlockingStreamsGroupSession {
pub fn join(config: StreamsGroupConfig) -> Result<Self> {
let runtime = build_runtime()?;
let session = block_on(&runtime, StreamsGroupSession::join(config))?;
Ok(Self { session, runtime })
}
pub fn group_id(&self) -> &str {
self.session.group_id()
}
pub fn member_id(&self) -> &str {
self.session.member_id()
}
pub fn member_epoch(&self) -> i32 {
self.session.member_epoch()
}
pub fn heartbeat_interval(&self) -> Duration {
self.session.heartbeat_interval()
}
pub fn assignment(&self) -> &StreamsGroupSessionAssignment {
self.session.assignment()
}
pub fn set_task_state(
&mut self,
active_tasks: Vec<StreamsGroupHeartbeatTask>,
standby_tasks: Vec<StreamsGroupHeartbeatTask>,
warmup_tasks: Vec<StreamsGroupHeartbeatTask>,
task_offsets: Vec<StreamsGroupHeartbeatTaskOffset>,
task_end_offsets: Vec<StreamsGroupHeartbeatTaskOffset>,
) {
self.session.set_task_state(
active_tasks,
standby_tasks,
warmup_tasks,
task_offsets,
task_end_offsets,
);
}
pub fn set_task_state_with_optional_offsets(
&mut self,
active_tasks: Vec<StreamsGroupHeartbeatTask>,
standby_tasks: Vec<StreamsGroupHeartbeatTask>,
warmup_tasks: Vec<StreamsGroupHeartbeatTask>,
task_offsets: Option<Vec<StreamsGroupHeartbeatTaskOffset>>,
task_end_offsets: Option<Vec<StreamsGroupHeartbeatTaskOffset>>,
) {
self.session.set_task_state_with_optional_offsets(
active_tasks,
standby_tasks,
warmup_tasks,
task_offsets,
task_end_offsets,
);
}
pub fn is_closed(&self) -> bool {
self.session.is_closed()
}
pub fn heartbeat(&mut self) -> Result<StreamsGroupHeartbeatResponseV0> {
block_on(&self.runtime, self.session.heartbeat())
}
pub fn close(&mut self) -> Result<()> {
block_on(&self.runtime, self.session.close())
}
}
#[cfg(test)]
mod tests {
use super::BlockingShareConsumer;
use super::{
BlockingAdminClient, BlockingBufferedProducer, BlockingConsumer, BlockingConsumerGroup,
BlockingProducer, BlockingStreamsGroupSession,
};
use crate::{
ClientConfig, ConsumerConfig, ConsumerGroupConfig, Error, ProducerConfig,
ShareConsumerConfig,
};
#[test]
fn blocking_build_rejects_nested_tokio_runtime() {
let runtime_result = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build();
assert!(
runtime_result.is_ok(),
"test runtime should build: {runtime_result:?}"
);
let Some(runtime) = runtime_result.ok() else {
return;
};
let producer_result = runtime
.block_on(async { BlockingProducer::build(ProducerConfig::new(["localhost:9092"])) });
assert!(matches!(
producer_result,
Err(Error::Unsupported(
"blocking kafrust clients cannot run inside a Tokio runtime; use the async API instead"
))
));
let buffered_producer_result = runtime.block_on(async {
BlockingBufferedProducer::build(ProducerConfig::new(["localhost:9092"]))
});
assert!(matches!(
buffered_producer_result,
Err(Error::Unsupported(
"blocking kafrust clients cannot run inside a Tokio runtime; use the async API instead"
))
));
let consumer_result = runtime
.block_on(async { BlockingConsumer::build(ConsumerConfig::new(["localhost:9092"])) });
assert!(matches!(
consumer_result,
Err(Error::Unsupported(
"blocking kafrust clients cannot run inside a Tokio runtime; use the async API instead"
))
));
let admin_result = runtime
.block_on(async { BlockingAdminClient::build(ClientConfig::new(["localhost:9092"])) });
assert!(matches!(
admin_result,
Err(Error::Unsupported(
"blocking kafrust clients cannot run inside a Tokio runtime; use the async API instead"
))
));
let group_result = runtime.block_on(async {
BlockingConsumerGroup::join(
ConsumerGroupConfig::new(["localhost:9092"], "orders-group").subscribe("orders"),
)
});
assert!(matches!(
group_result,
Err(Error::Unsupported(
"blocking kafrust clients cannot run inside a Tokio runtime; use the async API instead"
))
));
let share_result = runtime.block_on(async {
BlockingShareConsumer::build(
ShareConsumerConfig::new(["localhost:9092"], "orders-share").subscribe("orders"),
)
});
assert!(matches!(
share_result,
Err(Error::Unsupported(
"blocking kafrust clients cannot run inside a Tokio runtime; use the async API instead"
))
));
let streams_result = runtime.block_on(async {
BlockingStreamsGroupSession::join(crate::streams::StreamsGroupConfig::new(
["localhost:9092"],
"orders-streams",
crate::streams::StreamsGroupHeartbeatTopology {
epoch: 0,
subtopologies: Vec::new(),
},
))
});
assert!(matches!(
streams_result,
Err(Error::Unsupported(
"blocking kafrust clients cannot run inside a Tokio runtime; use the async API instead"
))
));
}
}