use crate::telemetry::logging::{scope::RequestLogScope, targets};
use futures::stream::{BoxStream, Stream};
use futures_util::StreamExt;
use ntex::rt;
use tokio::sync::mpsc;
use tracing::debug;
use crate::telemetry::metrics::subscription_metrics::SubscriptionTransport;
use crate::telemetry::TelemetryContext;
use crate::executor::executors::error::SubgraphExecutorError;
use crate::executor::response::subgraph_response::SubgraphResponse;
type SubscriptionItem = Result<SubgraphResponse<'static>, SubgraphExecutorError>;
pub enum SendOutcome {
Sent,
Dropped,
Closed,
}
pub fn try_send_or_drop<T>(
tx: &mpsc::Sender<T>,
item: T,
telemetry_context: &TelemetryContext,
transport: SubscriptionTransport,
subgraph_name: &str,
endpoint: &str,
) -> SendOutcome {
match tx.try_send(item) {
Ok(()) => SendOutcome::Sent,
Err(mpsc::error::TrySendError::Full(_)) => {
debug!(
target: targets::SUBSCRIPTIONS,
subgraph = subgraph_name, endpoint = %endpoint,
"Consumer for subgraph is too slow, dropping message",
);
telemetry_context
.metrics
.subscriptions
.record_message_dropped(transport);
SendOutcome::Dropped
}
Err(mpsc::error::TrySendError::Closed(_)) => {
debug!(
target: targets::SUBSCRIPTIONS,
subgraph = subgraph_name, endpoint = %endpoint,
"Subscription buffer for subgraph has no more receivers, all consumers disconnected or unsubscribed; stopping upstream drain",
);
SendOutcome::Closed
}
}
}
pub async fn drain_into<S>(
mut source: S,
tx: mpsc::Sender<SubscriptionItem>,
telemetry_context: &TelemetryContext,
transport: SubscriptionTransport,
subgraph_name: &str,
endpoint: &str,
) where
S: Stream<Item = SubscriptionItem> + Unpin,
{
loop {
tokio::select! {
item = source.next() => {
let Some(item) = item else {
break;
};
if matches!(
try_send_or_drop(
&tx,
item,
telemetry_context,
transport,
subgraph_name,
endpoint
),
SendOutcome::Closed
) {
break;
}
}
_ = tx.closed() => break,
}
}
}
pub fn buffered<S>(
source: S,
buffer_size: usize,
telemetry_context: std::sync::Arc<TelemetryContext>,
transport: SubscriptionTransport,
subgraph_name: String,
endpoint: String,
) -> BoxStream<'static, SubscriptionItem>
where
S: Stream<Item = SubscriptionItem> + Unpin + 'static,
{
let (tx, mut rx) = mpsc::channel::<SubscriptionItem>(buffer_size);
let log_scope = RequestLogScope::capture();
drop(rt::spawn(log_scope.scope(async move {
drain_into(
source,
tx,
&telemetry_context,
transport,
&subgraph_name,
&endpoint,
)
.await;
})));
Box::pin(async_stream::stream! {
while let Some(item) = rx.recv().await {
yield item;
}
})
}