query_runtime/observability/graph_observation.rs
1//! Measured graph-execution observation.
2//!
3//! These rows record what a graph attempt did on the device. They
4//! carry no predicted or budgeted bytes: the allocator enforces a query's
5//! limit, so a planning-time claim would be an estimate masquerading as a
6//! fact. Keeping only the measurement leaves future capacity decisions with
7//! evidence. No model is stored to calibrate.
8
9use query_planning::{correlation::QueryId, plan::GraphAlgorithm};
10use serde_json::{Value, json};
11
12/// Direct source for one graph attempt's measured device-memory peak.
13///
14/// Consumers must not infer a source from a missing value or an error string.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum GraphMeasuredPeakProvenance {
17 /// An exclusive resident-graph cache scope reported its own allocator
18 /// high-water mark after the warm graph execution had retired. This is
19 /// the only physical peak source for work that runs on the cache runtime.
20 /// Work on the admitted attempt runtime does not take this path.
21 CacheScopeHighWater,
22 /// Neither runtime measurement completed. `measured_peak_bytes` is
23 /// unavailable. No synthetic zero stands in for it.
24 Unavailable,
25}
26
27impl GraphMeasuredPeakProvenance {
28 /// Stable label used by bounded public projections.
29 pub const fn as_str(self) -> &'static str {
30 match self {
31 Self::CacheScopeHighWater => "cache_scope_high_water",
32 Self::Unavailable => "unavailable",
33 }
34 }
35}
36
37impl std::fmt::Display for GraphMeasuredPeakProvenance {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.write_str(self.as_str())
40 }
41}
42
43/// Engine-owned lifecycle state at the graph executor boundary.
44///
45/// A row exists before the executor is entered, so a missing terminal state is
46/// deliberately fail-closed and is not execution evidence.
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48pub enum GraphClaimExecutionState {
49 /// The row was recorded but no exact terminal graph boundary was observed.
50 Pending,
51 /// The native graph algorithm executor was entered.
52 AlgorithmStarted,
53}
54
55impl GraphClaimExecutionState {
56 /// Stable label used by bounded public projections.
57 pub const fn as_str(self) -> &'static str {
58 match self {
59 Self::Pending => "pending",
60 Self::AlgorithmStarted => "algorithm_started",
61 }
62 }
63}
64
65impl std::fmt::Display for GraphClaimExecutionState {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 f.write_str(self.as_str())
68 }
69}
70
71/// Fixed-shape measured-peak observation for one graph attempt.
72///
73/// The engine creates one of these before it enters the graph executor and
74/// fills the runtime measurement when that attempt leaves it. It is
75/// diagnostic only: the row cannot mint admission evidence or modify a device
76/// grant. `node_index` is typed executor identity; report consumers should use
77/// `node_id`.
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct GraphMeasuredPeakRow {
80 /// Query-wide diagnostic correlation id, when the execution context has
81 /// one. The runtime metrics owner assigns this sequence.
82 pub query_id: Option<QueryId>,
83 /// Stable native graph node id for reports.
84 pub node_id: String,
85 /// Typed graph node identity used to correlate the post-execution sample
86 /// without parsing a report string.
87 pub node_index: usize,
88 /// The graph algorithm that owned this measured peak.
89 pub algorithm: GraphAlgorithm,
90 /// Exact engine state at the graph executor boundary.
91 pub execution_state: GraphClaimExecutionState,
92 /// Measured graph-stage peak bytes when a runtime observation completed.
93 pub measured_peak_bytes: Option<u64>,
94 /// Source of [`Self::measured_peak_bytes`].
95 pub measured_peak_provenance: GraphMeasuredPeakProvenance,
96}
97
98impl GraphMeasuredPeakRow {
99 /// Opens a pending observation for one graph plan node.
100 pub fn pending(
101 query_id: Option<QueryId>,
102 node_id: impl Into<String>,
103 node_index: usize,
104 algorithm: GraphAlgorithm,
105 ) -> Self {
106 Self {
107 query_id,
108 node_id: node_id.into(),
109 node_index,
110 algorithm,
111 execution_state: GraphClaimExecutionState::Pending,
112 measured_peak_bytes: None,
113 measured_peak_provenance: GraphMeasuredPeakProvenance::Unavailable,
114 }
115 }
116
117 /// Records one terminal graph-boundary state. A later observation cannot
118 /// overwrite the first exact boundary.
119 pub(crate) fn record_execution_state(&mut self, execution_state: GraphClaimExecutionState) {
120 if self.execution_state == GraphClaimExecutionState::Pending {
121 self.execution_state = execution_state;
122 }
123 }
124
125 /// Records one terminal graph-boundary state, for tests that build rows
126 /// without an execution.
127 #[cfg(any(test, feature = "test-hooks"))]
128 #[doc(hidden)]
129 pub fn record_execution_state_for_test(&mut self, execution_state: GraphClaimExecutionState) {
130 self.record_execution_state(execution_state);
131 }
132
133 /// Records the one direct peak observation for this graph attempt.
134 ///
135 /// An unavailable source always clears the measured value. A zero
136 /// high-water mark remains an observed zero.
137 pub fn record_measured_peak(
138 &mut self,
139 measured_peak_bytes: Option<u64>,
140 measured_peak_provenance: GraphMeasuredPeakProvenance,
141 ) {
142 match measured_peak_provenance {
143 GraphMeasuredPeakProvenance::Unavailable => self.measured_peak_bytes = None,
144 GraphMeasuredPeakProvenance::CacheScopeHighWater => self.measured_peak_bytes = measured_peak_bytes,
145 }
146 self.measured_peak_provenance = measured_peak_provenance;
147 }
148
149 /// Serializes this measured-peak row as named JSON.
150 pub fn to_json(&self) -> Value {
151 json!({
152 "query_id": self.query_id.as_ref().map(QueryId::as_str),
153 "node_id": self.node_id,
154 "node_index": self.node_index,
155 "algorithm": self.algorithm.as_str(),
156 "execution_state": self.execution_state.as_str(),
157 "measured_peak_bytes": self.measured_peak_bytes,
158 "measured_peak_provenance": self.measured_peak_provenance.as_str(),
159 })
160 }
161}