1use 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
11pub 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 pub fn new(value: impl Into<String>) -> Self {
24 Self(value.into())
25 }
26
27 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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
68#[serde(transparent)]
69pub struct AdmissionRequestId(u64);
70
71impl AdmissionRequestId {
72 pub const fn new(value: u64) -> Self {
74 Self(value)
75 }
76
77 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#[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 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#[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 pub const fn get(self) -> u64 {
127 self.0
128 }
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
133#[serde(transparent)]
134pub struct RuntimeSpanId(u64);
135
136impl RuntimeSpanId {
137 pub const fn new(value: u64) -> Self {
139 Self(value)
140 }
141
142 pub const fn get(self) -> u64 {
144 self.0
145 }
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
150pub struct RuntimeObservationLimits {
151 pub max_retained_records: u64,
153 pub max_retained_bytes: u64,
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
159#[serde(rename_all = "snake_case")]
160pub(crate) enum RuntimeClockDomain {
161 HostMonotonic,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
167pub struct RuntimeObservationManifest {
168 pub schema: String,
170 pub query_id: QueryId,
172 pub trace_id: Option<TraceId>,
174 pub unix_epoch_ns: Option<u64>,
176 pub(crate) clock_domain: RuntimeClockDomain,
178 pub(crate) limits: RuntimeObservationLimits,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
184pub struct PlanNodeRow {
185 pub plan_execution_id: PlanExecutionId,
187 pub node_id: RuntimePlanNodeId,
189 pub operator_kind: String,
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
195pub struct PlanEdgeRow {
196 pub plan_execution_id: PlanExecutionId,
198 pub from_node_id: RuntimePlanNodeId,
200 pub to_node_id: RuntimePlanNodeId,
202 pub input_ordinal: u32,
204}
205
206#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
208pub struct TaskDefinitionRow {
209 pub plan_execution_id: PlanExecutionId,
211 pub task_id: RuntimeTaskId,
213 pub plan_node_id: Option<RuntimePlanNodeId>,
215 pub task_kind: String,
217}
218
219#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
221pub struct CompiledEdgeRow {
222 pub plan_execution_id: PlanExecutionId,
224 pub edge_id: RuntimePipelineEdgeId,
226 pub from_task_id: RuntimeTaskId,
228 pub to_task_id: RuntimeTaskId,
230 pub edge_kind: String,
232 pub resource_ids: Vec<RuntimeResourceId>,
234}
235
236#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
238pub struct BarrierDefinitionRow {
239 pub plan_execution_id: PlanExecutionId,
241 pub barrier_id: RuntimeBarrierId,
243 pub barrier_kind: String,
245 pub upstream_task_ids: Vec<RuntimeTaskId>,
247 pub downstream_task_ids: Vec<RuntimeTaskId>,
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
253pub struct SinkDefinitionRow {
254 pub plan_execution_id: PlanExecutionId,
256 pub sink_id: RuntimeSinkId,
258 pub sink_kind: String,
260 pub input_task_id: RuntimeTaskId,
262}
263
264#[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#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
275pub struct WorkerDefinitionRow {
276 pub plan_execution_id: Option<PlanExecutionId>,
278 pub worker_id: RuntimeWorkerId,
280 pub worker_kind: String,
282 pub device_ordinal: Option<u32>,
284}
285
286#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
288#[serde(tag = "kind", rename_all = "snake_case")]
289pub enum RuntimeResourceDomain {
290 Host,
292 Device {
294 device_ordinal: u32,
296 },
297 Storage,
299}
300
301#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
303pub struct RuntimeResourceRow {
304 pub plan_execution_id: Option<PlanExecutionId>,
306 pub resource_id: RuntimeResourceId,
308 pub resource_kind: String,
310 pub domain: RuntimeResourceDomain,
312 pub producer_task_id: Option<RuntimeTaskId>,
314 pub consumer_task_ids: Vec<RuntimeTaskId>,
316 pub lifetime: String,
318}
319
320#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
322pub enum RuntimeTopologyRecord {
323 PlanNode(PlanNodeRow),
325 PlanEdge(PlanEdgeRow),
327 TaskDefinition(TaskDefinitionRow),
329 CompiledEdge(CompiledEdgeRow),
331 BarrierDefinition(BarrierDefinitionRow),
333 SinkDefinition(SinkDefinitionRow),
335 JobDefinition(JobDefinitionRow),
337 WorkerDefinition(WorkerDefinitionRow),
339 Resource(RuntimeResourceRow),
341}
342
343#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
345#[serde(rename_all = "snake_case")]
346pub enum QueryBoundaryKind {
347 Started,
349 CleanupStarted,
351 CleanupFinished,
353 Succeeded,
355 Failed,
357 Cancelled,
359 Dropped,
361 Panicked,
363}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
367#[serde(rename_all = "snake_case")]
368pub enum PlanExecutionBoundaryKind {
369 Started,
371 Finished,
373 Failed,
375 Cancelled,
377}
378
379#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
381#[serde(tag = "state", rename_all = "snake_case")]
382pub enum TaskLifecycleState {
383 Ready,
385 Started,
387 Finished,
389 Failed {
391 error_code: String,
393 },
394 Cancelled,
396 Dropped,
398 Panicked,
400}
401
402#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
404#[serde(rename_all = "snake_case")]
405pub enum RuntimeReservationAction {
406 Reserve,
408 Grow,
410 Shrink,
412 Transfer,
414 Alias,
416 Relabel,
418 SourceCachePromote,
420 SourceCacheEvict,
422 Release,
424 Deny,
426 Rollback,
428}
429
430#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
432#[serde(rename_all = "snake_case")]
433pub enum RuntimeReservationDisposition {
434 Applied,
436 Granted,
438 Denied,
440 ExplicitRelease,
442 DropRelease,
444 UnwindRelease,
446 CleanupRelease,
448 RollbackRelease,
450 AliasDeferred,
452 CancelledRelease,
454}
455
456#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
472#[serde(tag = "state", rename_all = "snake_case")]
473pub enum GpuJobLifecycleState {
474 Enqueued,
476 Started,
478 Finished,
480 Failed {
482 error_code: String,
484 },
485 CancellationRequested,
487 Cancelled,
489 Dropped,
491 Panicked,
493}
494
495#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
497#[serde(rename_all = "snake_case")]
498pub enum StreamLeaseAcquisition {
499 Reused,
501 Created,
503}
504
505#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
507#[serde(tag = "state", rename_all = "snake_case")]
508pub enum StreamLeaseLifecycleState {
509 Enqueued,
511 Acquired {
513 acquisition: StreamLeaseAcquisition,
515 host_wait_ns: u64,
517 },
518 CancellationRequested,
520 Cancelled,
522 Failed {
524 error_code: String,
526 },
527 Released,
529}
530
531#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
536pub struct RuntimeStreamLeaseScope {
537 pub(crate) plan_execution_id: Option<PlanExecutionId>,
538 pub task_id: Option<RuntimeTaskId>,
540 pub source_worker_id: Option<RuntimeWorkerId>,
542 pub gpu_job_id: Option<RuntimeJobId>,
544 pub gpu_worker_id: Option<RuntimeWorkerId>,
546}
547
548impl RuntimeStreamLeaseScope {
549 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
569#[serde(tag = "state", rename_all = "snake_case")]
570pub enum SourceReadLifecycleState {
571 Enqueued,
573 WorkerStarted {
575 queue_wait_ns: u64,
577 },
578 ReadFinished {
580 read_duration_ns: u64,
582 },
583 ReadFailed {
585 error_code: String,
587 read_duration_ns: u64,
589 },
590 ConsumerJoinStarted,
592 ConsumerJoinFinished {
594 join_duration_ns: u64,
596 },
597 Cancelled,
599 Panicked,
601}
602
603#[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#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
635pub struct RuntimeTaskLifecycleObservation {
636 pub attempt_id: TaskAttemptId,
638 pub(crate) task_kind: String,
639 pub lifecycle: TaskLifecycleState,
641}
642
643#[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 pub grant_bytes: Option<u64>,
652}
653
654#[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 pub lifecycle: GpuJobLifecycleState,
663}
664
665#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
667pub struct RuntimeStreamLeaseObservation {
668 pub(crate) lease_id: RuntimeResourceId,
669 pub(crate) device_ordinal: u32,
670 pub scope: RuntimeStreamLeaseScope,
672 pub lifecycle: StreamLeaseLifecycleState,
674}
675
676#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
678pub struct RuntimeSourceReadObservation {
679 pub task_id: RuntimeTaskId,
681 pub worker_id: RuntimeWorkerId,
683 pub chunk_index: usize,
685 pub file_index: usize,
687 pub lifecycle: SourceReadLifecycleState,
689}
690
691#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
708pub struct RuntimeReservationObservation {
709 pub reservation_id: RuntimeReservationId,
711 pub lineage_id: RuntimeReservationId,
713 pub task_attempt_id: Option<TaskAttemptId>,
715 pub task_kind: Option<String>,
717 pub action: RuntimeReservationAction,
719 pub denied_action: Option<RuntimeReservationAction>,
721 pub disposition: RuntimeReservationDisposition,
723 pub alias: bool,
725 pub reservation_kind: String,
727 pub memory_space: String,
729 pub memory_boundary: String,
731 pub requested_bytes: u64,
733 pub granted_bytes: u64,
735 pub charged_delta_bytes: i128,
737 pub current_reserved_bytes: u64,
739 pub budget_bytes: u64,
741 pub node_id: Option<String>,
743 pub stage: Option<String>,
745 pub chunk_index: Option<usize>,
747 pub file_index: Option<usize>,
749 pub source_location_id: Option<RuntimeSourceLocationId>,
752 pub row_groups: Option<Vec<usize>>,
754 pub attempt: Option<usize>,
756 pub max_attempts: Option<usize>,
758 pub resident_resource_id: Option<String>,
760 pub source_read_contract_quality: Option<String>,
762 pub source_read_planning_bytes: Option<u64>,
764 pub source_read_estimated_bytes: Option<u64>,
766}
767
768#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
770pub struct RuntimeCleanupFailureObservation {
771 pub summary: CleanupFailureSummary,
773}
774
775#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
780#[serde(rename_all = "snake_case")]
781pub enum RuntimeOperatorStageKind {
782 RegexReplacePreflight,
784 RegexReplaceExecute,
786 ExactCardinalityProbe,
788 PreparedGroupbyPrepare,
790 PreparedGroupbyAggregate,
792 LibcudfGroupby,
794 HashPartition,
796 PartialAggregate,
798 FinalMerge,
800 PartitionLocalTopK,
802 FinalSort,
804 VectorPack,
806 VectorNative,
808 VectorAssemble,
810 MlPack,
812 MlNative,
814 MlAssemble,
816}
817
818impl RuntimeOperatorStageKind {
819 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
851#[serde(tag = "state", rename_all = "snake_case")]
852pub enum RuntimeOperatorStageState {
853 Started,
855 Finished,
857 Failed {
859 error_code: String,
861 },
862 Cancelled,
864}
865
866#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
874pub struct RuntimeOperatorStageObservation {
875 pub stage_kind: RuntimeOperatorStageKind,
877 pub lifecycle: RuntimeOperatorStageState,
879 pub(crate) node_id: Option<String>,
880 pub(crate) task_id: Option<RuntimeTaskId>,
881 pub chunk_index: Option<usize>,
883 pub(crate) partition_index: Option<usize>,
884 pub(crate) attempt: Option<u32>,
885 pub strategy_label: Option<String>,
887 pub input_rows: Option<u64>,
889 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 pub ml_algorithm: Option<String>,
899 #[serde(skip_serializing_if = "Option::is_none")]
900 pub vector_relation_role: Option<String>,
902 #[serde(skip_serializing_if = "Option::is_none")]
903 pub vector_dimension: Option<usize>,
905 #[serde(skip_serializing_if = "Option::is_none")]
906 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 pub native_version_major: Option<u16>,
915 #[serde(skip_serializing_if = "Option::is_none")]
916 pub native_version_minor: Option<u16>,
918 #[serde(skip_serializing_if = "Option::is_none")]
919 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 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 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 pub fn with_task_id(mut self, task_id: RuntimeTaskId) -> Self {
967 self.task_id = Some(task_id);
968 self
969 }
970
971 pub fn with_chunk_index(mut self, chunk_index: usize) -> Self {
973 self.chunk_index = Some(chunk_index);
974 self
975 }
976
977 pub fn with_partition_index(mut self, partition_index: usize) -> Self {
979 self.partition_index = Some(partition_index);
980 self
981 }
982
983 pub fn with_attempt(mut self, attempt: u32) -> Self {
985 self.attempt = Some(attempt);
986 self
987 }
988
989 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 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 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 pub fn with_reserved_bytes(mut self, reserved_bytes: u64) -> Self {
1019 self.reserved_bytes = Some(reserved_bytes);
1020 self
1021 }
1022
1023 pub fn with_vector_algorithm(mut self, algorithm: impl Into<String>) -> Self {
1025 self.vector_algorithm = Some(algorithm.into());
1026 self
1027 }
1028
1029 pub fn with_ml_algorithm(mut self, algorithm: impl Into<String>) -> Self {
1031 self.ml_algorithm = Some(algorithm.into());
1032 self
1033 }
1034
1035 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 pub fn with_vector_dimension(mut self, dimension: usize) -> Self {
1043 self.vector_dimension = Some(dimension);
1044 self
1045 }
1046
1047 pub fn with_matrix_layout(mut self, layout: impl Into<String>) -> Self {
1049 self.matrix_layout = Some(layout.into());
1050 self
1051 }
1052
1053 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 pub fn with_device_ordinal(mut self, device_ordinal: usize) -> Self {
1061 self.device_ordinal = Some(device_ordinal);
1062 self
1063 }
1064
1065 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1094#[serde(tag = "kind", rename_all = "snake_case")]
1095pub enum RuntimeObservationPayload {
1096 QueryBoundary {
1098 boundary: QueryBoundaryKind,
1100 },
1101 PlanExecutionBoundary {
1103 boundary: PlanExecutionBoundaryKind,
1105 },
1106 TaskLifecycle(Box<RuntimeTaskLifecycleObservation>),
1108 Admission(Box<RuntimeAdmissionObservation>),
1110 GpuJob(Box<RuntimeGpuJobObservation>),
1112 StreamLease(Box<RuntimeStreamLeaseObservation>),
1114 SourceRead(Box<RuntimeSourceReadObservation>),
1116 ResourceMeasurement(Box<RuntimeResourceMeasurementObservation>),
1118 Reservation(Box<RuntimeReservationObservation>),
1120 CleanupFailure(Box<RuntimeCleanupFailureObservation>),
1122 OperatorStage(Box<RuntimeOperatorStageObservation>),
1124}
1125
1126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1128pub struct RuntimeTimelineEvent {
1129 pub sequence: ObservationSequence,
1131 pub monotonic_ns: u64,
1133 pub plan_execution_id: Option<PlanExecutionId>,
1135 pub span_id: Option<RuntimeSpanId>,
1137 pub(crate) parent_span_id: Option<RuntimeSpanId>,
1138 pub payload: RuntimeObservationPayload,
1140}
1141
1142#[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 SourceReadDetail,
1158 OperatorDetail,
1160 JoinStrategyDetail,
1162 AggregateStrategyDetail,
1164 DynamicFilterDetail,
1166 BudgetDetail,
1168 LakehouseCacheFillDetail,
1171}
1172
1173#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1175#[serde(rename_all = "snake_case")]
1176pub enum RuntimeObservationLossReason {
1177 RecordLimit,
1179 ByteLimit,
1181 Oversized,
1183 SourceLocationKeyUnavailable,
1185 Unassigned,
1188}
1189
1190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1192pub(crate) struct RuntimeObservationClassLoss {
1193 pub(crate) class: RuntimeObservationRecordClass,
1194 pub(crate) count: u64,
1195}
1196
1197#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1199pub struct RuntimeObservationReasonLoss {
1200 pub reason: RuntimeObservationLossReason,
1202 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1233pub struct RuntimeObservationFooter {
1234 pub complete: bool,
1236 pub terminal: Option<RuntimeTerminalOutcome>,
1238 pub retained_records: u64,
1240 pub retained_bytes: u64,
1242 pub first_dropped_sequence: Option<ObservationSequence>,
1244 pub last_attempted_sequence: Option<ObservationSequence>,
1246 pub(crate) dropped_by_class: Vec<RuntimeObservationClassLoss>,
1247 pub dropped_by_reason: Vec<RuntimeObservationReasonLoss>,
1249 pub truncated: bool,
1251 pub degraded: bool,
1253 pub(crate) poison_recoveries: u64,
1254 #[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 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1269 pub counter_overflow: bool,
1270}
1271
1272#[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 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}