Skip to main content

query_runtime/metrics/
join.rs

1use std::collections::BTreeMap;
2
3use query_planning::correlation::QueryId;
4
5use super::QueryMetrics;
6
7/// Join-strategy decision facts captured for diagnostics.
8#[derive(Debug, Clone, Default, PartialEq, Eq)]
9pub struct JoinStrategyDecisionMetrics {
10    /// Query-wide diagnostic correlation id, when assigned.
11    pub(crate) query_id: Option<QueryId>,
12    /// Native join node id.
13    pub node_id: String,
14    /// Stable decision fact keys and values.
15    pub facts: BTreeMap<String, String>,
16}
17
18/// Join-specific operator metrics.
19#[derive(Debug, Clone, Default, PartialEq, Eq)]
20pub struct JoinOperatorMetrics {
21    /// Query-wide diagnostic correlation id, when assigned.
22    pub query_id: Option<QueryId>,
23    /// Join type label.
24    pub join_type: String,
25    /// Execution strategy label.
26    pub execution_strategy: String,
27    /// Number of equality key pairs.
28    pub key_count: usize,
29    /// Whether a residual predicate was present.
30    pub residual_predicate: bool,
31    /// Null-equality policy label.
32    pub null_equality: String,
33    /// Planner orientation used to execute or describe this join.
34    ///
35    /// For reusable hash joins this is the side that owns the hash table. For
36    /// keyless conditional and cross joins no hash table is built. For those
37    /// joins the value is an orientation label for comparable metrics, and no
38    /// physical build artifact backs it.
39    pub build_side: String,
40    /// The side opposite `build_side` in the planner orientation.
41    pub probe_side: String,
42    /// Build-side input row count.
43    pub build_rows: u64,
44    /// Build-side input bytes.
45    pub build_bytes: u64,
46    /// Probe-side input row count.
47    pub probe_rows: u64,
48    /// Probe-side input bytes.
49    pub probe_bytes: u64,
50    /// Device bytes retained by the reusable cuDF join build artifact.
51    ///
52    /// This is a direct cudf observation. For hash joins it is the retained
53    /// hash table; for filtered joins it is the retained filtered-join build
54    /// artifact. It remains `0` for join paths without a reusable build artifact.
55    pub hash_table_bytes: u64,
56    /// Device bytes retained by a filtered-join probe state.
57    pub filtered_probe_state_bytes: u64,
58    /// Device bytes retained by a mixed join retained size-data state.
59    pub mixed_size_data_bytes: u64,
60    /// Device bytes retained by join gather maps produced for this join.
61    ///
62    /// This is a direct cudf observation over the returned device index
63    /// columns; no row-count estimate is involved.
64    pub gather_map_bytes: u64,
65    /// Upper bound of the join output's materialized bytes, `None` when the
66    /// byte model cannot bound one of its columns.
67    pub output_reservation_bytes: Option<u64>,
68    /// Join output row count.
69    pub output_rows: u64,
70    /// Join output bytes.
71    pub output_bytes: u64,
72}
73
74impl QueryMetrics {
75    /// Records join-strategy diagnostics for a node.
76    pub fn record_join_strategy_decision(&mut self, node_id: impl ToString, facts: &BTreeMap<&'static str, String>) {
77        self.join_strategy_decisions.push(JoinStrategyDecisionMetrics {
78            query_id: self.inherited_query_id(),
79            node_id: node_id.to_string(),
80            facts: facts.iter().map(|(key, value)| ((*key).to_string(), value.clone())).collect(),
81        });
82    }
83
84    /// Records one reusable hash-join build.
85    pub fn record_hash_join_build(&mut self) {
86        self.hash_join_build_count = self.hash_join_build_count.saturating_add(1);
87    }
88
89    /// Records one reusable filtered-join build.
90    pub fn record_filtered_join_build(&mut self) {
91        self.filtered_join_build_count = self.filtered_join_build_count.saturating_add(1);
92    }
93
94    /// Records a one-shot join execution.
95    pub fn record_one_shot_join(&mut self) {
96        self.one_shot_join_count = self.one_shot_join_count.saturating_add(1);
97    }
98
99    /// Adds retained hash-join hash table bytes.
100    pub fn record_hash_join_hash_table_bytes(&mut self, bytes: u64) {
101        self.hash_join_hash_table_bytes = self.hash_join_hash_table_bytes.saturating_add(bytes);
102    }
103
104    /// Adds retained filtered-join hash table bytes.
105    pub fn record_filtered_join_hash_table_bytes(&mut self, bytes: u64) {
106        self.filtered_join_hash_table_bytes = self.filtered_join_hash_table_bytes.saturating_add(bytes);
107    }
108
109    /// Adds retained filtered-join probe-state bytes.
110    pub fn record_filtered_join_probe_state_bytes(&mut self, bytes: u64) {
111        self.filtered_join_probe_state_bytes = self.filtered_join_probe_state_bytes.saturating_add(bytes);
112    }
113
114    /// Adds retained mixed-join size-data bytes.
115    pub fn record_mixed_join_size_data_bytes(&mut self, bytes: u64) {
116        self.mixed_join_size_data_bytes = self.mixed_join_size_data_bytes.saturating_add(bytes);
117    }
118
119    /// Adds one-shot join index-map bytes.
120    pub fn record_one_shot_join_index_bytes(&mut self, bytes: u64) {
121        self.one_shot_join_index_bytes = self.one_shot_join_index_bytes.saturating_add(bytes);
122    }
123
124    /// Adds one-shot join output materialization reservation bytes.
125    pub fn record_one_shot_join_materialization_bytes(&mut self, bytes: u64) {
126        self.one_shot_join_materialization_bytes = self.one_shot_join_materialization_bytes.saturating_add(bytes);
127    }
128
129    /// Adds one-shot join output bytes.
130    pub fn record_one_shot_join_output_bytes(&mut self, bytes: u64) {
131        self.one_shot_join_output_bytes = self.one_shot_join_output_bytes.saturating_add(bytes);
132    }
133}