use std::sync::Arc;
use std::time::Duration;
use tonic::Code;
use tonic::Status;
use super::super::Result;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum LedgerStreamFamily {
Checkpoint,
Transaction,
Event,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum LedgerStreamOperation {
List,
GetServiceInfo,
Subscribe,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum LedgerStreamStage {
List,
InitialReplay,
LiveTipStartup,
PollingBaseline,
PollingTail,
GapRecovery,
LiveSubscription,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum LedgerStreamEvent {
#[non_exhaustive]
RpcResponse {
family: LedgerStreamFamily,
operation: LedgerStreamOperation,
stage: LedgerStreamStage,
code: Code,
elapsed: Duration,
},
#[non_exhaustive]
RetryScheduled {
family: LedgerStreamFamily,
operation: LedgerStreamOperation,
stage: LedgerStreamStage,
status: Status,
consecutive_failures: u32,
delay: Duration,
},
#[non_exhaustive]
RetryRecovered {
family: LedgerStreamFamily,
operation: LedgerStreamOperation,
started_in: LedgerStreamStage,
consecutive_failures: u32,
elapsed: Duration,
},
#[non_exhaustive]
SubscriptionStreamInterrupted {
family: LedgerStreamFamily,
stage: LedgerStreamStage,
status: Status,
},
#[non_exhaustive]
GapRecoveryStarted { family: LedgerStreamFamily },
#[non_exhaustive]
SubscriptionBufferLimitReached {
family: LedgerStreamFamily,
buffered_items: usize,
limit: usize,
},
#[non_exhaustive]
TerminalError {
family: LedgerStreamFamily,
status: Status,
},
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum ListEvent {
#[non_exhaustive]
RpcResponse {
family: LedgerStreamFamily,
code: Code,
elapsed: Duration,
},
#[non_exhaustive]
RetryScheduled {
family: LedgerStreamFamily,
status: Status,
consecutive_failures: u32,
delay: Duration,
},
#[non_exhaustive]
RetryRecovered {
family: LedgerStreamFamily,
consecutive_failures: u32,
elapsed: Duration,
},
#[non_exhaustive]
TerminalError {
family: LedgerStreamFamily,
status: Status,
},
}
impl ListEvent {
pub(super) fn from_stream_event(event: LedgerStreamEvent) -> Option<Self> {
match event {
LedgerStreamEvent::RpcResponse {
family,
operation: LedgerStreamOperation::List,
code,
elapsed,
..
} => Some(Self::RpcResponse {
family,
code,
elapsed,
}),
LedgerStreamEvent::RetryScheduled {
family,
operation: LedgerStreamOperation::List,
status,
consecutive_failures,
delay,
..
} => Some(Self::RetryScheduled {
family,
status,
consecutive_failures,
delay,
}),
LedgerStreamEvent::RetryRecovered {
family,
operation: LedgerStreamOperation::List,
consecutive_failures,
elapsed,
..
} => Some(Self::RetryRecovered {
family,
consecutive_failures,
elapsed,
}),
LedgerStreamEvent::TerminalError { family, status } => {
Some(Self::TerminalError { family, status })
}
_ => None,
}
}
}
pub(super) type ListObserver = Arc<dyn Fn(ListEvent) + Send + Sync + 'static>;
pub(super) type LedgerStreamObserver = Arc<dyn Fn(LedgerStreamEvent) + Send + Sync + 'static>;
#[derive(Clone, Default)]
pub(super) struct LedgerStreamObservability {
observer: Option<LedgerStreamObserver>,
}
impl LedgerStreamObservability {
pub(super) fn new(observer: Option<LedgerStreamObserver>) -> Self {
Self { observer }
}
pub(super) fn emit(&self, event: impl FnOnce() -> LedgerStreamEvent) {
if let Some(observer) = &self.observer {
observer(event());
}
}
pub(super) fn start_timer(&self) -> Option<tokio::time::Instant> {
self.observer.as_ref().map(|_| tokio::time::Instant::now())
}
pub(super) fn emit_rpc_response<T>(
&self,
started_at: Option<tokio::time::Instant>,
family: LedgerStreamFamily,
operation: LedgerStreamOperation,
stage: LedgerStreamStage,
result: &Result<T>,
) {
let Some(started_at) = started_at else {
return;
};
let code = result
.as_ref()
.map_or_else(|status| status.code(), |_| Code::Ok);
self.emit(|| LedgerStreamEvent::RpcResponse {
family,
operation,
stage,
code,
elapsed: started_at.elapsed(),
});
}
}