Skip to main content

nodedb_types/
diagnostic.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Canonical diagnostic-layer taxonomy for end-to-end probes.
4//!
5//! Smoke probes and QA harnesses assert against these names so a layer
6//! mismatch is unambiguous regardless of which engine emitted it.
7
8use serde::{Deserialize, Serialize};
9
10/// Stable layer identifier surfaced in error spans, structured logs, and
11/// probe diagnostic reports. New variants are additive — readers must
12/// not rely on the variant set being closed.
13#[derive(
14    Debug,
15    Clone,
16    Copy,
17    PartialEq,
18    Eq,
19    Hash,
20    Serialize,
21    Deserialize,
22    zerompk::ToMessagePack,
23    zerompk::FromMessagePack,
24)]
25#[msgpack(c_enum)]
26#[serde(rename_all = "snake_case")]
27pub enum DiagnosticLayer {
28    /// The persistent edge store (per `(tenant, collection)`).
29    EdgeStore,
30    /// The in-memory CSR adjacency partitions on Data Plane cores.
31    Csr,
32    /// The serialization shape returned to clients (the bug class from
33    /// the original graph-traverse wire-shape investigation).
34    WireShape,
35    /// The end-to-end write path from client through WAL to commit.
36    WritePath,
37    /// Cross-node replication / Raft log application.
38    Replication,
39}
40
41impl DiagnosticLayer {
42    /// Stable snake_case name suitable for logs, span tags, and JSON.
43    pub const fn as_str(self) -> &'static str {
44        match self {
45            Self::EdgeStore => "edge_store",
46            Self::Csr => "csr",
47            Self::WireShape => "wire_shape",
48            Self::WritePath => "write_path",
49            Self::Replication => "replication",
50        }
51    }
52}
53
54impl std::fmt::Display for DiagnosticLayer {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        f.write_str(self.as_str())
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn snake_case_names_are_stable() {
66        assert_eq!(DiagnosticLayer::EdgeStore.as_str(), "edge_store");
67        assert_eq!(DiagnosticLayer::Csr.as_str(), "csr");
68        assert_eq!(DiagnosticLayer::WireShape.as_str(), "wire_shape");
69        assert_eq!(DiagnosticLayer::WritePath.as_str(), "write_path");
70        assert_eq!(DiagnosticLayer::Replication.as_str(), "replication");
71    }
72
73    #[test]
74    fn display_matches_as_str() {
75        for v in [
76            DiagnosticLayer::EdgeStore,
77            DiagnosticLayer::Csr,
78            DiagnosticLayer::WireShape,
79            DiagnosticLayer::WritePath,
80            DiagnosticLayer::Replication,
81        ] {
82            assert_eq!(format!("{v}"), v.as_str());
83        }
84    }
85
86    #[test]
87    fn serde_round_trip_snake_case() {
88        let v = DiagnosticLayer::EdgeStore;
89        let s = sonic_rs::to_string(&v).unwrap();
90        assert_eq!(s, "\"edge_store\"");
91        let back: DiagnosticLayer = sonic_rs::from_str(&s).unwrap();
92        assert_eq!(back, v);
93    }
94}