solana_metrics/
poh_timing_point.rs

1//! A poh_timing_point module
2
3use {
4    crossbeam_channel::{Receiver, Sender},
5    log::*,
6    solana_clock::Slot,
7    std::fmt,
8};
9
10/// Receiver of SlotPohTimingInfo from the channel
11pub type PohTimingReceiver = Receiver<SlotPohTimingInfo>;
12
13/// Sender of SlotPohTimingInfo to the channel
14pub type PohTimingSender = Sender<SlotPohTimingInfo>;
15
16/// PohTimingPoint. Each TimingPoint is annotated with a timestamp in milliseconds.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum PohTimingPoint {
19    PohSlotStart(u64),
20    PohSlotEnd(u64),
21    FullSlotReceived(u64),
22}
23
24impl fmt::Display for PohTimingPoint {
25    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26        match *self {
27            PohTimingPoint::PohSlotStart(t) => write!(f, "poh_start={t}"),
28            PohTimingPoint::PohSlotEnd(t) => write!(f, "poh_end  ={t}"),
29            PohTimingPoint::FullSlotReceived(t) => write!(f, "poh_full ={t}"),
30        }
31    }
32}
33
34/// SlotPohTimingInfo. This struct is sent to channel and received by
35/// poh_timing_report service.
36#[derive(Clone, Debug)]
37pub struct SlotPohTimingInfo {
38    /// current slot
39    pub slot: Slot,
40    /// root slot
41    pub root_slot: Option<Slot>,
42    /// timing event
43    pub timing_point: PohTimingPoint,
44}
45
46impl fmt::Display for SlotPohTimingInfo {
47    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48        write!(
49            f,
50            "PohTimingPoint: {}, slot={}, root_slot={}",
51            self.timing_point,
52            self.slot,
53            self.root_slot.unwrap_or(0),
54        )
55    }
56}
57
58impl SlotPohTimingInfo {
59    /// create slot start poh timing point
60    pub fn new_slot_start_poh_time_point(
61        slot: Slot,
62        root_slot: Option<Slot>,
63        timestamp: u64,
64    ) -> SlotPohTimingInfo {
65        SlotPohTimingInfo {
66            slot,
67            root_slot,
68            timing_point: PohTimingPoint::PohSlotStart(timestamp),
69        }
70    }
71
72    /// create slot end poh timing point
73    pub fn new_slot_end_poh_time_point(
74        slot: Slot,
75        root_slot: Option<Slot>,
76        timestamp: u64,
77    ) -> SlotPohTimingInfo {
78        SlotPohTimingInfo {
79            slot,
80            root_slot,
81            timing_point: PohTimingPoint::PohSlotEnd(timestamp),
82        }
83    }
84
85    /// create slot full poh timing point
86    pub fn new_slot_full_poh_time_point(
87        slot: Slot,
88        root_slot: Option<Slot>,
89        timestamp: u64,
90    ) -> SlotPohTimingInfo {
91        SlotPohTimingInfo {
92            slot,
93            root_slot,
94            timing_point: PohTimingPoint::FullSlotReceived(timestamp),
95        }
96    }
97}
98
99/// send poh timing to channel
100pub fn send_poh_timing_point(sender: &PohTimingSender, slot_timing: SlotPohTimingInfo) {
101    trace!("{}", slot_timing);
102    if let Err(e) = sender.try_send(slot_timing) {
103        info!("failed to send slot poh timing {:?}", e);
104    }
105}
106
107#[cfg(test)]
108mod test {
109    use super::*;
110    #[test]
111    fn test_poh_timing_point() {
112        // create slot start with root
113        let p = SlotPohTimingInfo::new_slot_start_poh_time_point(100, Some(101), 100);
114        assert!(p.slot == 100);
115        assert_eq!(p.root_slot, Some(101));
116        assert_eq!(p.timing_point, PohTimingPoint::PohSlotStart(100));
117        assert_eq!(
118            format!("{p}"),
119            "PohTimingPoint: poh_start=100, slot=100, root_slot=101"
120        );
121
122        // create slot start without root
123        let p = SlotPohTimingInfo::new_slot_start_poh_time_point(100, None, 100);
124        assert!(p.slot == 100);
125        assert_eq!(p.root_slot, None);
126        assert_eq!(p.timing_point, PohTimingPoint::PohSlotStart(100));
127        assert_eq!(
128            format!("{p}"),
129            "PohTimingPoint: poh_start=100, slot=100, root_slot=0"
130        );
131
132        // create slot end with root
133        let p = SlotPohTimingInfo::new_slot_end_poh_time_point(100, Some(101), 100);
134        assert!(p.slot == 100);
135        assert_eq!(p.root_slot, Some(101));
136        assert_eq!(p.timing_point, PohTimingPoint::PohSlotEnd(100));
137        assert_eq!(
138            format!("{p}"),
139            "PohTimingPoint: poh_end  =100, slot=100, root_slot=101"
140        );
141
142        // create slot end without root
143        let p = SlotPohTimingInfo::new_slot_end_poh_time_point(100, None, 100);
144        assert!(p.slot == 100);
145        assert_eq!(p.root_slot, None);
146        assert_eq!(p.timing_point, PohTimingPoint::PohSlotEnd(100));
147        assert_eq!(
148            format!("{p}"),
149            "PohTimingPoint: poh_end  =100, slot=100, root_slot=0"
150        );
151
152        // create slot full with root
153        let p = SlotPohTimingInfo::new_slot_full_poh_time_point(100, Some(101), 100);
154        assert!(p.slot == 100);
155        assert_eq!(p.root_slot, Some(101));
156        assert_eq!(p.timing_point, PohTimingPoint::FullSlotReceived(100));
157        assert_eq!(
158            format!("{p}"),
159            "PohTimingPoint: poh_full =100, slot=100, root_slot=101"
160        );
161
162        // create slot full without root
163        let p = SlotPohTimingInfo::new_slot_full_poh_time_point(100, None, 100);
164        assert!(p.slot == 100);
165        assert_eq!(p.root_slot, None);
166        assert_eq!(p.timing_point, PohTimingPoint::FullSlotReceived(100));
167
168        assert_eq!(
169            format!("{p}"),
170            "PohTimingPoint: poh_full =100, slot=100, root_slot=0"
171        );
172    }
173}