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    ConstraintActivation,
157    ExactMatch,
158    MetadataOnlyFieldDefault,
159    MetadataOnlyIndexRename,
160    RejectedEntityIdentity,
161    RejectedFieldContract,
162    RejectedFieldSlot,
163    RejectedRowLayout,
164    RejectedSchemaVersion,
165    RejectedSnapshot,
166}
167
168///
169/// SqlCompileRejectPhase
170///
171/// Stable SQL compile rejection buckets. These counters identify the broad
172/// admission phase that rejected a SQL command without exposing SQL text,
173/// parser diagnostics, field names, or lowered query details.
174///
175
176#[derive(Clone, Copy, Debug, Eq, PartialEq)]
177#[remain::sorted]
178pub enum SqlCompileRejectPhase {
179    CacheKey,
180    Parse,
181    Semantic,
182}
183
184///
185/// SqlWriteKind
186///
187
188#[derive(Clone, Copy, Debug, Eq, PartialEq)]
189#[remain::sorted]
190pub enum SqlWriteKind {
191    Delete,
192    Insert,
193    InsertSelect,
194    Update,
195}
196
197///
198/// PlanKind
199///
200
201#[derive(Clone, Copy, Debug)]
202#[remain::sorted]
203pub enum PlanKind {
204    ByKey,
205    ByKeys,
206    FullScan,
207    IndexBranchSet,
208    IndexMultiLookup,
209    IndexPrefix,
210    IndexRange,
211    Intersection,
212    KeyRange,
213    Union,
214}
215
216///
217/// PlanChoiceReason
218///
219/// Stable selected-route reason buckets for non-index and primary-key access
220/// choices. These counters explain why a query did not land on a secondary
221/// index route without exposing predicates, literals, or index names.
222///
223
224#[derive(Clone, Copy, Debug, Eq, PartialEq)]
225#[remain::sorted]
226pub enum PlanChoiceReason {
227    ConflictingPrimaryKeyChildrenAccessPreferred,
228    ConstantFalsePredicate,
229    EmptyChildAccessPreferred,
230    FullScanAccess,
231    IntentKeyAccessOverride,
232    LimitZeroWindow,
233    NonIndexAccess,
234    PlannerCompositeNonIndex,
235    PlannerFullScanFallback,
236    PlannerKeySetAccess,
237    PlannerPrimaryKeyLookup,
238    PlannerPrimaryKeyRange,
239    RequiredOrderPrimaryKeyRangePreferred,
240    SingletonPrimaryKeyChildAccessPreferred,
241}
242
243///
244/// GroupedPlanExecutionMode
245///
246/// Canonical grouped-plan mode carried by metrics events.
247/// This keeps grouped metrics classification structured without routing
248/// through string codes that the sink would immediately decode again.
249///
250#[derive(Clone, Copy, Debug, Eq, PartialEq)]
251#[remain::sorted]
252pub enum GroupedPlanExecutionMode {
253    HashMaterialized,
254    OrderedStreaming,
255}
256
257///
258/// MetricsEvent
259///
260
261#[derive(Clone, Copy, Debug)]
262#[remain::sorted]
263pub enum MetricsEvent {
264    AcceptedSchemaFootprint {
265        entity_path: &'static str,
266        fields: u64,
267        nested_leaf_facts: u64,
268    },
269    Cache {
270        entity_path: &'static str,
271        kind: CacheKind,
272        outcome: CacheOutcome,
273    },
274    CacheEntries {
275        kind: CacheKind,
276        entries: u64,
277    },
278    CacheMissReason {
279        entity_path: &'static str,
280        kind: CacheKind,
281        reason: CacheMissReason,
282    },
283    ExecError {
284        kind: ExecKind,
285        entity_path: &'static str,
286        outcome: ExecOutcome,
287    },
288    ExecFinish {
289        kind: ExecKind,
290        entity_path: &'static str,
291        rows_touched: u64,
292        inst_delta: u64,
293        outcome: ExecOutcome,
294    },
295    ExecStart {
296        kind: ExecKind,
297        entity_path: &'static str,
298    },
299    IndexDelta {
300        entity_path: &'static str,
301        inserts: u64,
302        removes: u64,
303    },
304    LoadRowEfficiency {
305        entity_path: &'static str,
306        candidate_rows_scanned: u64,
307        candidate_rows_filtered: u64,
308        result_rows_emitted: u64,
309    },
310    MutationCommitPlan {
311        entity_path: &'static str,
312        class: MutationCommitClass,
313    },
314    NonAtomicPartialCommit {
315        entity_path: &'static str,
316        committed_rows: u64,
317    },
318    Plan {
319        entity_path: &'static str,
320        kind: PlanKind,
321        grouped_execution_mode: Option<GroupedPlanExecutionMode>,
322    },
323    PlanChoice {
324        entity_path: &'static str,
325        reason: PlanChoiceReason,
326    },
327    PreparedShapeAlreadyFinalized {
328        entity_path: &'static str,
329    },
330    RelationValidation {
331        entity_path: &'static str,
332        reverse_lookups: u64,
333        blocked_deletes: u64,
334    },
335    ReverseIndexDelta {
336        entity_path: &'static str,
337        inserts: u64,
338        removes: u64,
339    },
340    RowsAggregated {
341        entity_path: &'static str,
342        rows_aggregated: u64,
343    },
344    RowsEmitted {
345        entity_path: &'static str,
346        rows_emitted: u64,
347    },
348    RowsFiltered {
349        entity_path: &'static str,
350        rows_filtered: u64,
351    },
352    RowsScanned {
353        entity_path: &'static str,
354        rows_scanned: u64,
355    },
356    SaveMutation {
357        entity_path: &'static str,
358        kind: SaveMutationKind,
359        rows_touched: u64,
360    },
361    SchemaReconcile {
362        entity_path: &'static str,
363        outcome: SchemaReconcileOutcome,
364    },
365    SchemaStoreFootprint {
366        encoded_bytes: u64,
367        entity_path: &'static str,
368        latest_snapshot_bytes: u64,
369        snapshots: u64,
370    },
371    SchemaTransition {
372        entity_path: &'static str,
373        outcome: SchemaTransitionOutcome,
374    },
375    SqlCompileReject {
376        entity_path: &'static str,
377        phase: SqlCompileRejectPhase,
378    },
379    SqlWrite {
380        entity_path: &'static str,
381        kind: SqlWriteKind,
382        staged_rows: u64,
383        matched_rows: u64,
384        mutated_rows: u64,
385        returning_rows: u64,
386    },
387    SqlWriteError {
388        entity_path: &'static str,
389        kind: SqlWriteKind,
390        class: ErrorClass,
391    },
392    UniqueViolation {
393        entity_path: &'static str,
394    },
395}