use std::{sync::OnceLock, time::Duration};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RpcMethod {
GetObject,
BatchGetObjects,
GetTransaction,
BatchGetTransactions,
GetCheckpoint,
GetServiceInfo,
GetEpoch,
ListDynamicFields,
ListOwnedObjects,
ListTransactions,
ExecuteTransaction,
SimulateTransaction,
SubscribeCheckpoints,
SubscribeExecutedTransactions,
GetBalance,
Other,
}
impl RpcMethod {
pub const ALL: [Self; 16] = [
Self::GetObject,
Self::BatchGetObjects,
Self::GetTransaction,
Self::BatchGetTransactions,
Self::GetCheckpoint,
Self::GetServiceInfo,
Self::GetEpoch,
Self::ListDynamicFields,
Self::ListOwnedObjects,
Self::ListTransactions,
Self::ExecuteTransaction,
Self::SimulateTransaction,
Self::SubscribeCheckpoints,
Self::SubscribeExecutedTransactions,
Self::GetBalance,
Self::Other,
];
pub const fn label(self) -> &'static str {
match self {
Self::GetObject => "GetObject",
Self::BatchGetObjects => "BatchGetObjects",
Self::GetTransaction => "GetTransaction",
Self::BatchGetTransactions => "BatchGetTransactions",
Self::GetCheckpoint => "GetCheckpoint",
Self::GetServiceInfo => "GetServiceInfo",
Self::GetEpoch => "GetEpoch",
Self::ListDynamicFields => "ListDynamicFields",
Self::ListOwnedObjects => "ListOwnedObjects",
Self::ListTransactions => "ListTransactions",
Self::ExecuteTransaction => "ExecuteTransaction",
Self::SimulateTransaction => "SimulateTransaction",
Self::SubscribeCheckpoints => "SubscribeCheckpoints",
Self::SubscribeExecutedTransactions => "SubscribeExecutedTransactions",
Self::GetBalance => "GetBalance",
Self::Other => "other",
}
}
pub(super) fn from_path(name: &str) -> Self {
Self::ALL
.into_iter()
.find(|method| method.label() == name)
.unwrap_or(Self::Other)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Traffic {
Normal,
Recovery,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Operation {
Rpc { method: RpcMethod, traffic: Traffic },
Read,
ReadAttempt,
RetryDelay,
RequestAdmission,
RecoveryRead,
RecoveryAttempt,
RecoveryDelay,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Outcome {
Success,
Error(Option<tonic::Code>),
Deadline,
Cancelled,
Skipped,
}
pub trait Observer: Send + Sync + 'static {
fn started(&self, operation: Operation);
fn finished(&self, operation: Operation, elapsed: Duration, outcome: Outcome);
}
static OBSERVER: OnceLock<Box<dyn Observer>> = OnceLock::new();
pub fn install(observer: Box<dyn Observer>) -> Result<(), Box<dyn Observer>> {
OBSERVER.set(observer)
}
struct ActiveObservation {
observer: &'static dyn Observer,
operation: Operation,
started: tokio::time::Instant,
outcome: Outcome,
}
pub(crate) struct Observation(Option<ActiveObservation>);
impl Observation {
#[cfg(test)]
pub(super) fn for_test(observer: &'static dyn Observer, operation: Operation) -> Self {
observer.started(operation);
Self(Some(ActiveObservation {
observer,
operation,
started: tokio::time::Instant::now(),
outcome: Outcome::Cancelled,
}))
}
pub(crate) fn start(operation: Operation) -> Self {
Self(OBSERVER.get().map(|observer| {
observer.started(operation);
ActiveObservation {
observer: observer.as_ref(),
operation,
started: tokio::time::Instant::now(),
outcome: Outcome::Cancelled,
}
}))
}
pub(crate) fn finish(mut self, outcome: Outcome) {
if let Some(active) = self.0.as_mut() {
active.outcome = outcome;
}
}
pub(crate) fn is_active(&self) -> bool {
self.0.is_some()
}
}
impl Drop for Observation {
fn drop(&mut self) {
if let Some(active) = &self.0 {
active
.observer
.finished(active.operation, active.started.elapsed(), active.outcome);
}
}
}