use std::sync::Arc;
use crate::error::Error;
use crate::topic::backend::TopicState;
use crate::topic::subscription::Subscription;
use crate::topic::types::{
PublishOutcome, SubscriberOptions, SubscriptionMode, TopicPublishOutcomeConfig,
TrackedPublishPermit, TrackedPublishReceipt, TrackedTryPublishOutcome,
};
use otel_arrow_dfe_config::TopicName;
use otel_arrow_dfe_config::topic::TopicBroadcastOnLagPolicy;
use tokio::sync::Semaphore;
pub struct TopicHandle<T: Send + Sync + 'static> {
inner: Arc<dyn TopicState<T>>,
publish_outcome_default: TopicPublishOutcomeConfig,
}
impl<T: Send + Sync + 'static> Clone for TopicHandle<T> {
fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
publish_outcome_default: self.publish_outcome_default,
}
}
}
pub struct TrackedTopicPublisher<T: Send + Sync + 'static> {
handle: TopicHandle<T>,
in_flight: Arc<Semaphore>,
max_in_flight: usize,
timeout: std::time::Duration,
}
impl<T: Send + Sync + 'static> Clone for TrackedTopicPublisher<T> {
fn clone(&self) -> Self {
Self {
handle: self.handle.clone(),
in_flight: Arc::clone(&self.in_flight),
max_in_flight: self.max_in_flight,
timeout: self.timeout,
}
}
}
impl<T: Send + Sync + 'static> TopicHandle<T> {
pub(crate) fn new(inner: Arc<dyn TopicState<T>>) -> Self {
Self {
inner,
publish_outcome_default: TopicPublishOutcomeConfig::default(),
}
}
#[must_use]
pub fn with_default_publish_outcome_config(&self, config: TopicPublishOutcomeConfig) -> Self {
Self {
inner: Arc::clone(&self.inner),
publish_outcome_default: config,
}
}
#[must_use]
pub fn tracked_publisher(&self) -> TrackedTopicPublisher<T> {
self.tracked_publisher_with_config(self.publish_outcome_default)
}
#[must_use]
pub fn tracked_publisher_with_config(
&self,
config: TopicPublishOutcomeConfig,
) -> TrackedTopicPublisher<T> {
TrackedTopicPublisher {
handle: self.clone(),
in_flight: Arc::new(Semaphore::new(config.max_in_flight.max(1))),
max_in_flight: config.max_in_flight.max(1),
timeout: config.timeout,
}
}
pub async fn publish(&self, msg: Arc<T>) -> Result<(), Error> {
self.inner.publish(msg).await
}
pub fn try_publish(&self, msg: Arc<T>) -> Result<PublishOutcome, Error> {
self.inner.try_publish(msg)
}
pub fn subscribe(
&self,
mode: SubscriptionMode,
opts: SubscriberOptions,
) -> Result<Subscription<T>, Error> {
let backend = match mode {
SubscriptionMode::Balanced { group } => self.inner.subscribe_balanced(group, opts)?,
SubscriptionMode::Broadcast => self.inner.subscribe_broadcast(opts)?,
};
Ok(Subscription::new(backend))
}
pub fn close(&self) {
self.inner.close();
}
#[must_use]
pub fn name(&self) -> &TopicName {
self.inner.name()
}
#[must_use]
pub const fn default_publish_outcome_config(&self) -> TopicPublishOutcomeConfig {
self.publish_outcome_default
}
#[must_use]
pub fn broadcast_on_lag_policy(&self) -> TopicBroadcastOnLagPolicy {
self.inner.broadcast_on_lag_policy()
}
#[cfg(test)]
pub(crate) fn debug_balanced_available_permits(
&self,
) -> Vec<(crate::topic::SubscriptionGroupName, usize)> {
self.inner.debug_balanced_available_permits()
}
}
impl<T: Send + Sync + 'static> TrackedTopicPublisher<T> {
pub async fn publish(&self, msg: Arc<T>) -> Result<TrackedPublishReceipt, Error> {
let permit = self
.in_flight
.clone()
.acquire_owned()
.await
.expect("tracked publisher semaphore should not close");
self.handle
.inner
.publish_tracked(
msg,
self.timeout,
TrackedPublishPermit::from_tokio_owned(permit),
)
.await
}
pub fn try_publish(&self, msg: Arc<T>) -> Result<TrackedTryPublishOutcome, Error> {
let permit = match self.in_flight.clone().try_acquire_owned() {
Ok(permit) => permit,
Err(_) => return Ok(TrackedTryPublishOutcome::MaxInFlightReached),
};
self.handle.inner.try_publish_tracked(
msg,
self.timeout,
TrackedPublishPermit::from_tokio_owned(permit),
)
}
#[must_use]
pub fn topic(&self) -> &TopicName {
self.handle.name()
}
#[must_use]
pub fn max_in_flight(&self) -> usize {
self.max_in_flight
}
}