1use super::{
2 DiskScratchPolicy, EvaluationIncompleteReason, FormulaPlaneMode, ResourceLedgerSnapshot,
3};
4use formualizer_common::ResourceExhaustionReason;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[non_exhaustive]
10pub enum EvaluationResourceClass {
11 SemanticFormat,
12 Admission,
13 RetainedMemory,
14 ScratchMemory,
15 WorkTime,
16 Optimization,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21#[non_exhaustive]
22pub enum EvaluationResourceReason {
23 FormulaPlaneTopologyCandidates,
24 FormulaPlaneTopologyEdges,
25 FormulaPlaneTopologyRetainedBytes,
26 FormulaPlaneMaterializationCells,
27 FormulaReplayEncodedBytes,
28 FormulaReplayMemoryBytes,
29 FormulaReplayDiskBytes,
30 FormulaReplayFiles,
31 EvaluationCancelled,
32 EvaluationError,
33}
34
35impl EvaluationResourceClass {
36 pub const fn as_str(self) -> &'static str {
37 match self {
38 Self::SemanticFormat => "semantic_format",
39 Self::Admission => "admission",
40 Self::RetainedMemory => "retained_memory",
41 Self::ScratchMemory => "scratch_memory",
42 Self::WorkTime => "work_time",
43 Self::Optimization => "optimization",
44 }
45 }
46}
47
48impl EvaluationResourceReason {
49 pub const fn class(self) -> EvaluationResourceClass {
50 match self {
51 Self::FormulaPlaneTopologyCandidates | Self::FormulaPlaneTopologyEdges => {
52 EvaluationResourceClass::Optimization
53 }
54 Self::FormulaPlaneTopologyRetainedBytes => EvaluationResourceClass::RetainedMemory,
55 Self::FormulaPlaneMaterializationCells
56 | Self::FormulaReplayEncodedBytes
57 | Self::FormulaReplayDiskBytes
58 | Self::FormulaReplayFiles => EvaluationResourceClass::Admission,
59 Self::FormulaReplayMemoryBytes => EvaluationResourceClass::ScratchMemory,
60 Self::EvaluationCancelled | Self::EvaluationError => EvaluationResourceClass::WorkTime,
61 }
62 }
63
64 pub const fn as_str(self) -> &'static str {
65 match self {
66 Self::FormulaPlaneTopologyCandidates => "formula_plane_topology_candidates",
67 Self::FormulaPlaneTopologyEdges => "formula_plane_topology_edges",
68 Self::FormulaPlaneTopologyRetainedBytes => "formula_plane_topology_retained_bytes",
69 Self::FormulaPlaneMaterializationCells => "formula_plane_materialization_cells",
70 Self::FormulaReplayEncodedBytes => "formula_replay_encoded_bytes",
71 Self::FormulaReplayMemoryBytes => "formula_replay_memory_bytes",
72 Self::FormulaReplayDiskBytes => "formula_replay_disk_bytes",
73 Self::FormulaReplayFiles => "formula_replay_files",
74 Self::EvaluationCancelled => "evaluation_cancelled",
75 Self::EvaluationError => "evaluation_error",
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
81#[non_exhaustive]
82pub enum EvaluationRequestKind {
83 Vertex,
84 Targeted,
85 TargetPreparation,
86 RecalcPlan,
87 #[default]
88 Full,
89 FullWithDelta,
90 Cell,
91 Cells,
92 CellsCancellable,
93 CellsWithDelta,
94 FullCancellable,
95 TargetedCancellable,
96 FullLogged,
97}
98
99#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
100#[non_exhaustive]
101pub enum EvaluationRequestOutcome {
102 #[default]
103 InProgress,
104 Success,
105 Cancelled,
106 Error,
107}
108
109#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
110#[non_exhaustive]
111pub enum FormulaPlaneTopologyStrategy {
112 #[default]
113 NotUsed,
114 Legacy,
115 SkippedNoActiveSpans,
116 SkippedNoDirtyWork,
117 Cached,
118 CompiledAndCached,
119 ExactPagedIndexed,
120 ExactInMemoryRuns,
121 ExactNativeScratch,
122 ExactRepeatedPasses,
123 CapacityFallbackMaterialization,
124}
125
126#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
127#[non_exhaustive]
128pub enum FormulaPlaneTopologyCacheOutcome {
129 #[default]
130 NotUsed,
131 Hit,
132 Built,
133 SkippedOverflow,
134 SkippedDynamicLegacy,
135}
136
137#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
138#[non_exhaustive]
139pub enum FormulaDirtyLeaseOutcome {
140 #[default]
141 NotAcquired,
142 Acquired,
143 Empty,
144 Acknowledged,
145 AcknowledgedPartial,
146 AcknowledgedEmpty,
147 RetainedOnCancellation,
148 RetainedOnError,
149}
150
151#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
155#[non_exhaustive]
156pub enum FormulaPlaneRoute {
157 #[default]
158 GlobalMixed,
159 ContractedLegacyIsland,
160}
161
162#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
163#[non_exhaustive]
164pub enum FormulaPlaneRoutePhase {
165 #[default]
166 Planned,
167 Executed,
168}
169
170#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
171#[non_exhaustive]
172pub enum FormulaPlaneRouteTransitionReason {
173 #[default]
174 ProvenIsolated,
175 DynamicReference,
176 NamedDependency,
177 SpillOrArray,
178 StructuralSummaryUncertain,
179 UnsupportedReadSummary,
180 BoundaryDiscoveryOverflow,
181 SpanCycleDemotion,
182 RuntimeReplan,
183}
184
185pub const FORMULA_PLANE_ROUTE_EVENT_CAPACITY: usize = 16;
186pub const FORMULA_PLANE_ROUTE_EVENT_SHEET_CAPACITY: usize = 8;
187
188#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
189#[non_exhaustive]
190pub struct FormulaPlaneRouteEvent {
191 pub island_id: u64,
194 pub phase: FormulaPlaneRoutePhase,
195 pub route: FormulaPlaneRoute,
196 pub sheet_ids: [u16; FORMULA_PLANE_ROUTE_EVENT_SHEET_CAPACITY],
199 pub sheet_count: u8,
200 pub authority_epoch: u64,
202 pub index_epoch: u64,
204 pub transition_reason: FormulaPlaneRouteTransitionReason,
205 pub demotion_generation: u32,
206 pub replan_generation: u32,
207}
208
209impl EvaluationRequestKind {
210 pub const fn as_str(self) -> &'static str {
211 match self {
212 Self::Vertex => "vertex",
213 Self::Targeted => "targeted",
214 Self::TargetPreparation => "target_preparation",
215 Self::RecalcPlan => "recalc_plan",
216 Self::Full => "full",
217 Self::FullWithDelta => "full_with_delta",
218 Self::Cell => "cell",
219 Self::Cells => "cells",
220 Self::CellsCancellable => "cells_cancellable",
221 Self::CellsWithDelta => "cells_with_delta",
222 Self::FullCancellable => "full_cancellable",
223 Self::TargetedCancellable => "targeted_cancellable",
224 Self::FullLogged => "full_logged",
225 }
226 }
227}
228
229impl EvaluationRequestOutcome {
230 pub const fn as_str(self) -> &'static str {
231 match self {
232 Self::InProgress => "in_progress",
233 Self::Success => "success",
234 Self::Cancelled => "cancelled",
235 Self::Error => "error",
236 }
237 }
238}
239
240impl FormulaPlaneTopologyStrategy {
241 pub const fn as_str(self) -> &'static str {
242 match self {
243 Self::NotUsed => "not_used",
244 Self::Legacy => "legacy",
245 Self::SkippedNoActiveSpans => "skipped_no_active_spans",
246 Self::SkippedNoDirtyWork => "skipped_no_dirty_work",
247 Self::Cached => "cached",
248 Self::CompiledAndCached => "compiled_and_cached",
249 Self::ExactPagedIndexed => "exact_paged_indexed",
250 Self::ExactInMemoryRuns => "exact_in_memory_runs",
251 Self::ExactNativeScratch => "exact_native_scratch",
252 Self::ExactRepeatedPasses => "exact_repeated_passes",
253 Self::CapacityFallbackMaterialization => "capacity_fallback_materialization",
254 }
255 }
256
257 pub(crate) const fn severity(self) -> u8 {
258 match self {
259 Self::NotUsed => 0,
260 Self::Legacy => 1,
261 Self::SkippedNoActiveSpans => 2,
262 Self::SkippedNoDirtyWork => 3,
263 Self::Cached => 4,
264 Self::CompiledAndCached => 5,
265 Self::ExactPagedIndexed => 6,
266 Self::ExactInMemoryRuns => 7,
267 Self::ExactNativeScratch => 8,
268 Self::ExactRepeatedPasses => 9,
269 Self::CapacityFallbackMaterialization => 10,
270 }
271 }
272}
273
274impl FormulaPlaneTopologyCacheOutcome {
275 pub const fn as_str(self) -> &'static str {
276 match self {
277 Self::NotUsed => "not_used",
278 Self::Hit => "hit",
279 Self::Built => "built",
280 Self::SkippedOverflow => "skipped_overflow",
281 Self::SkippedDynamicLegacy => "skipped_dynamic_legacy",
282 }
283 }
284
285 pub(crate) const fn severity(self) -> u8 {
286 match self {
287 Self::NotUsed => 0,
288 Self::Hit => 1,
289 Self::Built => 2,
290 Self::SkippedOverflow => 3,
291 Self::SkippedDynamicLegacy => 4,
292 }
293 }
294}
295
296impl FormulaDirtyLeaseOutcome {
297 pub const fn as_str(self) -> &'static str {
298 match self {
299 Self::NotAcquired => "not_acquired",
300 Self::Acquired => "acquired",
301 Self::Empty => "empty",
302 Self::Acknowledged => "acknowledged",
303 Self::AcknowledgedPartial => "acknowledged_partial",
304 Self::AcknowledgedEmpty => "acknowledged_empty",
305 Self::RetainedOnCancellation => "retained_on_cancellation",
306 Self::RetainedOnError => "retained_on_error",
307 }
308 }
309}
310
311#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
312#[non_exhaustive]
313pub struct FormulaPlaneTopologyRequestStats {
314 pub strategy: FormulaPlaneTopologyStrategy,
316 pub cache_outcome: FormulaPlaneTopologyCacheOutcome,
318 pub cache_hit_events: u64,
319 pub cache_build_events: u64,
320 pub cache_skip_events: u64,
321 pub cache_skip_streak: u64,
322 pub exact_pass_count: u64,
323 pub native_topology_disk_bytes: u64,
325 pub candidate_cap: Option<u64>,
326 pub edge_cap: Option<u64>,
327 pub retained_byte_cap: Option<u64>,
328 pub operator_guidance: Option<&'static str>,
329 pub producers_observed: u64,
331 pub candidates_observed: u64,
332 pub edges_observed: u64,
333 pub retained_bytes_observed: u64,
334 pub candidate_cap_hits: u64,
335 pub edge_cap_hits: u64,
336 pub byte_cap_hits: u64,
337 pub overflow_reason: Option<EvaluationResourceReason>,
338 pub incomplete_reason: Option<EvaluationIncompleteReason>,
339 pub route_events: [FormulaPlaneRouteEvent; FORMULA_PLANE_ROUTE_EVENT_CAPACITY],
342 pub route_event_count: u8,
343 pub route_events_dropped: u64,
344 pub route_event_bytes_observed: u64,
347 pub island_membership_vertices: u64,
348 pub island_membership_retained_bytes: u64,
349 pub legacy_relationships_omitted: u64,
350 pub boundary_relationships_retained: u64,
351}
352
353#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
354#[non_exhaustive]
355pub struct EvaluationResourceLedgerRequestStats {
356 pub retained_limit: Option<u64>,
357 pub mixed_cache_limit: Option<u64>,
358 pub retained_current: u64,
359 pub retained_peak: u64,
360 pub scratch_limit: Option<u64>,
361 pub schedule_discovery_limit: Option<u64>,
362 pub scratch_current: u64,
363 pub scratch_peak: u64,
364 pub disk_scratch_policy: Option<DiskScratchPolicy>,
365 pub work_limit: Option<u64>,
366 pub work_charged: u64,
367 pub deadline_ns: Option<u64>,
368 pub deadline_checkpoints: u64,
369 pub exhaustion: Option<ResourceExhaustionReason>,
370}
371
372impl EvaluationResourceLedgerRequestStats {
373 pub(crate) fn update(&mut self, snapshot: ResourceLedgerSnapshot) {
374 self.retained_limit = snapshot.retained_limit;
375 self.mixed_cache_limit = snapshot.mixed_cache_limit;
376 self.retained_current = snapshot.retained_current;
377 self.retained_peak = snapshot.retained_peak;
378 self.scratch_limit = snapshot.scratch_limit;
379 self.schedule_discovery_limit = snapshot.schedule_discovery_limit;
380 self.scratch_current = snapshot.scratch_current;
381 self.scratch_peak = snapshot.scratch_peak;
382 self.disk_scratch_policy = snapshot.disk_scratch_policy;
383 self.work_limit = snapshot.work_limit;
384 self.work_charged = snapshot.work_charged;
385 self.deadline_ns = snapshot.deadline_ns;
386 self.deadline_checkpoints = snapshot.deadline_checkpoints;
387 self.exhaustion = snapshot.exhaustion;
388 }
389}
390
391#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
392#[non_exhaustive]
393pub struct EvaluationRequestPhaseTimings {
394 pub total_ns: u64,
395 pub staged_prepare_ns: u64,
396 pub topology_ns: u64,
397 pub materialization_ns: u64,
398 pub evaluation_ns: u64,
399}
400
401#[derive(Debug, Clone, Copy, PartialEq, Eq)]
402#[non_exhaustive]
403pub struct EvaluationResourceRequestStats {
404 pub request_id: u64,
405 pub kind: EvaluationRequestKind,
406 pub formula_plane_mode: FormulaPlaneMode,
407 pub outcome: EvaluationRequestOutcome,
408 pub staged_selected: u64,
409 pub staged_retained: u64,
410 pub target_requested: u64,
411 pub target_normalized_regions: u64,
412 pub target_scope_level: u8,
414 pub target_widening_reason_bits: u64,
416 pub graph_source_scratch_estimated: u64,
417 pub graph_source_scratch_observed: u64,
418 pub target_commit_estimated_work: u64,
419 pub target_commit_actual_work: u64,
420 pub target_commit_window_ns: u64,
421 pub target_admission_failure: Option<ResourceExhaustionReason>,
422 pub evaluation_commit_preflight_count: u64,
423 pub evaluation_commit_estimated_ns: u64,
424 pub evaluation_commit_actual_ns: u64,
425 pub runtime_replan_rounds: u64,
426 pub runtime_widening_rounds: u64,
427 pub workbook_exact_attempts: u64,
428 pub topology: FormulaPlaneTopologyRequestStats,
429 pub fallback_materialized_cells: u64,
430 pub cycle_materialized_cells: u64,
431 pub dirty_lease: FormulaDirtyLeaseOutcome,
432 pub ledger: EvaluationResourceLedgerRequestStats,
433 pub phases: EvaluationRequestPhaseTimings,
434}
435
436impl EvaluationResourceRequestStats {
437 pub(crate) fn new(
438 request_id: u64,
439 kind: EvaluationRequestKind,
440 formula_plane_mode: FormulaPlaneMode,
441 staged_retained: usize,
442 ) -> Self {
443 Self {
444 request_id,
445 kind,
446 formula_plane_mode,
447 outcome: EvaluationRequestOutcome::InProgress,
448 staged_selected: 0,
449 staged_retained: staged_retained as u64,
450 target_requested: 0,
451 target_normalized_regions: 0,
452 target_scope_level: 0,
453 target_widening_reason_bits: 0,
454 graph_source_scratch_estimated: 0,
455 graph_source_scratch_observed: 0,
456 target_commit_estimated_work: 0,
457 target_commit_actual_work: 0,
458 target_commit_window_ns: 0,
459 target_admission_failure: None,
460 evaluation_commit_preflight_count: 0,
461 evaluation_commit_estimated_ns: 0,
462 evaluation_commit_actual_ns: 0,
463 runtime_replan_rounds: 0,
464 runtime_widening_rounds: 0,
465 workbook_exact_attempts: 0,
466 topology: FormulaPlaneTopologyRequestStats {
467 strategy: if formula_plane_mode == FormulaPlaneMode::AuthoritativeExperimental {
468 FormulaPlaneTopologyStrategy::NotUsed
469 } else {
470 FormulaPlaneTopologyStrategy::Legacy
471 },
472 ..FormulaPlaneTopologyRequestStats::default()
473 },
474 fallback_materialized_cells: 0,
475 cycle_materialized_cells: 0,
476 dirty_lease: FormulaDirtyLeaseOutcome::NotAcquired,
477 ledger: EvaluationResourceLedgerRequestStats::default(),
478 phases: EvaluationRequestPhaseTimings::default(),
479 }
480 }
481}
482
483#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
486#[non_exhaustive]
487pub struct EvaluationResourceBaselineStats {
488 pub last_request_id: u64,
489 pub requests_started: u64,
490 pub requests_succeeded: u64,
491 pub requests_cancelled: u64,
492 pub requests_errored: u64,
493 pub staged_selected_total: u64,
494 pub topology_cache_hits: u64,
495 pub topology_cache_builds: u64,
496 pub topology_cache_skips: u64,
497 pub topology_cache_skip_streak_current: u64,
498 pub topology_cache_skip_streak_max: u64,
499 pub topology_exact_passes_total: u64,
500 pub topology_native_disk_bytes_total: u64,
501 pub topology_candidate_cap_hits: u64,
502 pub topology_edge_cap_hits: u64,
503 pub topology_byte_cap_hits: u64,
504 pub topology_candidates_observed_total: u64,
505 pub topology_edges_observed_total: u64,
506 pub topology_retained_bytes_observed_max: u64,
507 pub fallback_materialized_cells_total: u64,
508 pub cycle_materialized_cells_total: u64,
509 pub dirty_leases_acknowledged: u64,
510 pub dirty_leases_retained_on_cancel: u64,
511 pub dirty_leases_retained_on_error: u64,
512 pub ledger_retained_peak: u64,
513 pub ledger_scratch_peak: u64,
514 pub ledger_work_charged_total: u64,
515 pub ledger_deadline_checkpoints: u64,
516 pub ledger_exhaustions: u64,
517 pub last_ledger_exhaustion: Option<ResourceExhaustionReason>,
518 pub total_request_ns: u64,
519 pub staged_prepare_ns: u64,
520 pub topology_ns: u64,
521 pub materialization_ns: u64,
522 pub evaluation_ns: u64,
523}
524
525impl EvaluationResourceBaselineStats {
526 pub(crate) fn record_started(&mut self, request_id: u64) {
527 self.last_request_id = request_id;
528 self.requests_started = self.requests_started.saturating_add(1);
529 }
530
531 pub(crate) fn record_finished(&mut self, stats: &EvaluationResourceRequestStats) {
532 match stats.outcome {
533 EvaluationRequestOutcome::Success => {
534 self.requests_succeeded = self.requests_succeeded.saturating_add(1)
535 }
536 EvaluationRequestOutcome::Cancelled => {
537 self.requests_cancelled = self.requests_cancelled.saturating_add(1)
538 }
539 EvaluationRequestOutcome::Error => {
540 self.requests_errored = self.requests_errored.saturating_add(1)
541 }
542 EvaluationRequestOutcome::InProgress => {}
543 }
544 self.staged_selected_total = self
545 .staged_selected_total
546 .saturating_add(stats.staged_selected);
547 self.topology_cache_hits = self
548 .topology_cache_hits
549 .saturating_add(stats.topology.cache_hit_events);
550 self.topology_cache_builds = self
551 .topology_cache_builds
552 .saturating_add(stats.topology.cache_build_events);
553 self.topology_cache_skips = self
554 .topology_cache_skips
555 .saturating_add(stats.topology.cache_skip_events);
556 self.topology_cache_skip_streak_current = stats.topology.cache_skip_streak;
557 self.topology_cache_skip_streak_max = self
558 .topology_cache_skip_streak_max
559 .max(stats.topology.cache_skip_streak);
560 self.topology_exact_passes_total = self
561 .topology_exact_passes_total
562 .saturating_add(stats.topology.exact_pass_count);
563 self.topology_native_disk_bytes_total = self
564 .topology_native_disk_bytes_total
565 .saturating_add(stats.topology.native_topology_disk_bytes);
566 self.topology_candidate_cap_hits = self
567 .topology_candidate_cap_hits
568 .saturating_add(stats.topology.candidate_cap_hits);
569 self.topology_edge_cap_hits = self
570 .topology_edge_cap_hits
571 .saturating_add(stats.topology.edge_cap_hits);
572 self.topology_byte_cap_hits = self
573 .topology_byte_cap_hits
574 .saturating_add(stats.topology.byte_cap_hits);
575 self.topology_candidates_observed_total = self
576 .topology_candidates_observed_total
577 .saturating_add(stats.topology.candidates_observed);
578 self.topology_edges_observed_total = self
579 .topology_edges_observed_total
580 .saturating_add(stats.topology.edges_observed);
581 self.topology_retained_bytes_observed_max = self
582 .topology_retained_bytes_observed_max
583 .max(stats.topology.retained_bytes_observed);
584 self.fallback_materialized_cells_total = self
585 .fallback_materialized_cells_total
586 .saturating_add(stats.fallback_materialized_cells);
587 self.cycle_materialized_cells_total = self
588 .cycle_materialized_cells_total
589 .saturating_add(stats.cycle_materialized_cells);
590 match stats.dirty_lease {
591 FormulaDirtyLeaseOutcome::Acknowledged
592 | FormulaDirtyLeaseOutcome::AcknowledgedPartial
593 | FormulaDirtyLeaseOutcome::AcknowledgedEmpty => {
594 self.dirty_leases_acknowledged = self.dirty_leases_acknowledged.saturating_add(1)
595 }
596 FormulaDirtyLeaseOutcome::RetainedOnCancellation => {
597 self.dirty_leases_retained_on_cancel =
598 self.dirty_leases_retained_on_cancel.saturating_add(1)
599 }
600 FormulaDirtyLeaseOutcome::RetainedOnError => {
601 self.dirty_leases_retained_on_error =
602 self.dirty_leases_retained_on_error.saturating_add(1)
603 }
604 _ => {}
605 }
606 self.ledger_retained_peak = self.ledger_retained_peak.max(stats.ledger.retained_peak);
607 self.ledger_scratch_peak = self.ledger_scratch_peak.max(stats.ledger.scratch_peak);
608 self.ledger_work_charged_total = self
609 .ledger_work_charged_total
610 .saturating_add(stats.ledger.work_charged);
611 self.ledger_deadline_checkpoints = self
612 .ledger_deadline_checkpoints
613 .saturating_add(stats.ledger.deadline_checkpoints);
614 if let Some(reason) = stats.ledger.exhaustion {
615 self.ledger_exhaustions = self.ledger_exhaustions.saturating_add(1);
616 self.last_ledger_exhaustion = Some(reason);
617 }
618 self.total_request_ns = self.total_request_ns.saturating_add(stats.phases.total_ns);
619 self.staged_prepare_ns = self
620 .staged_prepare_ns
621 .saturating_add(stats.phases.staged_prepare_ns);
622 self.topology_ns = self.topology_ns.saturating_add(stats.phases.topology_ns);
623 self.materialization_ns = self
624 .materialization_ns
625 .saturating_add(stats.phases.materialization_ns);
626 self.evaluation_ns = self
627 .evaluation_ns
628 .saturating_add(stats.phases.evaluation_ns);
629 }
630}