use { super::Slot, prometheus_client::{ encoding::{EncodeLabelSet, EncodeLabelValue}, metrics::{counter::Counter, family::Family, histogram::Histogram}, registry::Registry, }, std::collections::{BTreeMap, HashMap}, tokio::time::Instant, }; const MAX_SLOT_OBSERVATIONS: usize = 1000; #[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelValue)] pub enum SlotOrder { New, OutOfOrder, } #[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelValue)] pub enum Event { Vaa, AccumulatorMessages, CompletedUpdate, } #[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelSet)] struct ObservedSlotLabels { pub order: SlotOrder, pub event: Event, } #[derive(Clone, Debug)] pub struct Metrics { observed_slot: Family, observed_slot_latency: Family, first_observed_time_of_slot: BTreeMap, newest_observed_slot: HashMap, } impl Metrics { pub fn new(metrics_registry: &mut Registry) -> Self { let new = Self { observed_slot: Family::default(), observed_slot_latency: Family::new_with_constructor(|| { Histogram::new( [ 0.1, 0.2, 0.3, 0.4, 0.5, 0.7, 1.0, 1.3, 1.7, 2.0, 3.0, 5.0, 10.0, 20.0, ] .into_iter(), ) }), first_observed_time_of_slot: BTreeMap::new(), newest_observed_slot: HashMap::new(), }; { let observed_slot = new.observed_slot.clone(); let observed_slot_latency = new.observed_slot_latency.clone(); metrics_registry.register( "aggregate_observed_slot", "Total number of observed slots", observed_slot, ); metrics_registry.register( "aggregate_observed_slot_latency_seconds", "Latency of observed slots in seconds", observed_slot_latency, ); } new } /// Observe a slot and event. An event at a slot should be observed only once. pub fn observe(&mut self, slot: Slot, event: Event) { let order = if self .newest_observed_slot .get(&event) .map_or(true, |&observed_slot| slot > observed_slot) { self.newest_observed_slot.insert(event.clone(), slot); SlotOrder::New } else { SlotOrder::OutOfOrder }; let labels = ObservedSlotLabels { order, event }; self.observed_slot.get_or_create(&labels).inc(); if let Some(start) = self.first_observed_time_of_slot.get(&slot) { let latency = start.elapsed().as_secs_f64(); self.observed_slot_latency .get_or_create(&labels) .observe(latency); } else { self.first_observed_time_of_slot .insert(slot, Instant::now()); self.observed_slot_latency .get_or_create(&labels) .observe(0.0); } // Clear out old slots while self.first_observed_time_of_slot.len() > MAX_SLOT_OBSERVATIONS { let oldest_slot = *self.first_observed_time_of_slot.keys().next().unwrap(); self.first_observed_time_of_slot.remove(&oldest_slot); } } }