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