Skip to main content

query_planning/
correlation.rs

1//! Low-level diagnostic correlation identities.
2//!
3//! These value types are intentionally below error, metrics, runtime, and
4//! observability.  Keeping them here prevents diagnostic error construction
5//! from depending on the metrics aggregate merely to name a query or trace.
6
7use std::{
8    fmt,
9    sync::atomic::{AtomicU64, Ordering},
10    time::{SystemTime, UNIX_EPOCH},
11};
12
13use serde::{Deserialize, Serialize};
14
15use crate::plan::NodeId;
16
17static NEXT_QUERY_SEQUENCE: AtomicU64 = AtomicU64::new(1);
18
19/// Query-wide diagnostic correlation id.
20#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
21#[serde(transparent)]
22pub struct QueryId(String);
23
24impl QueryId {
25    /// Sentinel used by tests and manually-constructed metrics that have not
26    /// been attached to a query entrypoint yet.
27    pub const UNASSIGNED: &'static str = "unassigned";
28
29    /// Mints a process-local query id.
30    pub fn mint() -> Self {
31        let sequence = NEXT_QUERY_SEQUENCE.fetch_add(1, Ordering::Relaxed);
32        let nanos = SystemTime::now().duration_since(UNIX_EPOCH).map_or(0, |duration| duration.as_nanos() as u64);
33        Self(format!("q-{nanos:016x}-{sequence:08x}"))
34    }
35
36    /// Wraps a caller-provided stable query id.
37    pub fn new(value: impl Into<String>) -> Self {
38        let value = value.into();
39        if value.trim().is_empty() { Self::default() } else { Self(value) }
40    }
41
42    /// Returns the report-facing query id string.
43    pub fn as_str(&self) -> &str {
44        &self.0
45    }
46
47    /// Returns true when this id is not the default sentinel.
48    pub fn is_assigned(&self) -> bool {
49        self.0 != Self::UNASSIGNED
50    }
51}
52
53impl Default for QueryId {
54    fn default() -> Self {
55        Self(Self::UNASSIGNED.to_string())
56    }
57}
58
59impl From<String> for QueryId {
60    fn from(value: String) -> Self {
61        Self::new(value)
62    }
63}
64
65impl From<&str> for QueryId {
66    fn from(value: &str) -> Self {
67        Self::new(value)
68    }
69}
70
71impl From<&String> for QueryId {
72    fn from(value: &String) -> Self {
73        Self::new(value.clone())
74    }
75}
76
77impl fmt::Display for QueryId {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        f.write_str(self.as_str())
80    }
81}
82
83/// Decoding applies the same canonicalization as [`QueryId::new`], so a
84/// blank serialized id reads back as [`QueryId::UNASSIGNED`].
85impl<'de> Deserialize<'de> for QueryId {
86    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
87    where D: serde::Deserializer<'de> {
88        String::deserialize(deserializer).map(Self::new)
89    }
90}
91
92/// Optional parent trace id used to join multiple query ids under one request.
93#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
94#[serde(transparent)]
95pub struct TraceId(String);
96
97impl TraceId {
98    /// Wraps a caller-provided trace id.
99    pub fn new(value: impl Into<String>) -> Self {
100        Self(value.into())
101    }
102
103    /// Returns the report-facing trace id string.
104    pub fn as_str(&self) -> &str {
105        &self.0
106    }
107}
108
109impl From<String> for TraceId {
110    fn from(value: String) -> Self {
111        Self::new(value)
112    }
113}
114
115impl From<&str> for TraceId {
116    fn from(value: &str) -> Self {
117        Self::new(value)
118    }
119}
120
121impl fmt::Display for TraceId {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        f.write_str(self.as_str())
124    }
125}
126
127/// Typed diagnostic id for a plan-local node.
128#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
129pub struct PlanNodeId(String);
130
131impl PlanNodeId {
132    /// Wraps a caller-provided plan node id label.
133    pub fn new(value: impl Into<String>) -> Self {
134        Self(value.into())
135    }
136
137    /// Returns the report-facing plan node id string.
138    pub fn as_str(&self) -> &str {
139        &self.0
140    }
141}
142
143impl From<NodeId> for PlanNodeId {
144    fn from(value: NodeId) -> Self {
145        Self(value.to_string())
146    }
147}
148
149impl From<String> for PlanNodeId {
150    fn from(value: String) -> Self {
151        Self::new(value)
152    }
153}
154
155impl From<&str> for PlanNodeId {
156    fn from(value: &str) -> Self {
157        Self::new(value)
158    }
159}
160
161impl fmt::Display for PlanNodeId {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        f.write_str(self.as_str())
164    }
165}
166
167/// Typed diagnostic id for a compiled native task.
168#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
169pub struct CompiledTaskId(String);
170
171impl CompiledTaskId {
172    /// Wraps a caller-provided compiled task id label.
173    pub fn new(value: impl ToString) -> Self {
174        Self(value.to_string())
175    }
176
177    /// Returns the report-facing compiled task id string.
178    pub fn as_str(&self) -> &str {
179        &self.0
180    }
181}
182
183impl From<String> for CompiledTaskId {
184    fn from(value: String) -> Self {
185        Self::new(value)
186    }
187}
188
189impl From<&str> for CompiledTaskId {
190    fn from(value: &str) -> Self {
191        Self::new(value)
192    }
193}
194
195impl fmt::Display for CompiledTaskId {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        f.write_str(self.as_str())
198    }
199}