Skip to main content

query_runtime/observability/
runtime_observation.rs

1//! Typed runtime observations, topology, and bounded retention records.
2
3use std::fmt;
4
5use query_planning::correlation::{QueryId, TraceId};
6use serde::{Deserialize, Serialize};
7
8use super::attempt_report::{CleanupFailureSummary, QueryAttemptReport, RuntimeTerminalOutcome};
9use crate::observability::{RuntimeTaskId, TaskAttemptId};
10
11/// JSONL schema emitted by sealed runtime observation snapshots.
12pub const RUNTIME_OBSERVATION_JSONL_SCHEMA: &str = "query_engine.runtime_observation";
13
14macro_rules! string_id {
15    ($name:ident, $doc:literal) => {
16        #[doc = $doc]
17        #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
18        #[serde(transparent)]
19        pub struct $name(String);
20
21        impl $name {
22            /// Creates an identity from its stable, scope-local label.
23            pub fn new(value: impl Into<String>) -> Self {
24                Self(value.into())
25            }
26
27            /// Returns the stable label.
28            pub fn as_str(&self) -> &str {
29                &self.0
30            }
31        }
32
33        impl From<String> for $name {
34            fn from(value: String) -> Self {
35                Self::new(value)
36            }
37        }
38
39        impl From<&str> for $name {
40            fn from(value: &str) -> Self {
41                Self::new(value)
42            }
43        }
44
45        impl fmt::Display for $name {
46            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
47                formatter.write_str(self.as_str())
48            }
49        }
50    };
51}
52
53string_id!(PlanExecutionId, "Identity for one invocation of a native plan.");
54string_id!(RuntimePlanNodeId, "Plan-node identity scoped to one plan execution.");
55string_id!(RuntimeReservationId, "Reservation identity scoped to one plan execution.");
56string_id!(RuntimeResourceId, "Resident-resource identity scoped to one plan execution.");
57string_id!(RuntimeJobId, "Executor-job identity scoped to one query.");
58string_id!(RuntimeWorkerId, "Worker identity scoped to one query.");
59string_id!(RuntimePipelineEdgeId, "Compiled-pipeline edge identity scoped to one plan execution.");
60string_id!(RuntimeBarrierId, "Compiled pipeline-barrier identity scoped to one plan execution.");
61string_id!(RuntimeSinkId, "Compiled pipeline-sink identity scoped to one plan execution.");
62
63/// Service-local identity for one admission attempt.
64///
65/// This value is never a query identity. It is meaningful only for joining
66/// admission lifecycle events inside one process.
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
68#[serde(transparent)]
69pub struct AdmissionRequestId(u64);
70
71impl AdmissionRequestId {
72    /// Creates a service-local admission request identity.
73    pub const fn new(value: u64) -> Self {
74        Self(value)
75    }
76
77    /// Returns the service-local numeric value.
78    pub const fn get(self) -> u64 {
79        self.0
80    }
81}
82
83impl fmt::Display for AdmissionRequestId {
84    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85        self.0.fmt(formatter)
86    }
87}
88
89/// Opaque query-scoped identity for one source location.
90///
91/// Construction is restricted to the observation session's keyed digest path;
92/// callers can inspect or deserialize an exported identity but cannot turn raw
93/// source text into an apparently valid identifier.
94#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
95#[serde(transparent)]
96pub struct RuntimeSourceLocationId(String);
97
98impl RuntimeSourceLocationId {
99    pub(crate) fn from_keyed_digest(value: String) -> Self {
100        Self(value)
101    }
102
103    /// Returns the opaque query-scoped label.
104    pub fn as_str(&self) -> &str {
105        &self.0
106    }
107}
108
109impl fmt::Display for RuntimeSourceLocationId {
110    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
111        formatter.write_str(self.as_str())
112    }
113}
114
115/// Total recorder order assigned to every attempted record.
116#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
117#[serde(transparent)]
118pub struct ObservationSequence(u64);
119
120impl ObservationSequence {
121    pub(crate) const fn new(value: u64) -> Self {
122        Self(value)
123    }
124
125    /// Returns the one-based sequence value.
126    pub const fn get(self) -> u64 {
127        self.0
128    }
129}
130
131/// Span identity scoped to one query.
132#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
133#[serde(transparent)]
134pub struct RuntimeSpanId(u64);
135
136impl RuntimeSpanId {
137    /// Creates a span identity from a query-local value.
138    pub const fn new(value: u64) -> Self {
139        Self(value)
140    }
141
142    /// Returns the query-local value.
143    pub const fn get(self) -> u64 {
144        self.0
145    }
146}
147
148/// Recorder limits copied into the manifest.
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
150pub struct RuntimeObservationLimits {
151    /// Maximum retained topology rows, timeline events and metrics detail rows.
152    pub max_retained_records: u64,
153    /// Maximum deterministic logical bytes charged to retained records.
154    pub max_retained_bytes: u64,
155}
156
157/// Clock domain for host runtime observations.
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
159#[serde(rename_all = "snake_case")]
160pub(crate) enum RuntimeClockDomain {
161    /// Query-relative time sampled from a monotonic host clock.
162    HostMonotonic,
163}
164
165/// First JSONL record describing immutable query identity and capture limits.
166#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
167pub struct RuntimeObservationManifest {
168    /// JSONL schema label.
169    pub schema: String,
170    /// Query-wide diagnostic identity.
171    pub query_id: QueryId,
172    /// Optional parent trace identity.
173    pub trace_id: Option<TraceId>,
174    /// Optional wall-clock anchor captured once when the session is created.
175    pub unix_epoch_ns: Option<u64>,
176    /// Clock domain used by timeline offsets.
177    pub(crate) clock_domain: RuntimeClockDomain,
178    /// Deterministic per-query retention limits.
179    pub(crate) limits: RuntimeObservationLimits,
180}
181
182/// One native plan node in the static topology.
183#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
184pub struct PlanNodeRow {
185    /// Plan invocation containing this node.
186    pub plan_execution_id: PlanExecutionId,
187    /// Node identity within that plan invocation.
188    pub node_id: RuntimePlanNodeId,
189    /// Stable native operator category used for diagnostic grouping.
190    pub operator_kind: String,
191}
192
193/// One directed edge in the static native plan topology.
194#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
195pub struct PlanEdgeRow {
196    /// Plan invocation containing this dependency.
197    pub plan_execution_id: PlanExecutionId,
198    /// Input-producing plan node.
199    pub from_node_id: RuntimePlanNodeId,
200    /// Plan node that consumes the input.
201    pub to_node_id: RuntimePlanNodeId,
202    /// Zero-based position of the input in the consuming node.
203    pub input_ordinal: u32,
204}
205
206/// One compiled task definition in the static runtime topology.
207#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
208pub struct TaskDefinitionRow {
209    /// Plan invocation whose compiled schedule defines this task.
210    pub plan_execution_id: PlanExecutionId,
211    /// Compiled task identity within the plan invocation.
212    pub task_id: RuntimeTaskId,
213    /// Originating plan node, or `None` for a task without a direct plan node.
214    pub plan_node_id: Option<RuntimePlanNodeId>,
215    /// Stable compiled task category used for diagnostic grouping.
216    pub task_kind: String,
217}
218
219/// One compiled dependency edge in the static runtime topology.
220#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
221pub struct CompiledEdgeRow {
222    /// Plan invocation containing this compiled dependency.
223    pub plan_execution_id: PlanExecutionId,
224    /// Dependency edge identity within the plan invocation.
225    pub edge_id: RuntimePipelineEdgeId,
226    /// Compiled task that produces the dependency.
227    pub from_task_id: RuntimeTaskId,
228    /// Compiled task that consumes the dependency.
229    pub to_task_id: RuntimeTaskId,
230    /// Stable dependency category.
231    pub edge_kind: String,
232    /// Resident resources carried by this dependency.
233    pub resource_ids: Vec<RuntimeResourceId>,
234}
235
236/// One compiled barrier definition in the static runtime topology.
237#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
238pub struct BarrierDefinitionRow {
239    /// Plan invocation containing this barrier.
240    pub plan_execution_id: PlanExecutionId,
241    /// Barrier identity within the compiled schedule.
242    pub barrier_id: RuntimeBarrierId,
243    /// Stable synchronization-barrier category.
244    pub barrier_kind: String,
245    /// Tasks whose completion precedes this barrier.
246    pub upstream_task_ids: Vec<RuntimeTaskId>,
247    /// Tasks whose scheduling follows this barrier.
248    pub downstream_task_ids: Vec<RuntimeTaskId>,
249}
250
251/// The compiled result-sink definition in the static runtime topology.
252#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
253pub struct SinkDefinitionRow {
254    /// Plan invocation containing this result sink.
255    pub plan_execution_id: PlanExecutionId,
256    /// Result-sink identity within the plan invocation.
257    pub sink_id: RuntimeSinkId,
258    /// Stable result materialization category.
259    pub sink_kind: String,
260    /// Compiled task that produces the sink input.
261    pub input_task_id: RuntimeTaskId,
262}
263
264/// One GPU executor job definition.
265#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
266pub struct JobDefinitionRow {
267    pub(crate) plan_execution_id: Option<PlanExecutionId>,
268    pub(crate) job_id: RuntimeJobId,
269    pub(crate) job_kind: String,
270    pub(crate) device_ordinal: u32,
271}
272
273/// One host worker definition used by GPU or source execution.
274#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
275pub struct WorkerDefinitionRow {
276    /// Associated plan invocation, or `None` for query-wide work.
277    pub plan_execution_id: Option<PlanExecutionId>,
278    /// Worker identity within this query observation.
279    pub worker_id: RuntimeWorkerId,
280    /// Stable source or GPU worker category.
281    pub worker_kind: String,
282    /// Associated CUDA ordinal, or `None` for a worker without a device binding.
283    pub device_ordinal: Option<u32>,
284}
285
286/// Resource domain carried by a static resource definition.
287#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
288#[serde(tag = "kind", rename_all = "snake_case")]
289pub enum RuntimeResourceDomain {
290    /// Resource stored in host memory.
291    Host,
292    /// Resource stored on the specified CUDA device.
293    Device {
294        /// CUDA device ordinal containing the resource.
295        device_ordinal: u32,
296    },
297    /// Resource retained in source or cache storage.
298    Storage,
299}
300
301/// One resident resource in the static runtime topology.
302#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
303pub struct RuntimeResourceRow {
304    /// Associated plan invocation, or `None` for query-wide ownership.
305    pub plan_execution_id: Option<PlanExecutionId>,
306    /// Resident-resource identity within this observation.
307    pub resource_id: RuntimeResourceId,
308    /// Stable resident-resource category.
309    pub resource_kind: String,
310    /// Memory or storage domain containing the resource.
311    pub domain: RuntimeResourceDomain,
312    /// Producing task, or `None` when no compiled task produces the resource.
313    pub producer_task_id: Option<RuntimeTaskId>,
314    /// Compiled tasks that consume the resource.
315    pub consumer_task_ids: Vec<RuntimeTaskId>,
316    /// Diagnostic label describing the resource's scheduled lifetime.
317    pub lifetime: String,
318}
319
320/// Static topology accepted by the bounded recorder.
321#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
322pub enum RuntimeTopologyRecord {
323    /// Native plan node definition.
324    PlanNode(PlanNodeRow),
325    /// Directed input dependency between plan nodes.
326    PlanEdge(PlanEdgeRow),
327    /// Compiled executable task definition.
328    TaskDefinition(TaskDefinitionRow),
329    /// Resource dependency between compiled tasks.
330    CompiledEdge(CompiledEdgeRow),
331    /// Compiled task synchronization barrier.
332    BarrierDefinition(BarrierDefinitionRow),
333    /// Compiled result materialization sink.
334    SinkDefinition(SinkDefinitionRow),
335    /// GPU executor job definition.
336    JobDefinition(JobDefinitionRow),
337    /// Host worker definition for source or GPU execution.
338    WorkerDefinition(WorkerDefinitionRow),
339    /// Resident-resource ownership and lifetime definition.
340    Resource(RuntimeResourceRow),
341}
342
343/// Query-level observation boundary.
344#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
345#[serde(rename_all = "snake_case")]
346pub enum QueryBoundaryKind {
347    /// Query observation began.
348    Started,
349    /// Terminal query cleanup began.
350    CleanupStarted,
351    /// Terminal query cleanup completed.
352    CleanupFinished,
353    /// Query execution completed successfully.
354    Succeeded,
355    /// An execution error ended the query.
356    Failed,
357    /// Caller cancellation or deadline expiry ended the query.
358    Cancelled,
359    /// The query owner was dropped before another terminal outcome.
360    Dropped,
361    /// An unwind ended the query.
362    Panicked,
363}
364
365/// Plan-execution observation boundary.
366#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
367#[serde(rename_all = "snake_case")]
368pub enum PlanExecutionBoundaryKind {
369    /// One native plan invocation began.
370    Started,
371    /// The plan invocation completed successfully.
372    Finished,
373    /// The plan invocation ended with an error, drop or panic.
374    Failed,
375    /// Caller cancellation or deadline expiry ended the plan invocation.
376    Cancelled,
377}
378
379/// Exactly-once lifecycle state for one compiled task attempt.
380#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
381#[serde(tag = "state", rename_all = "snake_case")]
382pub enum TaskLifecycleState {
383    /// The task attempt became runnable.
384    Ready,
385    /// The task attempt began execution.
386    Started,
387    /// The task attempt completed successfully.
388    Finished,
389    /// A classified error ended the task attempt.
390    Failed {
391        /// Stable machine code of the classified error.
392        error_code: String,
393    },
394    /// Cooperative cancellation ended the task attempt.
395    Cancelled,
396    /// The task lifecycle was dropped before another terminal state.
397    Dropped,
398    /// An unwind ended the task attempt.
399    Panicked,
400}
401
402/// Reservation operation recorded at the ownership boundary that applied it.
403#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
404#[serde(rename_all = "snake_case")]
405pub enum RuntimeReservationAction {
406    /// Request an initial byte reservation.
407    Reserve,
408    /// Increase an existing reservation.
409    Grow,
410    /// Decrease an existing reservation.
411    Shrink,
412    /// Move a reservation charge to another ownership boundary.
413    Transfer,
414    /// Create a reference to existing reservation ownership.
415    Alias,
416    /// Change the reservation's diagnostic ownership label.
417    Relabel,
418    /// Transfer a source result into resident-cache ownership.
419    SourceCachePromote,
420    /// Remove resident source-cache ownership.
421    SourceCacheEvict,
422    /// Return charged reservation bytes.
423    Release,
424    /// Record an operation rejected against a reservation limit.
425    Deny,
426    /// Undo an uncommitted ownership operation.
427    Rollback,
428}
429
430/// Terminal or nonterminal ownership disposition for a reservation operation.
431#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
432#[serde(rename_all = "snake_case")]
433pub enum RuntimeReservationDisposition {
434    /// A nonterminal reservation operation changed ownership state.
435    Applied,
436    /// The requested reservation charge was accepted.
437    Granted,
438    /// The requested operation did not acquire a charge.
439    Denied,
440    /// An explicit release returned the charge.
441    ExplicitRelease,
442    /// Normal destruction returned the charge.
443    DropRelease,
444    /// Destruction during unwinding returned the charge.
445    UnwindRelease,
446    /// Terminal cleanup returned the charge.
447    CleanupRelease,
448    /// Rollback returned an uncommitted charge.
449    RollbackRelease,
450    /// An alias keeps the underlying charge retained.
451    AliasDeferred,
452    /// Cancellation cleanup returned the charge.
453    CancelledRelease,
454}
455
456/// Query-service admission lifecycle state.
457#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
458#[serde(tag = "state", rename_all = "snake_case")]
459pub enum AdmissionLifecycleState {
460    Enqueued,
461    Admitted,
462    Rejected { reason: String },
463    TimedOut,
464    CancellationRequested,
465    Cancelled,
466    Released,
467    Dropped,
468}
469
470/// GPU execution-pool job lifecycle state.
471#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
472#[serde(tag = "state", rename_all = "snake_case")]
473pub enum GpuJobLifecycleState {
474    /// The pool accepted the job before a worker began it.
475    Enqueued,
476    /// A worker began the job body.
477    Started,
478    /// The job body completed successfully.
479    Finished,
480    /// A classified error ended the job body.
481    Failed {
482        /// Stable machine code of the classified error.
483        error_code: String,
484    },
485    /// Cooperative cancellation was requested for the accepted job.
486    CancellationRequested,
487    /// Cancellation ended the job.
488    Cancelled,
489    /// The job was dropped before another terminal state.
490    Dropped,
491    /// An unwind ended the job body.
492    Panicked,
493}
494
495/// How a service stream lease was obtained.
496#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
497#[serde(rename_all = "snake_case")]
498pub enum StreamLeaseAcquisition {
499    /// An existing pooled CUDA stream was leased.
500    Reused,
501    /// A new CUDA stream was created for the lease.
502    Created,
503}
504
505/// Service stream-lease lifecycle state.
506#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
507#[serde(tag = "state", rename_all = "snake_case")]
508pub enum StreamLeaseLifecycleState {
509    /// Stream acquisition began waiting for a lease.
510    Enqueued,
511    /// A CUDA stream lease was acquired after the recorded host wait.
512    Acquired {
513        /// Whether the lease reused a stream or created one.
514        acquisition: StreamLeaseAcquisition,
515        /// Elapsed host acquisition wait in nanoseconds.
516        host_wait_ns: u64,
517    },
518    /// Cancellation was requested while acquiring a lease.
519    CancellationRequested,
520    /// Cancellation ended stream acquisition.
521    Cancelled,
522    /// A classified error ended stream acquisition.
523    Failed {
524        /// Stable machine code of the stream acquisition error.
525        error_code: String,
526    },
527    /// Query ownership transferred synchronously to pool-managed cleanup.
528    Released,
529}
530
531/// Explicit correlation available when a service-stream lease is acquired.
532///
533/// Callers compose this value at execution boundaries; stream acquisition does
534/// not consult thread-local or task-local ambient state.
535#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
536pub struct RuntimeStreamLeaseScope {
537    pub(crate) plan_execution_id: Option<PlanExecutionId>,
538    /// Associated compiled task, or `None` when acquisition has no task binding.
539    pub task_id: Option<RuntimeTaskId>,
540    /// Source worker requesting the stream, or `None` outside source work.
541    pub source_worker_id: Option<RuntimeWorkerId>,
542    /// GPU executor job requesting the stream, or `None` outside a job.
543    pub gpu_job_id: Option<RuntimeJobId>,
544    /// GPU executor worker requesting the stream, or `None` when unavailable.
545    pub gpu_worker_id: Option<RuntimeWorkerId>,
546}
547
548impl RuntimeStreamLeaseScope {
549    /// Creates a lease scope bound to one plan invocation with no worker or task correlation.
550    pub fn for_plan(plan_execution_id: PlanExecutionId) -> Self {
551        Self { plan_execution_id: Some(plan_execution_id), ..Self::default() }
552    }
553
554    pub(crate) fn with_source_worker(mut self, task_id: RuntimeTaskId, worker_id: RuntimeWorkerId) -> Self {
555        self.task_id = Some(task_id);
556        self.source_worker_id = Some(worker_id);
557        self
558    }
559
560    pub(crate) fn with_gpu_worker(mut self, job_id: RuntimeJobId, worker_id: RuntimeWorkerId) -> Self {
561        self.gpu_job_id = Some(job_id);
562        self.gpu_worker_id = Some(worker_id);
563        self
564    }
565}
566
567/// Source-read scheduler lifecycle state.
568#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
569#[serde(tag = "state", rename_all = "snake_case")]
570pub enum SourceReadLifecycleState {
571    /// A source-read job entered the scheduler.
572    Enqueued,
573    /// The source worker began after the recorded queue wait.
574    WorkerStarted {
575        /// Nanoseconds from source job enqueue to worker start.
576        queue_wait_ns: u64,
577    },
578    /// The source worker completed the read.
579    ReadFinished {
580        /// Nanoseconds spent executing the source read.
581        read_duration_ns: u64,
582    },
583    /// A classified error ended the source read.
584    ReadFailed {
585        /// Stable machine code of the source-read error.
586        error_code: String,
587        /// Nanoseconds from read start to its error.
588        read_duration_ns: u64,
589    },
590    /// The consumer began waiting for the source worker result.
591    ConsumerJoinStarted,
592    /// The consumer finished joining the source worker result.
593    ConsumerJoinFinished {
594        /// Nanoseconds spent waiting for the source worker result.
595        join_duration_ns: u64,
596    },
597    /// Cooperative cancellation ended the source read.
598    Cancelled,
599    /// An unwind ended the source read.
600    Panicked,
601}
602
603/// Scope of one resource measurement.
604#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
605#[serde(rename_all = "snake_case")]
606pub enum RuntimeMeasurementScope {
607    Query,
608    Task,
609    Process,
610    DeviceGlobal,
611}
612
613/// Provenance of one resource measurement.
614#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
615#[serde(rename_all = "snake_case")]
616pub enum RuntimeMeasurementSource {
617    ReservationLedger,
618    FrameResidency,
619    AllocatorHighWater,
620    DeviceSampler,
621    StreamPool,
622}
623
624/// Evidence quality of one resource measurement.
625#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
626#[serde(rename_all = "snake_case")]
627pub enum RuntimeMeasurementQuality {
628    Exact,
629    Observed,
630    Estimated,
631}
632
633/// Task-lifecycle payload body retained out of line.
634#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
635pub struct RuntimeTaskLifecycleObservation {
636    /// Compiled task identity paired with its execution attempt number.
637    pub attempt_id: TaskAttemptId,
638    pub(crate) task_kind: String,
639    /// Task boundary recorded by the task lifecycle owner.
640    pub lifecycle: TaskLifecycleState,
641}
642
643/// Admission-lifecycle payload body retained out of line.
644#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
645pub struct RuntimeAdmissionObservation {
646    pub(crate) admission_request_id: AdmissionRequestId,
647    pub(crate) lifecycle: AdmissionLifecycleState,
648    pub(crate) queue_depth: usize,
649    pub(crate) device_ordinal: Option<u32>,
650    /// Immutable admitted query capacity in bytes, or `None` before a grant.
651    pub grant_bytes: Option<u64>,
652}
653
654/// GPU-job payload body retained out of line.
655#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
656pub struct RuntimeGpuJobObservation {
657    pub(crate) job_id: RuntimeJobId,
658    pub(crate) job_kind: String,
659    pub(crate) device_ordinal: u32,
660    pub(crate) worker_id: Option<RuntimeWorkerId>,
661    /// Executor-job boundary recorded by the pool owner.
662    pub lifecycle: GpuJobLifecycleState,
663}
664
665/// Stream-lease payload body retained out of line.
666#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
667pub struct RuntimeStreamLeaseObservation {
668    pub(crate) lease_id: RuntimeResourceId,
669    pub(crate) device_ordinal: u32,
670    /// Explicit task, source-worker and GPU-job correlation attached at acquisition.
671    pub scope: RuntimeStreamLeaseScope,
672    /// Stream lease boundary recorded by the pool owner.
673    pub lifecycle: StreamLeaseLifecycleState,
674}
675
676/// Source-read payload body retained out of line.
677#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
678pub struct RuntimeSourceReadObservation {
679    /// Compiled task that owns this source read.
680    pub task_id: RuntimeTaskId,
681    /// Source worker that performed this read.
682    pub worker_id: RuntimeWorkerId,
683    /// Zero-based source chunk index within this execution.
684    pub chunk_index: usize,
685    /// Zero-based source file index within the source contract.
686    pub file_index: usize,
687    /// Read, worker or consumer-join boundary recorded by the source scheduler.
688    pub lifecycle: SourceReadLifecycleState,
689}
690
691/// Resource-measurement payload body retained out of line.
692#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
693pub struct RuntimeResourceMeasurementObservation {
694    pub(crate) resource_id: RuntimeResourceId,
695    pub(crate) scope: RuntimeMeasurementScope,
696    pub(crate) source: RuntimeMeasurementSource,
697    pub(crate) quality: RuntimeMeasurementQuality,
698    pub(crate) measurement_kind: String,
699    pub(crate) current_value: u64,
700    pub(crate) delta: i128,
701    pub(crate) limit: Option<u64>,
702    pub(crate) device_ordinal: Option<u32>,
703    pub(crate) grant_bytes: Option<u64>,
704}
705
706/// Context-rich reservation payload body retained out of line.
707#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
708pub struct RuntimeReservationObservation {
709    /// Identity of the reservation operated on at this boundary.
710    pub reservation_id: RuntimeReservationId,
711    /// Identity shared by reservations derived from the same ownership lineage.
712    pub lineage_id: RuntimeReservationId,
713    /// Associated task attempt, or `None` outside task execution.
714    pub task_attempt_id: Option<TaskAttemptId>,
715    /// Associated compiled task category, or `None` when unavailable.
716    pub task_kind: Option<String>,
717    /// Reservation operation attempted at this boundary.
718    pub action: RuntimeReservationAction,
719    /// Operation that was denied, or `None` when this record is not a denial.
720    pub denied_action: Option<RuntimeReservationAction>,
721    /// Ownership result after the operation.
722    pub disposition: RuntimeReservationDisposition,
723    /// Whether the reservation aliases existing ownership without a second charge.
724    pub alias: bool,
725    /// Stable category of the reservation.
726    pub reservation_kind: String,
727    /// Diagnostic memory-domain label.
728    pub memory_space: String,
729    /// Diagnostic execution-boundary label for the charge.
730    pub memory_boundary: String,
731    /// Bytes requested by the recorded operation.
732    pub requested_bytes: u64,
733    /// Bytes granted by the recorded operation.
734    pub granted_bytes: u64,
735    /// Signed change in charged reservation bytes at this boundary.
736    pub charged_delta_bytes: i128,
737    /// Reservation bytes charged after this operation.
738    pub current_reserved_bytes: u64,
739    /// Byte limit against which the reservation was checked.
740    pub budget_bytes: u64,
741    /// Associated native plan node label, or `None` when unavailable.
742    pub node_id: Option<String>,
743    /// Associated execution-stage label, or `None` when unavailable.
744    pub stage: Option<String>,
745    /// Associated source chunk index, or `None` outside chunk work.
746    pub chunk_index: Option<usize>,
747    /// Associated source file index, or `None` outside file work.
748    pub file_index: Option<usize>,
749    /// Opaque query-scoped source-location digest, or `None` when unavailable.
750    /// The digest excludes raw source text from the observation.
751    pub source_location_id: Option<RuntimeSourceLocationId>,
752    /// Selected row-group ordinals, or `None` when the operation has no selection.
753    pub row_groups: Option<Vec<usize>>,
754    /// Source operation attempt number, or `None` outside bounded retries.
755    pub attempt: Option<usize>,
756    /// Source retry bound, or `None` when unavailable.
757    pub max_attempts: Option<usize>,
758    /// Associated resident-resource label, or `None` without a resident result.
759    pub resident_resource_id: Option<String>,
760    /// Source sizing evidence label, or `None` outside source reservation.
761    pub source_read_contract_quality: Option<String>,
762    /// Source contract planning byte charge, or `None` when unavailable.
763    pub source_read_planning_bytes: Option<u64>,
764    /// Source contract estimated bytes, or `None` when unavailable.
765    pub source_read_estimated_bytes: Option<u64>,
766}
767
768/// Secondary-cleanup-failure payload body retained out of line.
769#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
770pub struct RuntimeCleanupFailureObservation {
771    /// Bounded secondary cleanup failure; the primary outcome remains separate.
772    pub summary: CleanupFailureSummary,
773}
774
775/// Operator-substage category for blocking native primitives.
776///
777/// Pair events by [`RuntimeTimelineEvent::span_id`]. Kinds must stay short and
778/// free of SQL, paths, or unbounded diagnostic text.
779#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
780#[serde(rename_all = "snake_case")]
781pub enum RuntimeOperatorStageKind {
782    /// Regular-expression replacement size preflight.
783    RegexReplacePreflight,
784    /// Regular-expression replacement native execution.
785    RegexReplaceExecute,
786    /// Native exact-cardinality measurement used for strategy selection.
787    ExactCardinalityProbe,
788    /// Preparation of reusable groupby state.
789    PreparedGroupbyPrepare,
790    /// Aggregation using prepared groupby state.
791    PreparedGroupbyAggregate,
792    /// Direct libcudf groupby execution.
793    LibcudfGroupby,
794    /// Hash partitioning of a native frame.
795    HashPartition,
796    /// Aggregation of one partial input.
797    PartialAggregate,
798    /// Merge of partial aggregate results.
799    FinalMerge,
800    /// Top-k selection within one native partition.
801    PartitionLocalTopK,
802    /// Final native sort of retained inputs.
803    FinalSort,
804    /// Preparation of dense vector inputs for a native algorithm.
805    VectorPack,
806    /// Native vector algorithm execution.
807    VectorNative,
808    /// Assembly of vector algorithm output into query results.
809    VectorAssemble,
810    /// Preparation of matrix inputs for a native ML algorithm.
811    MlPack,
812    /// Native machine-learning algorithm execution.
813    MlNative,
814    /// Assembly of ML algorithm output into query results.
815    MlAssemble,
816}
817
818impl RuntimeOperatorStageKind {
819    /// Stable snake_case label used in facts and JSON.
820    pub const fn as_str(self) -> &'static str {
821        match self {
822            Self::RegexReplacePreflight => "regex_replace_preflight",
823            Self::RegexReplaceExecute => "regex_replace_execute",
824            Self::ExactCardinalityProbe => "exact_cardinality_probe",
825            Self::PreparedGroupbyPrepare => "prepared_groupby_prepare",
826            Self::PreparedGroupbyAggregate => "prepared_groupby_aggregate",
827            Self::LibcudfGroupby => "libcudf_groupby",
828            Self::HashPartition => "hash_partition",
829            Self::PartialAggregate => "partial_aggregate",
830            Self::FinalMerge => "final_merge",
831            Self::PartitionLocalTopK => "partition_local_top_k",
832            Self::FinalSort => "final_sort",
833            Self::VectorPack => "vector_pack",
834            Self::VectorNative => "vector_native",
835            Self::VectorAssemble => "vector_assemble",
836            Self::MlPack => "ml_pack",
837            Self::MlNative => "ml_native",
838            Self::MlAssemble => "ml_assemble",
839        }
840    }
841}
842
843impl fmt::Display for RuntimeOperatorStageKind {
844    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
845        formatter.write_str(self.as_str())
846    }
847}
848
849/// Lifecycle state for one operator-substage span.
850#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
851#[serde(tag = "state", rename_all = "snake_case")]
852pub enum RuntimeOperatorStageState {
853    /// The native operator stage began.
854    Started,
855    /// The native operator stage completed successfully.
856    Finished,
857    /// A classified error ended the operator stage.
858    Failed {
859        /// Stable machine code of the classified error.
860        error_code: String,
861    },
862    /// Cooperative cancellation ended the operator stage.
863    Cancelled,
864}
865
866/// Operator-substage payload body retained out of line.
867///
868/// The payload carries numeric facts and bounded string labels, such as
869/// `node_id` and `strategy_label`. It excludes SQL, raw paths, and unbounded
870/// strings.
871/// Constructors preserve caller labels without bounding or validating them;
872/// callers supply labels that satisfy the observation contract.
873#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
874pub struct RuntimeOperatorStageObservation {
875    /// Native primitive or preparation stage measured by this span.
876    pub stage_kind: RuntimeOperatorStageKind,
877    /// Start, completion or failure boundary within the stage span.
878    pub lifecycle: RuntimeOperatorStageState,
879    pub(crate) node_id: Option<String>,
880    pub(crate) task_id: Option<RuntimeTaskId>,
881    /// Associated source chunk index, or `None` outside chunk work.
882    pub chunk_index: Option<usize>,
883    pub(crate) partition_index: Option<usize>,
884    pub(crate) attempt: Option<u32>,
885    /// Bounded execution strategy label, or `None` when unavailable.
886    pub strategy_label: Option<String>,
887    /// Observed input row count, or `None` without a row observation.
888    pub input_rows: Option<u64>,
889    /// Observed output row count, or `None` without a row observation.
890    pub output_rows: Option<u64>,
891    pub(crate) input_bytes: Option<u64>,
892    pub(crate) output_bytes: Option<u64>,
893    pub(crate) reserved_bytes: Option<u64>,
894    #[serde(skip_serializing_if = "Option::is_none")]
895    pub(crate) vector_algorithm: Option<String>,
896    #[serde(skip_serializing_if = "Option::is_none")]
897    /// Machine-learning algorithm label, or `None` outside ML stages.
898    pub ml_algorithm: Option<String>,
899    #[serde(skip_serializing_if = "Option::is_none")]
900    /// Vector input role label, or `None` outside vector stages.
901    pub vector_relation_role: Option<String>,
902    #[serde(skip_serializing_if = "Option::is_none")]
903    /// Number of coordinates in a dense vector, or `None` when unavailable.
904    pub vector_dimension: Option<usize>,
905    #[serde(skip_serializing_if = "Option::is_none")]
906    /// Native matrix layout label, or `None` when unavailable.
907    pub matrix_layout: Option<String>,
908    #[serde(skip_serializing_if = "Option::is_none")]
909    pub(crate) execution_binding_id: Option<u64>,
910    #[serde(skip_serializing_if = "Option::is_none")]
911    pub(crate) device_ordinal: Option<usize>,
912    #[serde(skip_serializing_if = "Option::is_none")]
913    /// Native component major version, or `None` without version evidence.
914    pub native_version_major: Option<u16>,
915    #[serde(skip_serializing_if = "Option::is_none")]
916    /// Native component minor version, or `None` without version evidence.
917    pub native_version_minor: Option<u16>,
918    #[serde(skip_serializing_if = "Option::is_none")]
919    /// Native component patch version, or `None` without version evidence.
920    pub native_version_patch: Option<u16>,
921    #[serde(skip_serializing_if = "Option::is_none")]
922    pub(crate) bounded_control_scalar_events: Option<u64>,
923    #[serde(skip_serializing_if = "Option::is_none")]
924    pub(crate) bounded_control_scalar_bytes: Option<u64>,
925}
926
927impl RuntimeOperatorStageObservation {
928    /// Creates a stage observation with only the required identity fields set.
929    pub fn new(stage_kind: RuntimeOperatorStageKind, lifecycle: RuntimeOperatorStageState) -> Self {
930        Self {
931            stage_kind,
932            lifecycle,
933            node_id: None,
934            task_id: None,
935            chunk_index: None,
936            partition_index: None,
937            attempt: None,
938            strategy_label: None,
939            input_rows: None,
940            output_rows: None,
941            input_bytes: None,
942            output_bytes: None,
943            reserved_bytes: None,
944            vector_algorithm: None,
945            ml_algorithm: None,
946            vector_relation_role: None,
947            vector_dimension: None,
948            matrix_layout: None,
949            execution_binding_id: None,
950            device_ordinal: None,
951            native_version_major: None,
952            native_version_minor: None,
953            native_version_patch: None,
954            bounded_control_scalar_events: None,
955            bounded_control_scalar_bytes: None,
956        }
957    }
958
959    /// Attaches the bounded native plan-node label for this stage.
960    pub fn with_node_id(mut self, node_id: impl Into<String>) -> Self {
961        self.node_id = Some(node_id.into());
962        self
963    }
964
965    /// Attaches the compiled task associated with this stage.
966    pub fn with_task_id(mut self, task_id: RuntimeTaskId) -> Self {
967        self.task_id = Some(task_id);
968        self
969    }
970
971    /// Attaches the source chunk index associated with this stage.
972    pub fn with_chunk_index(mut self, chunk_index: usize) -> Self {
973        self.chunk_index = Some(chunk_index);
974        self
975    }
976
977    /// Attaches the native partition index associated with this stage.
978    pub fn with_partition_index(mut self, partition_index: usize) -> Self {
979        self.partition_index = Some(partition_index);
980        self
981    }
982
983    /// Attaches the operation attempt number associated with this stage.
984    pub fn with_attempt(mut self, attempt: u32) -> Self {
985        self.attempt = Some(attempt);
986        self
987    }
988
989    /// Attaches a bounded execution strategy label without validating its content.
990    pub fn with_strategy_label(mut self, strategy_label: impl Into<String>) -> Self {
991        self.strategy_label = Some(strategy_label.into());
992        self
993    }
994
995    /// Attaches the observed input row count.
996    pub fn with_input_rows(mut self, input_rows: u64) -> Self {
997        self.input_rows = Some(input_rows);
998        self
999    }
1000
1001    pub(crate) fn with_output_rows(mut self, output_rows: u64) -> Self {
1002        self.output_rows = Some(output_rows);
1003        self
1004    }
1005
1006    /// Attaches the observed input size in bytes.
1007    pub fn with_input_bytes(mut self, input_bytes: u64) -> Self {
1008        self.input_bytes = Some(input_bytes);
1009        self
1010    }
1011
1012    pub(crate) fn with_output_bytes(mut self, output_bytes: u64) -> Self {
1013        self.output_bytes = Some(output_bytes);
1014        self
1015    }
1016
1017    /// Attaches the reservation charge in bytes associated with this stage.
1018    pub fn with_reserved_bytes(mut self, reserved_bytes: u64) -> Self {
1019        self.reserved_bytes = Some(reserved_bytes);
1020        self
1021    }
1022
1023    /// Attaches the native vector algorithm label.
1024    pub fn with_vector_algorithm(mut self, algorithm: impl Into<String>) -> Self {
1025        self.vector_algorithm = Some(algorithm.into());
1026        self
1027    }
1028
1029    /// Attaches the native machine-learning algorithm label.
1030    pub fn with_ml_algorithm(mut self, algorithm: impl Into<String>) -> Self {
1031        self.ml_algorithm = Some(algorithm.into());
1032        self
1033    }
1034
1035    /// Attaches the vector input role used by the native call.
1036    pub fn with_vector_relation_role(mut self, role: impl Into<String>) -> Self {
1037        self.vector_relation_role = Some(role.into());
1038        self
1039    }
1040
1041    /// Attaches the number of coordinates in each dense vector.
1042    pub fn with_vector_dimension(mut self, dimension: usize) -> Self {
1043        self.vector_dimension = Some(dimension);
1044        self
1045    }
1046
1047    /// Attaches the native matrix layout label.
1048    pub fn with_matrix_layout(mut self, layout: impl Into<String>) -> Self {
1049        self.matrix_layout = Some(layout.into());
1050        self
1051    }
1052
1053    /// Attaches the native execution binding identity for correlation.
1054    pub fn with_execution_binding_id(mut self, binding_id: u64) -> Self {
1055        self.execution_binding_id = Some(binding_id);
1056        self
1057    }
1058
1059    /// Attaches the CUDA ordinal on which the stage executes.
1060    pub fn with_device_ordinal(mut self, device_ordinal: usize) -> Self {
1061        self.device_ordinal = Some(device_ordinal);
1062        self
1063    }
1064
1065    /// Attaches the native component version as major, minor and patch values.
1066    pub fn with_native_version(mut self, major: u16, minor: u16, patch: u16) -> Self {
1067        self.native_version_major = Some(major);
1068        self.native_version_minor = Some(minor);
1069        self.native_version_patch = Some(patch);
1070        self
1071    }
1072
1073    pub(crate) fn with_bounded_control_scalars(mut self, events: u64, bytes: u64) -> Self {
1074        self.bounded_control_scalar_events = Some(events);
1075        self.bounded_control_scalar_bytes = Some(bytes);
1076        self
1077    }
1078
1079    pub(crate) fn logical_retained_bytes(&self) -> u64 {
1080        string_bytes(self.stage_kind.as_str())
1081            + match &self.lifecycle {
1082                RuntimeOperatorStageState::Failed { error_code } => string_bytes(error_code),
1083                _ => 1,
1084            }
1085            + self.node_id.as_ref().map_or(0, |value| string_bytes(value))
1086            + self.task_id.as_ref().map_or(0, |value| string_bytes(value.as_str()))
1087            + self.strategy_label.as_ref().map_or(0, |value| string_bytes(value))
1088            + 48
1089    }
1090}
1091
1092/// Typed dynamic event payload.
1093#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1094#[serde(tag = "kind", rename_all = "snake_case")]
1095pub enum RuntimeObservationPayload {
1096    /// Query lifecycle boundary stored with the event timestamp.
1097    QueryBoundary {
1098        /// Query lifecycle transition represented by this event.
1099        boundary: QueryBoundaryKind,
1100    },
1101    /// Native plan invocation boundary stored with the event timestamp.
1102    PlanExecutionBoundary {
1103        /// Native plan invocation transition represented by this event.
1104        boundary: PlanExecutionBoundaryKind,
1105    },
1106    /// Compiled task attempt lifecycle evidence.
1107    TaskLifecycle(Box<RuntimeTaskLifecycleObservation>),
1108    /// Admission lifecycle and immutable grant evidence.
1109    Admission(Box<RuntimeAdmissionObservation>),
1110    /// GPU executor job lifecycle evidence.
1111    GpuJob(Box<RuntimeGpuJobObservation>),
1112    /// Stream acquisition and release lifecycle evidence.
1113    StreamLease(Box<RuntimeStreamLeaseObservation>),
1114    /// Source worker and consumer-join lifecycle evidence.
1115    SourceRead(Box<RuntimeSourceReadObservation>),
1116    /// Resource sample with its owning scope and provenance.
1117    ResourceMeasurement(Box<RuntimeResourceMeasurementObservation>),
1118    /// Reservation ownership transition and byte charge evidence.
1119    Reservation(Box<RuntimeReservationObservation>),
1120    /// Secondary cleanup failure retained without replacing a primary query error.
1121    CleanupFailure(Box<RuntimeCleanupFailureObservation>),
1122    /// Operator-substage lifecycle for blocking native primitives.
1123    OperatorStage(Box<RuntimeOperatorStageObservation>),
1124}
1125
1126/// One dynamic event in recorder total order.
1127#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1128pub struct RuntimeTimelineEvent {
1129    /// Recorder sequence that orders this event with topology record attempts.
1130    pub sequence: ObservationSequence,
1131    /// Nanoseconds elapsed from the query observation's monotonic clock origin.
1132    pub monotonic_ns: u64,
1133    /// Associated native plan invocation, or `None` for query-wide events.
1134    pub plan_execution_id: Option<PlanExecutionId>,
1135    /// Query-scoped span used to pair lifecycle events, or `None` without a span.
1136    pub span_id: Option<RuntimeSpanId>,
1137    pub(crate) parent_span_id: Option<RuntimeSpanId>,
1138    /// Typed lifecycle or resource evidence retained by this event.
1139    pub payload: RuntimeObservationPayload,
1140}
1141
1142/// Retention class used by explicit loss accounting.
1143#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1144#[serde(rename_all = "snake_case")]
1145pub enum RuntimeObservationRecordClass {
1146    PlanNode,
1147    PlanEdge,
1148    TaskDefinition,
1149    CompiledEdge,
1150    BarrierDefinition,
1151    SinkDefinition,
1152    JobDefinition,
1153    WorkerDefinition,
1154    Resource,
1155    TimelineEvent,
1156    /// A source-read detail row offered for bounded retention.
1157    SourceReadDetail,
1158    /// An operator occurrence row offered for bounded retention.
1159    OperatorDetail,
1160    /// A join strategy decision row offered for bounded retention.
1161    JoinStrategyDetail,
1162    /// An aggregate strategy decision row offered for bounded retention.
1163    AggregateStrategyDetail,
1164    /// A dynamic-filter runtime row offered for bounded retention.
1165    DynamicFilterDetail,
1166    /// A lower-level budget report row offered for bounded retention.
1167    BudgetDetail,
1168    /// A background lakehouse cache-fill completion offered for bounded
1169    /// retention.
1170    LakehouseCacheFillDetail,
1171}
1172
1173/// Reason an attempted record was not retained.
1174#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1175#[serde(rename_all = "snake_case")]
1176pub enum RuntimeObservationLossReason {
1177    /// The configured retained-record limit was exhausted.
1178    RecordLimit,
1179    /// The configured retained logical-byte budget was exhausted.
1180    ByteLimit,
1181    /// A single record exceeded the entire configured logical-byte budget.
1182    Oversized,
1183    /// A keyed source digest was unavailable; raw source text was not retained.
1184    SourceLocationKeyUnavailable,
1185    /// No plan execution was left unfrozen to carry the record when the
1186    /// session sealed.
1187    Unassigned,
1188}
1189
1190/// Count of dropped records in one class.
1191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1192pub(crate) struct RuntimeObservationClassLoss {
1193    pub(crate) class: RuntimeObservationRecordClass,
1194    pub(crate) count: u64,
1195}
1196
1197/// Count of dropped records for one reason.
1198#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1199pub struct RuntimeObservationReasonLoss {
1200    /// Condition that prevented retention.
1201    pub reason: RuntimeObservationLossReason,
1202    /// Records dropped for this condition; consult footer overflow metadata for exactness.
1203    pub count: u64,
1204}
1205
1206fn serialize_optional_attempt_report<S>(
1207    report: &Option<std::sync::Arc<QueryAttemptReport>>,
1208    serializer: S,
1209) -> Result<S::Ok, S::Error>
1210where
1211    S: serde::Serializer,
1212{
1213    match report {
1214        Some(report) => serializer.serialize_some(report.as_ref()),
1215        None => serializer.serialize_none(),
1216    }
1217}
1218
1219fn deserialize_optional_attempt_report<'de, D>(
1220    deserializer: D,
1221) -> Result<Option<std::sync::Arc<QueryAttemptReport>>, D::Error>
1222where D: serde::Deserializer<'de> {
1223    Option::<QueryAttemptReport>::deserialize(deserializer).map(|report| report.map(std::sync::Arc::new))
1224}
1225
1226/// Final loss, completeness, and terminal metadata.
1227///
1228/// An attempt-owned snapshot may also carry the exact immutable terminal
1229/// report. The `Arc` is shared with attempt completion; it does not create a
1230/// second report or event store. Keeping it in the exported footer lets the
1231/// retained JSONL carry the same sealed facts as the other projections.
1232#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1233pub struct RuntimeObservationFooter {
1234    /// Whether the observation session sealed. Retention losses remain described separately.
1235    pub complete: bool,
1236    /// Committed query outcome, or `None` when no outcome was supplied at seal.
1237    pub terminal: Option<RuntimeTerminalOutcome>,
1238    /// Retained topology rows, timeline events and bounded detail records.
1239    pub retained_records: u64,
1240    /// Deterministic logical bytes charged to retained records.
1241    pub retained_bytes: u64,
1242    /// First record attempt declined for retention, or `None` when none was dropped.
1243    pub first_dropped_sequence: Option<ObservationSequence>,
1244    /// Last attempted record sequence, or `None` when no record was attempted.
1245    pub last_attempted_sequence: Option<ObservationSequence>,
1246    pub(crate) dropped_by_class: Vec<RuntimeObservationClassLoss>,
1247    /// Loss counts grouped by their closed retention reason.
1248    pub dropped_by_reason: Vec<RuntimeObservationReasonLoss>,
1249    /// Whether at least one attempted record could not be retained.
1250    pub truncated: bool,
1251    /// Whether recording encountered degradation, including recovered recorder poisoning.
1252    pub degraded: bool,
1253    pub(crate) poison_recoveries: u64,
1254    /// The exact sealed attempt report, when this observation belongs to an
1255    /// execution attempt. A footer without an attempt report omits this field
1256    /// when serialized.
1257    #[serde(
1258        default,
1259        skip_serializing_if = "Option::is_none",
1260        serialize_with = "serialize_optional_attempt_report",
1261        deserialize_with = "deserialize_optional_attempt_report"
1262    )]
1263    pub(crate) attempt_report: Option<std::sync::Arc<QueryAttemptReport>>,
1264    /// At least one recorder sequence or accounting counter saturated.
1265    ///
1266    /// Saturated values remain monotonic, while this marker prevents exported
1267    /// observations from presenting them as exact.
1268    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1269    pub counter_overflow: bool,
1270}
1271
1272/// One independently parseable JSONL record.
1273#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1274#[serde(tag = "record_type", content = "data", rename_all = "snake_case")]
1275pub enum RuntimeObservationRecord {
1276    Manifest(RuntimeObservationManifest),
1277    PlanNode { sequence: ObservationSequence, row: PlanNodeRow },
1278    PlanEdge { sequence: ObservationSequence, row: PlanEdgeRow },
1279    TaskDefinition { sequence: ObservationSequence, row: TaskDefinitionRow },
1280    CompiledEdge { sequence: ObservationSequence, row: CompiledEdgeRow },
1281    BarrierDefinition { sequence: ObservationSequence, row: BarrierDefinitionRow },
1282    SinkDefinition { sequence: ObservationSequence, row: SinkDefinitionRow },
1283    JobDefinition { sequence: ObservationSequence, row: JobDefinitionRow },
1284    WorkerDefinition { sequence: ObservationSequence, row: WorkerDefinitionRow },
1285    Resource { sequence: ObservationSequence, row: RuntimeResourceRow },
1286    TimelineEvent(RuntimeTimelineEvent),
1287    Footer(RuntimeObservationFooter),
1288}
1289
1290fn string_bytes(value: &str) -> u64 {
1291    value.len() as u64
1292}
1293
1294impl RuntimeTopologyRecord {
1295    pub(crate) const fn class(&self) -> RuntimeObservationRecordClass {
1296        match self {
1297            Self::PlanNode(_) => RuntimeObservationRecordClass::PlanNode,
1298            Self::PlanEdge(_) => RuntimeObservationRecordClass::PlanEdge,
1299            Self::TaskDefinition(_) => RuntimeObservationRecordClass::TaskDefinition,
1300            Self::CompiledEdge(_) => RuntimeObservationRecordClass::CompiledEdge,
1301            Self::BarrierDefinition(_) => RuntimeObservationRecordClass::BarrierDefinition,
1302            Self::SinkDefinition(_) => RuntimeObservationRecordClass::SinkDefinition,
1303            Self::JobDefinition(_) => RuntimeObservationRecordClass::JobDefinition,
1304            Self::WorkerDefinition(_) => RuntimeObservationRecordClass::WorkerDefinition,
1305            Self::Resource(_) => RuntimeObservationRecordClass::Resource,
1306        }
1307    }
1308
1309    pub(super) fn logical_retained_bytes(&self) -> u64 {
1310        const ENVELOPE: u64 = 24;
1311        ENVELOPE
1312            + match self {
1313                Self::PlanNode(row) => {
1314                    string_bytes(row.plan_execution_id.as_str())
1315                        + string_bytes(row.node_id.as_str())
1316                        + string_bytes(&row.operator_kind)
1317                },
1318                Self::PlanEdge(row) => {
1319                    string_bytes(row.plan_execution_id.as_str())
1320                        + string_bytes(row.from_node_id.as_str())
1321                        + string_bytes(row.to_node_id.as_str())
1322                        + 4
1323                },
1324                Self::TaskDefinition(row) => {
1325                    string_bytes(row.plan_execution_id.as_str())
1326                        + string_bytes(row.task_id.as_str())
1327                        + row.plan_node_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1328                        + string_bytes(&row.task_kind)
1329                        + 1
1330                },
1331                Self::CompiledEdge(row) => {
1332                    string_bytes(row.plan_execution_id.as_str())
1333                        + string_bytes(row.edge_id.as_str())
1334                        + string_bytes(row.from_task_id.as_str())
1335                        + string_bytes(row.to_task_id.as_str())
1336                        + string_bytes(&row.edge_kind)
1337                        + row.resource_ids.iter().map(|id| string_bytes(id.as_str())).sum::<u64>()
1338                },
1339                Self::BarrierDefinition(row) => {
1340                    string_bytes(row.plan_execution_id.as_str())
1341                        + string_bytes(row.barrier_id.as_str())
1342                        + string_bytes(&row.barrier_kind)
1343                        + row.upstream_task_ids.iter().map(|id| string_bytes(id.as_str())).sum::<u64>()
1344                        + row.downstream_task_ids.iter().map(|id| string_bytes(id.as_str())).sum::<u64>()
1345                },
1346                Self::SinkDefinition(row) => {
1347                    string_bytes(row.plan_execution_id.as_str())
1348                        + string_bytes(row.sink_id.as_str())
1349                        + string_bytes(&row.sink_kind)
1350                        + string_bytes(row.input_task_id.as_str())
1351                },
1352                Self::JobDefinition(row) => {
1353                    row.plan_execution_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1354                        + string_bytes(row.job_id.as_str())
1355                        + string_bytes(&row.job_kind)
1356                        + 4
1357                },
1358                Self::WorkerDefinition(row) => {
1359                    row.plan_execution_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1360                        + string_bytes(row.worker_id.as_str())
1361                        + string_bytes(&row.worker_kind)
1362                        + 5
1363                },
1364                Self::Resource(row) => {
1365                    row.plan_execution_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1366                        + string_bytes(row.resource_id.as_str())
1367                        + string_bytes(&row.resource_kind)
1368                        + row.producer_task_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1369                        + row.consumer_task_ids.iter().map(|id| string_bytes(id.as_str())).sum::<u64>()
1370                        + string_bytes(&row.lifetime)
1371                        + match row.domain {
1372                            RuntimeResourceDomain::Host | RuntimeResourceDomain::Storage => 1,
1373                            RuntimeResourceDomain::Device { .. } => 5,
1374                        }
1375                },
1376            }
1377    }
1378}
1379
1380impl RuntimeTimelineEvent {
1381    pub(super) fn logical_retained_bytes(&self) -> u64 {
1382        const ENVELOPE: u64 = 42;
1383        let plan_execution_bytes = self.plan_execution_id.as_ref().map_or(0, |id| string_bytes(id.as_str()));
1384        let payload_bytes = match &self.payload {
1385            RuntimeObservationPayload::QueryBoundary { .. } => 2,
1386            RuntimeObservationPayload::PlanExecutionBoundary { .. } => 2,
1387            RuntimeObservationPayload::TaskLifecycle(observation) => {
1388                string_bytes(observation.attempt_id.task_id.as_str())
1389                    + 4
1390                    + string_bytes(&observation.task_kind)
1391                    + match &observation.lifecycle {
1392                        TaskLifecycleState::Failed { error_code } => string_bytes(error_code),
1393                        _ => 1,
1394                    }
1395            },
1396            RuntimeObservationPayload::Admission(observation) => {
1397                (match &observation.lifecycle {
1398                    AdmissionLifecycleState::Rejected { reason } => string_bytes(reason),
1399                    _ => 1,
1400                }) + 29
1401            },
1402            RuntimeObservationPayload::GpuJob(observation) => {
1403                string_bytes(observation.job_id.as_str())
1404                    + string_bytes(&observation.job_kind)
1405                    + observation.worker_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1406                    + match &observation.lifecycle {
1407                        GpuJobLifecycleState::Failed { error_code } => string_bytes(error_code),
1408                        _ => 1,
1409                    }
1410                    + 5
1411            },
1412            RuntimeObservationPayload::StreamLease(observation) => {
1413                string_bytes(observation.lease_id.as_str())
1414                    + observation.scope.plan_execution_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1415                    + observation.scope.task_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1416                    + observation.scope.source_worker_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1417                    + observation.scope.gpu_job_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1418                    + observation.scope.gpu_worker_id.as_ref().map_or(0, |id| string_bytes(id.as_str()))
1419                    + match &observation.lifecycle {
1420                        StreamLeaseLifecycleState::Failed { error_code } => string_bytes(error_code),
1421                        StreamLeaseLifecycleState::Acquired { .. } => 9,
1422                        _ => 1,
1423                    }
1424                    + 4
1425            },
1426            RuntimeObservationPayload::SourceRead(observation) => {
1427                string_bytes(observation.task_id.as_str())
1428                    + string_bytes(observation.worker_id.as_str())
1429                    + match &observation.lifecycle {
1430                        SourceReadLifecycleState::ReadFailed { error_code, .. } => string_bytes(error_code) + 8,
1431                        SourceReadLifecycleState::WorkerStarted { .. }
1432                        | SourceReadLifecycleState::ReadFinished { .. }
1433                        | SourceReadLifecycleState::ConsumerJoinFinished { .. } => 8,
1434                        _ => 1,
1435                    }
1436                    + 16
1437            },
1438            RuntimeObservationPayload::ResourceMeasurement(observation) => {
1439                string_bytes(observation.resource_id.as_str()) + string_bytes(&observation.measurement_kind) + 47
1440            },
1441            RuntimeObservationPayload::Reservation(observation) => {
1442                let RuntimeReservationObservation {
1443                    reservation_id,
1444                    lineage_id,
1445                    task_attempt_id,
1446                    task_kind,
1447                    reservation_kind,
1448                    memory_space,
1449                    memory_boundary,
1450                    node_id,
1451                    stage,
1452                    source_location_id,
1453                    row_groups,
1454                    resident_resource_id,
1455                    source_read_contract_quality,
1456                    ..
1457                } = observation.as_ref();
1458                string_bytes(reservation_id.as_str())
1459                    + string_bytes(lineage_id.as_str())
1460                    + task_attempt_id.as_ref().map_or(0, |attempt| string_bytes(attempt.task_id.as_str()) + 4)
1461                    + task_kind.as_ref().map_or(0, |value| string_bytes(value))
1462                    + string_bytes(reservation_kind)
1463                    + string_bytes(memory_space)
1464                    + string_bytes(memory_boundary)
1465                    + node_id.as_ref().map_or(0, |value| string_bytes(value))
1466                    + stage.as_ref().map_or(0, |value| string_bytes(value))
1467                    + source_location_id.as_ref().map_or(0, |value| string_bytes(value.as_str()))
1468                    + row_groups.as_ref().map_or(0, |values| values.len() as u64 * 8)
1469                    + resident_resource_id.as_ref().map_or(0, |value| string_bytes(value))
1470                    + source_read_contract_quality.as_ref().map_or(0, |value| string_bytes(value))
1471                    + 66
1472            },
1473            RuntimeObservationPayload::CleanupFailure(observation) => {
1474                let summary = &observation.summary;
1475                let failure = summary.failure();
1476                let text_bytes = failure.text().map_or(0, |text| string_bytes(text.value()));
1477                64 + text_bytes + u64::try_from(failure.facts().len()).unwrap_or(u64::MAX) * 16
1478            },
1479            RuntimeObservationPayload::OperatorStage(observation) => observation.logical_retained_bytes(),
1480        };
1481        ENVELOPE + plan_execution_bytes + payload_bytes
1482    }
1483}
1484
1485impl QueryAttemptReport {
1486    /// Projects the sealed observation footer completeness facts into this attempt report.
1487    pub(crate) fn set_observation_completeness(&mut self, footer: &RuntimeObservationFooter) {
1488        self.observation_lost_records = footer.dropped_by_class.iter().map(|loss| loss.count).sum();
1489        self.observation_retained_records = footer.retained_records;
1490        self.observation_retained_bytes = footer.retained_bytes;
1491        self.observation_truncated = footer.truncated;
1492        self.observation_degraded = footer.degraded;
1493    }
1494}