Skip to main content

icydb_core/metrics/sink/
events.rs

1//! Module: metrics::sink::events
2//! Responsibility: stable instrumentation event taxonomy for metrics sinks.
3//! Does not own: global metrics state mutation or report rendering.
4//! Boundary: exposes event enums consumed by metrics sinks and runtime instrumentation.
5
6use crate::error::{ErrorClass, InternalError};
7
8///
9/// ExecKind
10///
11
12#[derive(Clone, Copy, Debug)]
13#[remain::sorted]
14pub enum ExecKind {
15    Delete,
16    Load,
17    Save,
18}
19
20///
21/// ExecOutcome
22///
23
24#[derive(Clone, Copy, Debug)]
25#[remain::sorted]
26pub enum ExecOutcome {
27    Aborted,
28    ErrorConflict,
29    ErrorCorruption,
30    ErrorIncompatiblePersistedFormat,
31    ErrorInternal,
32    ErrorInvariantViolation,
33    ErrorNotFound,
34    ErrorUnsupported,
35    Success,
36}
37
38///
39/// CacheKind
40///
41
42#[derive(Clone, Copy, Debug)]
43#[remain::sorted]
44pub enum CacheKind {
45    SharedQueryPlan,
46    SqlCompiledCommand,
47}
48
49///
50/// CacheOutcome
51///
52
53#[derive(Clone, Copy, Debug)]
54#[remain::sorted]
55pub enum CacheOutcome {
56    Hit,
57    Insert,
58    Miss,
59}
60
61///
62/// CacheMissReason
63///
64/// Stable cache miss reason buckets for cache identities that already have a
65/// scoped entity path. These categories explain why a lookup missed without
66/// exposing query text, field names, or schema hashes in the metrics report.
67///
68
69#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70#[remain::sorted]
71pub enum CacheMissReason {
72    Cold,
73    DistinctKey,
74    SchemaFingerprint,
75    SchemaVersion,
76    Surface,
77    Visibility,
78}
79
80impl ExecOutcome {
81    // Map the crate's typed runtime error taxonomy into stable metrics buckets.
82    #[remain::check]
83    pub(super) const fn from_error(error: &InternalError) -> Self {
84        #[remain::sorted]
85        match error.class() {
86            ErrorClass::Conflict => Self::ErrorConflict,
87            ErrorClass::Corruption => Self::ErrorCorruption,
88            ErrorClass::IncompatiblePersistedFormat => Self::ErrorIncompatiblePersistedFormat,
89            ErrorClass::Internal => Self::ErrorInternal,
90            ErrorClass::InvariantViolation => Self::ErrorInvariantViolation,
91            ErrorClass::NotFound => Self::ErrorNotFound,
92            ErrorClass::Unsupported => Self::ErrorUnsupported,
93        }
94    }
95}
96
97///
98/// SaveMutationKind
99///
100
101#[derive(Clone, Copy, Debug)]
102#[remain::sorted]
103pub enum SaveMutationKind {
104    Insert,
105    Replace,
106    Update,
107}
108
109///
110/// MutationCommitClass
111///
112
113#[derive(Clone, Copy, Debug, Eq, PartialEq)]
114#[remain::sorted]
115pub enum MutationCommitClass {
116    DurableOnly,
117    LiveOnly,
118    MixedDurableAndLive,
119}
120
121///
122/// SchemaReconcileOutcome
123///
124/// Stable startup/metadata reconciliation outcomes for the schema trust
125/// boundary. The enum is intentionally low-cardinality so metrics can explain
126/// schema acceptance failures without exposing field names or diagnostic text.
127///
128
129#[derive(Clone, Copy, Debug, Eq, PartialEq)]
130#[remain::sorted]
131pub enum SchemaReconcileOutcome {
132    ExactMatch,
133    FirstCreate,
134    LatestSnapshotCorrupt,
135    RejectedFieldSlot,
136    RejectedOther,
137    RejectedRowLayout,
138    RejectedSchemaVersion,
139    StoreWriteError,
140}
141
142///
143/// SchemaTransitionOutcome
144///
145/// Stable schema transition policy buckets. These counters isolate the policy
146/// decision for an existing accepted snapshot from broader reconciliation
147/// outcomes such as first-create writes, corrupt stores, or store failures.
148///
149
150#[derive(Clone, Copy, Debug, Eq, PartialEq)]
151#[remain::sorted]
152pub enum SchemaTransitionOutcome {
153    AddExpressionIndex,
154    AddFieldPathIndex,
155    AppendOnlyFields,
156    ExactMatch,
157    MetadataOnlyFieldDefault,
158    MetadataOnlyIndexRename,
159    RejectedEntityIdentity,
160    RejectedFieldContract,
161    RejectedFieldSlot,
162    RejectedRowLayout,
163    RejectedSchemaVersion,
164    RejectedSnapshot,
165}
166
167///
168/// SqlCompileRejectPhase
169///
170/// Stable SQL compile rejection buckets. These counters identify the broad
171/// admission phase that rejected a SQL command without exposing SQL text,
172/// parser diagnostics, field names, or lowered query details.
173///
174
175#[derive(Clone, Copy, Debug, Eq, PartialEq)]
176#[remain::sorted]
177pub enum SqlCompileRejectPhase {
178    CacheKey,
179    Parse,
180    Semantic,
181}
182
183///
184/// SqlWriteKind
185///
186
187#[derive(Clone, Copy, Debug, Eq, PartialEq)]
188#[remain::sorted]
189pub enum SqlWriteKind {
190    Delete,
191    Insert,
192    InsertSelect,
193    Update,
194}
195
196///
197/// PlanKind
198///
199
200#[derive(Clone, Copy, Debug)]
201#[remain::sorted]
202pub enum PlanKind {
203    ByKey,
204    ByKeys,
205    FullScan,
206    IndexBranchSet,
207    IndexMultiLookup,
208    IndexPrefix,
209    IndexRange,
210    Intersection,
211    KeyRange,
212    Union,
213}
214
215///
216/// PlanChoiceReason
217///
218/// Stable selected-route reason buckets for non-index and primary-key access
219/// choices. These counters explain why a query did not land on a secondary
220/// index route without exposing predicates, literals, or index names.
221///
222
223#[derive(Clone, Copy, Debug, Eq, PartialEq)]
224#[remain::sorted]
225pub enum PlanChoiceReason {
226    ConflictingPrimaryKeyChildrenAccessPreferred,
227    ConstantFalsePredicate,
228    EmptyChildAccessPreferred,
229    FullScanAccess,
230    IntentKeyAccessOverride,
231    LimitZeroWindow,
232    NonIndexAccess,
233    PlannerCompositeNonIndex,
234    PlannerFullScanFallback,
235    PlannerKeySetAccess,
236    PlannerPrimaryKeyLookup,
237    PlannerPrimaryKeyRange,
238    RequiredOrderPrimaryKeyRangePreferred,
239    SingletonPrimaryKeyChildAccessPreferred,
240}
241
242///
243/// GroupedPlanExecutionMode
244///
245/// Canonical grouped-plan mode carried by metrics events.
246/// This keeps grouped metrics classification structured without routing
247/// through string codes that the sink would immediately decode again.
248///
249#[derive(Clone, Copy, Debug, Eq, PartialEq)]
250#[remain::sorted]
251pub enum GroupedPlanExecutionMode {
252    HashMaterialized,
253    OrderedStreaming,
254}
255
256///
257/// MetricsEvent
258///
259
260#[derive(Clone, Copy, Debug)]
261#[remain::sorted]
262pub enum MetricsEvent {
263    AcceptedSchemaFootprint {
264        entity_path: &'static str,
265        fields: u64,
266        nested_leaf_facts: u64,
267    },
268    Cache {
269        entity_path: &'static str,
270        kind: CacheKind,
271        outcome: CacheOutcome,
272    },
273    CacheEntries {
274        kind: CacheKind,
275        entries: u64,
276    },
277    CacheMissReason {
278        entity_path: &'static str,
279        kind: CacheKind,
280        reason: CacheMissReason,
281    },
282    ExecError {
283        kind: ExecKind,
284        entity_path: &'static str,
285        outcome: ExecOutcome,
286    },
287    ExecFinish {
288        kind: ExecKind,
289        entity_path: &'static str,
290        rows_touched: u64,
291        inst_delta: u64,
292        outcome: ExecOutcome,
293    },
294    ExecStart {
295        kind: ExecKind,
296        entity_path: &'static str,
297    },
298    IndexDelta {
299        entity_path: &'static str,
300        inserts: u64,
301        removes: u64,
302    },
303    LoadRowEfficiency {
304        entity_path: &'static str,
305        candidate_rows_scanned: u64,
306        candidate_rows_filtered: u64,
307        result_rows_emitted: u64,
308    },
309    MutationCommitPlan {
310        entity_path: &'static str,
311        class: MutationCommitClass,
312    },
313    NonAtomicPartialCommit {
314        entity_path: &'static str,
315        committed_rows: u64,
316    },
317    Plan {
318        entity_path: &'static str,
319        kind: PlanKind,
320        grouped_execution_mode: Option<GroupedPlanExecutionMode>,
321    },
322    PlanChoice {
323        entity_path: &'static str,
324        reason: PlanChoiceReason,
325    },
326    PreparedShapeAlreadyFinalized {
327        entity_path: &'static str,
328    },
329    RelationValidation {
330        entity_path: &'static str,
331        reverse_lookups: u64,
332        blocked_deletes: u64,
333    },
334    ReverseIndexDelta {
335        entity_path: &'static str,
336        inserts: u64,
337        removes: u64,
338    },
339    RowsAggregated {
340        entity_path: &'static str,
341        rows_aggregated: u64,
342    },
343    RowsEmitted {
344        entity_path: &'static str,
345        rows_emitted: u64,
346    },
347    RowsFiltered {
348        entity_path: &'static str,
349        rows_filtered: u64,
350    },
351    RowsScanned {
352        entity_path: &'static str,
353        rows_scanned: u64,
354    },
355    SaveMutation {
356        entity_path: &'static str,
357        kind: SaveMutationKind,
358        rows_touched: u64,
359    },
360    SchemaReconcile {
361        entity_path: &'static str,
362        outcome: SchemaReconcileOutcome,
363    },
364    SchemaStoreFootprint {
365        encoded_bytes: u64,
366        entity_path: &'static str,
367        latest_snapshot_bytes: u64,
368        snapshots: u64,
369    },
370    SchemaTransition {
371        entity_path: &'static str,
372        outcome: SchemaTransitionOutcome,
373    },
374    SqlCompileReject {
375        entity_path: &'static str,
376        phase: SqlCompileRejectPhase,
377    },
378    SqlWrite {
379        entity_path: &'static str,
380        kind: SqlWriteKind,
381        staged_rows: u64,
382        matched_rows: u64,
383        mutated_rows: u64,
384        returning_rows: u64,
385    },
386    SqlWriteError {
387        entity_path: &'static str,
388        kind: SqlWriteKind,
389        class: ErrorClass,
390    },
391    UniqueViolation {
392        entity_path: &'static str,
393    },
394}