icydb_core/db/diagnostics/
execution_trace.rs1use crate::db::{executor::ExecutionOptimization, query::plan::OrderDirection};
7
8#[cfg_attr(
9 doc,
10 doc = "ExecutionAccessPathVariant\n\nCoarse access path shape recorded in execution traces."
11)]
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub enum ExecutionAccessPathVariant {
14 ByKey,
15 ByKeys,
16 KeyRange,
17 IndexPrefix,
18 IndexMultiLookup,
19 IndexRange,
20 FullScan,
21 Union,
22 Intersection,
23}
24
25#[cfg_attr(
26 doc,
27 doc = "ExecutionTrace\n\nStructured execution trace snapshot for one load path.\nCaptures plan shape and counters without affecting behavior."
28)]
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub struct ExecutionTrace {
31 pub(crate) access_path_variant: ExecutionAccessPathVariant,
32 pub(crate) direction: OrderDirection,
33 pub(crate) optimization: Option<ExecutionOptimization>,
34 pub(crate) keys_scanned: u64,
35 pub(crate) rows_materialized: u64,
36 pub(crate) rows_returned: u64,
37 pub(crate) execution_time_micros: u64,
38 pub(crate) index_only: bool,
39 pub(crate) continuation_applied: bool,
40 pub(crate) index_predicate_applied: bool,
41 pub(crate) index_predicate_keys_rejected: u64,
42 pub(crate) distinct_keys_deduped: u64,
43}
44
45#[cfg_attr(
46 doc,
47 doc = "ExecutionMetrics\n\nCompact metrics view derived from one `ExecutionTrace`.\nKept small for lightweight observability surfaces."
48)]
49#[derive(Clone, Copy, Debug, Eq, PartialEq)]
50pub struct ExecutionMetrics {
51 pub(crate) rows_scanned: u64,
52 pub(crate) rows_materialized: u64,
53 pub(crate) execution_time_micros: u64,
54 pub(crate) index_only: bool,
55}
56
57impl ExecutionTrace {
58 #[must_use]
60 pub(crate) const fn new_from_variant(
61 access_path_variant: ExecutionAccessPathVariant,
62 direction: OrderDirection,
63 continuation_applied: bool,
64 ) -> Self {
65 Self {
66 access_path_variant,
67 direction,
68 optimization: None,
69 keys_scanned: 0,
70 rows_materialized: 0,
71 rows_returned: 0,
72 execution_time_micros: 0,
73 index_only: false,
74 continuation_applied,
75 index_predicate_applied: false,
76 index_predicate_keys_rejected: 0,
77 distinct_keys_deduped: 0,
78 }
79 }
80
81 #[expect(clippy::too_many_arguments)]
83 pub(in crate::db) fn set_path_outcome(
84 &mut self,
85 optimization: Option<ExecutionOptimization>,
86 keys_scanned: usize,
87 rows_materialized: usize,
88 rows_returned: usize,
89 execution_time_micros: u64,
90 index_only: bool,
91 index_predicate_applied: bool,
92 index_predicate_keys_rejected: u64,
93 distinct_keys_deduped: u64,
94 ) {
95 self.optimization = optimization;
96 self.keys_scanned = u64::try_from(keys_scanned).unwrap_or(u64::MAX);
97 self.rows_materialized = u64::try_from(rows_materialized).unwrap_or(u64::MAX);
98 self.rows_returned = u64::try_from(rows_returned).unwrap_or(u64::MAX);
99 self.execution_time_micros = execution_time_micros;
100 self.index_only = index_only;
101 self.index_predicate_applied = index_predicate_applied;
102 self.index_predicate_keys_rejected = index_predicate_keys_rejected;
103 self.distinct_keys_deduped = distinct_keys_deduped;
104 debug_assert_eq!(
105 self.keys_scanned,
106 u64::try_from(keys_scanned).unwrap_or(u64::MAX),
107 "execution trace keys_scanned must match rows_scanned metrics input",
108 );
109 }
110
111 #[must_use]
113 pub const fn metrics(&self) -> ExecutionMetrics {
114 ExecutionMetrics {
115 rows_scanned: self.keys_scanned,
116 rows_materialized: self.rows_materialized,
117 execution_time_micros: self.execution_time_micros,
118 index_only: self.index_only,
119 }
120 }
121
122 #[must_use]
124 pub const fn access_path_variant(&self) -> ExecutionAccessPathVariant {
125 self.access_path_variant
126 }
127
128 #[must_use]
130 pub const fn direction(&self) -> OrderDirection {
131 self.direction
132 }
133
134 #[must_use]
136 pub const fn optimization(&self) -> Option<ExecutionOptimization> {
137 self.optimization
138 }
139
140 #[must_use]
142 pub const fn keys_scanned(&self) -> u64 {
143 self.keys_scanned
144 }
145
146 #[must_use]
148 pub const fn rows_materialized(&self) -> u64 {
149 self.rows_materialized
150 }
151
152 #[must_use]
154 pub const fn rows_returned(&self) -> u64 {
155 self.rows_returned
156 }
157
158 #[must_use]
160 pub const fn execution_time_micros(&self) -> u64 {
161 self.execution_time_micros
162 }
163
164 #[must_use]
166 pub const fn index_only(&self) -> bool {
167 self.index_only
168 }
169
170 #[must_use]
172 pub const fn continuation_applied(&self) -> bool {
173 self.continuation_applied
174 }
175
176 #[must_use]
178 pub const fn index_predicate_applied(&self) -> bool {
179 self.index_predicate_applied
180 }
181
182 #[must_use]
184 pub const fn index_predicate_keys_rejected(&self) -> u64 {
185 self.index_predicate_keys_rejected
186 }
187
188 #[must_use]
190 pub const fn distinct_keys_deduped(&self) -> u64 {
191 self.distinct_keys_deduped
192 }
193}
194
195impl ExecutionMetrics {
196 #[must_use]
198 pub const fn rows_scanned(&self) -> u64 {
199 self.rows_scanned
200 }
201
202 #[must_use]
204 pub const fn rows_materialized(&self) -> u64 {
205 self.rows_materialized
206 }
207
208 #[must_use]
210 pub const fn execution_time_micros(&self) -> u64 {
211 self.execution_time_micros
212 }
213
214 #[must_use]
216 pub const fn index_only(&self) -> bool {
217 self.index_only
218 }
219}