1use std::any::Any;
16use std::sync::{Arc, Mutex};
17use std::time::Duration;
18
19use ahash::{HashMap, HashSet};
20use arrow_schema::{Schema as ArrowSchema, SchemaRef as ArrowSchemaRef, SortOptions};
21use async_stream::stream;
22use common_catalog::parse_catalog_and_schema_from_db_string;
23use common_plugins::GREPTIME_EXEC_READ_COST;
24use common_query::request::QueryRequest;
25use common_recordbatch::adapter::{RecordBatchMetrics, region_scan_output_bytes};
26use common_telemetry::tracing_context::TracingContext;
27use datafusion::execution::{SessionState, TaskContext};
28use datafusion::physical_plan::execution_plan::{Boundedness, EmissionType};
29use datafusion::physical_plan::filter_pushdown::{
30 ChildPushdownResult, FilterPushdownPhase, FilterPushdownPropagation, PushedDown,
31};
32use datafusion::physical_plan::metrics::{
33 Count, ExecutionPlanMetricsSet, Gauge, MetricBuilder, MetricsSet, Time,
34};
35use datafusion::physical_plan::stream::RecordBatchStreamAdapter;
36use datafusion::physical_plan::{
37 DisplayAs, DisplayFormatType, ExecutionPlan, Partitioning, PlanProperties,
38 SendableRecordBatchStream,
39};
40use datafusion_common::{Column as ColumnExpr, DataFusionError, Result};
41use datafusion_expr::{Expr, Extension, LogicalPlan, UserDefinedLogicalNodeCore};
42use datafusion_physical_expr::expressions::Column;
43use datafusion_physical_expr::{Distribution, EquivalenceProperties, PhysicalSortExpr};
44use futures_util::StreamExt;
45use greptime_proto::v1::region::RegionRequestHeader;
46use meter_core::data::ReadItem;
47use meter_macros::read_meter;
48use session::context::QueryContextRef;
49use store_api::metrics::{REGION_QUERY_CPU_TIME, REGION_QUERY_SCANNED_BYTES};
50use store_api::storage::RegionId;
51use table::table_name::TableName;
52use tokio::time::Instant;
53use tracing::{Instrument, Span};
54
55use crate::dist_plan::analyzer::AliasMapping;
56use crate::dist_plan::analyzer::utils::patch_batch_timezone;
57use crate::dist_plan::dyn_filter_bridge::{
58 CapturedDynFilter, capture_remote_dyn_filters_for_pushdown,
59 query_context_with_initial_dyn_filter_regs, register_dyn_filters_for_region,
60};
61use crate::dist_plan::{RemoteDynFilterProducerId, RemoteDynFilterRegistryLease};
62use crate::metrics::{MERGE_SCAN_ERRORS_TOTAL, MERGE_SCAN_POLL_ELAPSED, MERGE_SCAN_REGIONS};
63use crate::options::{FlowQueryExtensions, remote_dyn_filter_pushdown_enabled_from_extensions};
64use crate::query_engine::QueryEngineState;
65use crate::region_query::RegionQueryHandlerRef;
66
67fn query_engine_state_from_task_context(context: &TaskContext) -> Option<Arc<QueryEngineState>> {
68 context.session_config().get_extension()
69}
70
71fn remote_dyn_filter_enabled(query_ctx: &QueryContextRef) -> Result<bool> {
72 remote_dyn_filter_pushdown_enabled_from_extensions(&query_ctx.extensions())
73 .map_err(|err| DataFusionError::External(Box::new(err)))
74}
75
76fn acquire_remote_dyn_filter_registry_lease(
77 context: &TaskContext,
78 query_ctx: &QueryContextRef,
79 captured_dyn_filters: &[CapturedDynFilter],
80) -> Option<RemoteDynFilterRegistryLease> {
81 if captured_dyn_filters.is_empty() {
82 return None;
83 }
84
85 let query_id = query_ctx.remote_query_id_value()?;
86 let query_engine_state = query_engine_state_from_task_context(context)?;
87 Some(
88 query_engine_state
89 .dyn_filter_registry_manager()
90 .acquire_lease(query_id),
91 )
92}
93
94fn query_context_for_remote_dyn_filter_region(
95 query_ctx: &QueryContextRef,
96 region_id: RegionId,
97 remote_dyn_filter_registry_lease: Option<&RemoteDynFilterRegistryLease>,
98 captured_dyn_filters: &[CapturedDynFilter],
99) -> session::context::QueryContext {
100 if let Some(remote_dyn_filter_registry_lease) = remote_dyn_filter_registry_lease {
101 register_dyn_filters_for_region(
102 remote_dyn_filter_registry_lease.registry(),
103 region_id,
104 captured_dyn_filters,
105 );
106 }
107
108 query_context_with_initial_dyn_filter_regs(query_ctx, region_id, captured_dyn_filters)
109}
110
111#[derive(Debug, Hash, PartialOrd, PartialEq, Eq, Clone)]
112pub struct MergeScanLogicalPlan {
113 input: LogicalPlan,
115 is_placeholder: bool,
117 partition_cols: AliasMapping,
118 remote_dyn_filter_producer_id: Option<RemoteDynFilterProducerId>,
120}
121
122impl UserDefinedLogicalNodeCore for MergeScanLogicalPlan {
123 fn name(&self) -> &str {
124 Self::name()
125 }
126
127 fn inputs(&self) -> Vec<&LogicalPlan> {
130 vec![]
131 }
132
133 fn schema(&self) -> &datafusion_common::DFSchemaRef {
134 self.input.schema()
135 }
136
137 fn expressions(&self) -> Vec<datafusion_expr::Expr> {
139 vec![]
140 }
141
142 fn fmt_for_explain(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
143 write!(
144 f,
145 "MergeScan [is_placeholder={}, remote_input=[\n{}\n]]",
146 self.is_placeholder, self.input
147 )
148 }
149
150 fn with_exprs_and_inputs(
151 &self,
152 _exprs: Vec<datafusion::prelude::Expr>,
153 _inputs: Vec<LogicalPlan>,
154 ) -> Result<Self> {
155 Ok(self.clone())
156 }
157}
158
159impl MergeScanLogicalPlan {
160 pub fn new(input: LogicalPlan, is_placeholder: bool, partition_cols: AliasMapping) -> Self {
161 Self {
162 input,
163 is_placeholder,
164 partition_cols,
165 remote_dyn_filter_producer_id: None,
166 }
167 }
168
169 pub(crate) fn with_remote_dyn_filter_producer_id(
170 mut self,
171 remote_dyn_filter_producer_id: RemoteDynFilterProducerId,
172 ) -> Self {
173 self.remote_dyn_filter_producer_id = Some(remote_dyn_filter_producer_id);
174 self
175 }
176
177 pub fn name() -> &'static str {
178 "MergeScan"
179 }
180
181 pub fn into_logical_plan(self) -> LogicalPlan {
183 LogicalPlan::Extension(Extension {
184 node: Arc::new(self),
185 })
186 }
187
188 pub fn is_placeholder(&self) -> bool {
189 self.is_placeholder
190 }
191
192 pub fn input(&self) -> &LogicalPlan {
193 &self.input
194 }
195
196 pub fn partition_cols(&self) -> &AliasMapping {
197 &self.partition_cols
198 }
199
200 pub fn remote_dyn_filter_producer_id(&self) -> Option<RemoteDynFilterProducerId> {
201 self.remote_dyn_filter_producer_id
202 }
203}
204
205#[derive(Clone)]
206pub struct MergeScanExec {
207 table: TableName,
208 regions: Vec<RegionId>,
209 plan: LogicalPlan,
210 arrow_schema: ArrowSchemaRef,
211 region_query_handler: RegionQueryHandlerRef,
212 metric: ExecutionPlanMetricsSet,
213 properties: Arc<PlanProperties>,
214 sub_stage_metrics: Arc<Mutex<HashMap<RegionId, RecordBatchMetrics>>>,
216 partition_metrics: Arc<Mutex<HashMap<usize, PartitionMetrics>>>,
218 query_ctx: QueryContextRef,
219 remote_dyn_filter_producer_id: Option<RemoteDynFilterProducerId>,
221 captured_remote_dyn_filters: Arc<Mutex<Vec<CapturedDynFilter>>>,
222 target_partition: usize,
223 partition_cols: AliasMapping,
224 enable_per_region_metrics: bool,
225}
226
227impl std::fmt::Debug for MergeScanExec {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 f.debug_struct("MergeScanExec")
230 .field("table", &self.table)
231 .field("regions", &self.regions)
232 .field("plan", &self.plan)
233 .finish()
234 }
235}
236
237impl MergeScanExec {
238 #[allow(clippy::too_many_arguments)]
239 pub fn new(
240 session_state: &SessionState,
241 table: TableName,
242 regions: Vec<RegionId>,
243 plan: LogicalPlan,
244 arrow_schema: &ArrowSchema,
245 region_query_handler: RegionQueryHandlerRef,
246 query_ctx: QueryContextRef,
247 target_partition: usize,
248 partition_cols: AliasMapping,
249 remote_dyn_filter_producer_id: Option<RemoteDynFilterProducerId>,
250 enable_per_region_metrics: bool,
251 ) -> Result<Self> {
252 let arrow_schema = Arc::new(arrow_schema.clone());
256
257 let eq_properties = if let LogicalPlan::Sort(sort) = &plan
265 && target_partition >= regions.len()
266 {
267 let lex_ordering = sort
268 .expr
269 .iter()
270 .map(|sort_expr| {
271 let physical_expr = session_state
272 .create_physical_expr(sort_expr.expr.clone(), plan.schema())?;
273 Ok(PhysicalSortExpr::new(
274 physical_expr,
275 SortOptions {
276 descending: !sort_expr.asc,
277 nulls_first: sort_expr.nulls_first,
278 },
279 ))
280 })
281 .collect::<Result<Vec<_>>>()?;
282 EquivalenceProperties::new_with_orderings(arrow_schema.clone(), vec![lex_ordering])
283 } else {
284 EquivalenceProperties::new(arrow_schema.clone())
285 };
286
287 let partition_exprs = partition_cols
288 .iter()
289 .filter_map(|col| {
290 if let Some(first_alias) = col.1.first() {
291 session_state
292 .create_physical_expr(
293 Expr::Column(ColumnExpr::new_unqualified(
294 first_alias.name().to_string(),
295 )),
296 plan.schema(),
297 )
298 .ok()
299 } else {
300 None
301 }
302 })
303 .collect();
304 let partitioning = Partitioning::Hash(partition_exprs, target_partition);
305
306 let properties = Arc::new(PlanProperties::new(
307 eq_properties,
308 partitioning,
309 EmissionType::Incremental,
310 Boundedness::Bounded,
311 ));
312 Ok(Self {
313 table,
314 regions,
315 plan,
316 arrow_schema,
317 region_query_handler,
318 metric: ExecutionPlanMetricsSet::new(),
319 sub_stage_metrics: Arc::default(),
320 partition_metrics: Arc::default(),
321 properties,
322 query_ctx,
323 remote_dyn_filter_producer_id,
324 captured_remote_dyn_filters: Arc::default(),
325 target_partition,
326 partition_cols,
327 enable_per_region_metrics,
328 })
329 }
330
331 pub fn to_stream(
332 &self,
333 context: Arc<TaskContext>,
334 partition: usize,
335 ) -> Result<SendableRecordBatchStream> {
336 let regions = self.regions.clone();
338 let region_query_handler = self.region_query_handler.clone();
339 let metric = MergeScanMetric::new(&self.metric);
340 let arrow_schema = self.arrow_schema.clone();
341 let query_ctx = self.query_ctx.clone();
342 let sub_stage_metrics_moved = self.sub_stage_metrics.clone();
343 let partition_metrics_moved = self.partition_metrics.clone();
344 let plan = self.plan.clone();
345 let target_partition = self.target_partition;
346 let remote_dyn_filter_enabled = remote_dyn_filter_enabled(&self.query_ctx)?;
347 let captured_remote_dyn_filters = if remote_dyn_filter_enabled {
348 self.captured_remote_dyn_filters()
349 } else {
350 Vec::new()
351 };
352 let dbname = context.task_id().unwrap_or_default();
353 let tracing_context = TracingContext::from_json(context.session_id().as_str());
354 let current_channel = self.query_ctx.channel();
355 let read_preference = self.query_ctx.read_preference();
356 let explain_verbose = self.query_ctx.explain_verbose();
357 let remote_dyn_filter_registry_lease = acquire_remote_dyn_filter_registry_lease(
358 context.as_ref(),
359 &query_ctx,
360 &captured_remote_dyn_filters,
361 );
362
363 let stream = Box::pin(stream!({
364 let remote_dyn_filter_registry_lease = remote_dyn_filter_registry_lease;
365 if partition == 0 {
367 MERGE_SCAN_REGIONS.observe(regions.len() as f64);
368 }
369
370 let _finish_timer = metric.finish_time().timer();
371 let mut ready_timer = metric.ready_time().timer();
372 let mut first_consume_timer = Some(metric.first_consume_time().timer());
373
374 let partition_start = Instant::now();
376 let mut partition_ready_time: Option<Duration> = None;
377 let mut partition_first_consume_time: Option<Duration> = None;
378
379 for region_id in regions
380 .iter()
381 .skip(partition)
382 .step_by(target_partition)
383 .copied()
384 {
385 let region_span = tracing_context.attach(tracing::info_span!(
386 parent: &Span::current(),
387 "merge_scan_region",
388 region_id = %region_id,
389 partition = partition
390 ));
391 let region_query_ctx = query_context_for_remote_dyn_filter_region(
392 &query_ctx,
393 region_id,
394 remote_dyn_filter_registry_lease.as_ref(),
395 &captured_remote_dyn_filters,
396 );
397 let request = QueryRequest {
398 header: Some(RegionRequestHeader {
399 tracing_context: tracing_context.to_w3c(),
400 dbname: dbname.clone(),
401 query_context: Some((®ion_query_ctx).into()),
402 }),
403 region_id,
404 plan: plan.clone(),
405 };
406 let region_start = Instant::now();
407 let do_get_start = Instant::now();
408
409 if explain_verbose {
410 common_telemetry::info!(
411 "Merge scan one region, partition: {}, region_id: {}",
412 partition,
413 region_id
414 );
415 }
416
417 let mut stream = region_query_handler
418 .do_get(read_preference, request)
419 .instrument(region_span.clone())
420 .await
421 .map_err(|e| {
422 MERGE_SCAN_ERRORS_TOTAL.inc();
423 DataFusionError::External(Box::new(e))
424 })?;
425 let do_get_cost = do_get_start.elapsed();
426
427 if let Some(remote_dyn_filter_registry_lease) =
428 remote_dyn_filter_registry_lease.as_ref()
429 {
430 remote_dyn_filter_registry_lease
431 .ensure_fanout_task(region_query_handler.clone());
432 }
433
434 ready_timer.stop();
435 if partition_ready_time.is_none() {
436 partition_ready_time = Some(partition_start.elapsed());
437 }
438
439 let mut poll_duration = Duration::ZERO;
440 let mut poll_timer = Instant::now();
441 while let Some(batch) = stream.next().instrument(region_span.clone()).await {
442 let poll_elapsed = poll_timer.elapsed();
443 poll_duration += poll_elapsed;
444
445 let batch = batch.map_err(|e| DataFusionError::External(Box::new(e)))?;
446 let batch = patch_batch_timezone(
447 arrow_schema.clone(),
448 batch.into_df_record_batch().columns().to_vec(),
449 )?;
450 metric.record_output_batch_rows(batch.num_rows());
451 if let Some(mut first_consume_timer) = first_consume_timer.take() {
452 first_consume_timer.stop();
453 partition_first_consume_time = Some(partition_start.elapsed());
454 }
455
456 if let Some(metrics) = stream.metrics() {
457 let mut sub_stage_metrics = sub_stage_metrics_moved.lock().unwrap();
458 sub_stage_metrics.insert(region_id, metrics);
459 }
460
461 yield Ok(batch);
462 poll_timer = Instant::now();
464 }
465 if let Some(mut first_consume_timer) = first_consume_timer.take() {
468 first_consume_timer.stop();
469 partition_first_consume_time = Some(partition_start.elapsed());
470 }
471 let total_cost = region_start.elapsed();
472
473 let region_metrics = RegionMetrics {
475 region_id,
476 poll_duration,
477 do_get_cost,
478 total_cost,
479 };
480
481 {
483 let mut partition_metrics_guard = partition_metrics_moved.lock().unwrap();
484 let partition_metrics = partition_metrics_guard
485 .entry(partition)
486 .or_insert_with(|| PartitionMetrics::new(partition, explain_verbose));
487 partition_metrics.add_region_metrics(region_metrics);
488 }
489
490 if explain_verbose {
491 common_telemetry::info!(
492 "Merge scan finish one region, partition: {}, region_id: {}, poll_duration: {:?}, first_consume: {}, do_get_cost: {:?}",
493 partition,
494 region_id,
495 poll_duration,
496 metric.first_consume_time(),
497 do_get_cost
498 );
499 }
500
501 if let Some(metrics) = stream.metrics() {
503 let load = region_scan_load(&metrics);
504 let (c, s) = parse_catalog_and_schema_from_db_string(&dbname);
505 let value = read_meter!(c, s, load, current_channel as u8);
506 metric.record_greptime_exec_cost(value as usize);
507
508 let mut sub_stage_metrics = sub_stage_metrics_moved.lock().unwrap();
510 sub_stage_metrics.insert(region_id, metrics);
511 }
512
513 MERGE_SCAN_POLL_ELAPSED.observe(poll_duration.as_secs_f64());
514 }
515
516 ready_timer.stop();
519 if let Some(mut first_consume_timer) = first_consume_timer.take() {
520 first_consume_timer.stop();
521 }
522
523 let partition_finish_time = partition_start.elapsed();
525 {
526 let mut partition_metrics_guard = partition_metrics_moved.lock().unwrap();
527 if let Some(partition_metrics) = partition_metrics_guard.get_mut(&partition) {
528 partition_metrics.set_timings(
529 partition_ready_time.unwrap_or_default(),
530 partition_first_consume_time.unwrap_or_default(),
531 partition_finish_time,
532 );
533 partition_metrics.finish();
534 }
535 }
536 }));
537
538 Ok(Box::pin(RecordBatchStreamAdapter::new(
539 self.arrow_schema.clone(),
540 stream,
541 )))
542 }
543
544 pub fn try_with_new_distribution(&self, distribution: Distribution) -> Option<Self> {
545 let Distribution::HashPartitioned(hash_exprs) = distribution else {
546 return None;
548 };
549
550 if let Partitioning::Hash(curr_dist, _) = &self.properties.partitioning
551 && curr_dist == &hash_exprs
552 {
553 return None;
555 }
556
557 let all_partition_col_aliases: HashSet<_> = self
558 .partition_cols
559 .values()
560 .flat_map(|aliases| aliases.iter().map(|c| c.name()))
561 .collect();
562 let overlaps: Vec<_> = hash_exprs
563 .iter()
564 .filter(|expr| {
565 expr.as_any()
566 .downcast_ref::<Column>()
567 .is_some_and(|col_expr| all_partition_col_aliases.contains(col_expr.name()))
568 })
569 .cloned()
570 .collect();
571
572 if overlaps.is_empty() {
573 return None;
574 }
575
576 Some(Self {
577 table: self.table.clone(),
578 regions: self.regions.clone(),
579 plan: self.plan.clone(),
580 arrow_schema: self.arrow_schema.clone(),
581 region_query_handler: self.region_query_handler.clone(),
582 metric: self.metric.clone(),
583 properties: Arc::new(PlanProperties::new(
584 self.properties.eq_properties.clone(),
585 Partitioning::Hash(overlaps, self.target_partition),
586 self.properties.emission_type,
587 self.properties.boundedness,
588 )),
589 sub_stage_metrics: self.sub_stage_metrics.clone(),
590 partition_metrics: self.partition_metrics.clone(),
591 query_ctx: self.query_ctx.clone(),
592 remote_dyn_filter_producer_id: self.remote_dyn_filter_producer_id,
593 captured_remote_dyn_filters: self.captured_remote_dyn_filters.clone(),
594 target_partition: self.target_partition,
595 partition_cols: self.partition_cols.clone(),
596 enable_per_region_metrics: self.enable_per_region_metrics,
597 })
598 }
599
600 fn captured_remote_dyn_filters(&self) -> Vec<CapturedDynFilter> {
601 self.captured_remote_dyn_filters.lock().unwrap().clone()
602 }
603
604 pub fn sub_stage_metrics(&self) -> Vec<RecordBatchMetrics> {
605 self.sub_stage_metrics
606 .lock()
607 .unwrap()
608 .values()
609 .cloned()
610 .collect()
611 }
612
613 pub fn regions(&self) -> &[RegionId] {
614 &self.regions
615 }
616
617 pub fn is_flow_sink_scan(&self) -> bool {
618 let Some(sink_table_id) =
619 FlowQueryExtensions::parse_flow_extensions(&self.query_ctx.extensions())
620 .ok()
621 .flatten()
622 .and_then(|extensions| extensions.sink_table_id)
623 else {
624 return false;
625 };
626
627 !self.regions.is_empty()
628 && self
629 .regions
630 .iter()
631 .all(|region_id| region_id.table_id() == sink_table_id)
632 }
633
634 pub fn partition_count(&self) -> usize {
635 self.target_partition
636 }
637
638 pub fn region_count(&self) -> usize {
639 self.regions.len()
640 }
641
642 fn partition_metrics(&self) -> Vec<PartitionMetrics> {
643 self.partition_metrics
644 .lock()
645 .unwrap()
646 .values()
647 .cloned()
648 .collect()
649 }
650}
651
652#[cfg(test)]
653impl MergeScanExec {
654 fn remote_dyn_filter_producer_id(&self) -> Option<RemoteDynFilterProducerId> {
655 self.remote_dyn_filter_producer_id
656 }
657}
658
659#[derive(Debug, Clone)]
661struct RegionMetrics {
662 region_id: RegionId,
663 poll_duration: Duration,
664 do_get_cost: Duration,
665 total_cost: Duration,
667}
668
669#[derive(Debug, Clone)]
671struct PartitionMetrics {
672 partition: usize,
673 region_metrics: Vec<RegionMetrics>,
674 total_poll_duration: Duration,
675 total_do_get_cost: Duration,
676 total_regions: usize,
677 ready_time: Duration,
679 first_consume_time: Duration,
681 finish_time: Duration,
683 explain_verbose: bool,
684 finished: bool,
685}
686
687impl PartitionMetrics {
688 fn new(partition: usize, explain_verbose: bool) -> Self {
689 Self {
690 partition,
691 region_metrics: Vec::new(),
692 total_poll_duration: Duration::ZERO,
693 total_do_get_cost: Duration::ZERO,
694 total_regions: 0,
695 ready_time: Duration::ZERO,
696 first_consume_time: Duration::ZERO,
697 finish_time: Duration::ZERO,
698 explain_verbose,
699 finished: false,
700 }
701 }
702
703 fn add_region_metrics(&mut self, region_metrics: RegionMetrics) {
704 self.total_poll_duration += region_metrics.poll_duration;
705 self.total_do_get_cost += region_metrics.do_get_cost;
706 self.total_regions += 1;
707 self.region_metrics.push(region_metrics);
708 }
709
710 fn set_timings(
712 &mut self,
713 ready_time: Duration,
714 first_consume_time: Duration,
715 finish_time: Duration,
716 ) {
717 self.ready_time = ready_time;
718 self.first_consume_time = first_consume_time;
719 self.finish_time = finish_time;
720 }
721
722 fn finish(&mut self) {
724 if self.finished {
725 return;
726 }
727 self.finished = true;
728 self.log_metrics();
729 }
730
731 fn log_metrics(&self) {
733 if self.explain_verbose {
734 common_telemetry::info!(
735 "MergeScan partition {} finished: {} regions, total_poll_duration: {:?}, total_do_get_cost: {:?}, ready_time: {:?}, first_consume_time: {:?}, finish_time: {:?}",
736 self.partition,
737 self.total_regions,
738 self.total_poll_duration,
739 self.total_do_get_cost,
740 self.ready_time,
741 self.first_consume_time,
742 self.finish_time
743 );
744 } else {
745 common_telemetry::debug!(
746 "MergeScan partition {} finished: {} regions, total_poll_duration: {:?}, total_do_get_cost: {:?}, ready_time: {:?}, first_consume_time: {:?}, finish_time: {:?}",
747 self.partition,
748 self.total_regions,
749 self.total_poll_duration,
750 self.total_do_get_cost,
751 self.ready_time,
752 self.first_consume_time,
753 self.finish_time
754 );
755 }
756 }
757}
758
759impl Drop for PartitionMetrics {
760 fn drop(&mut self) {
761 if !self.finished {
762 self.log_metrics();
763 }
764 }
765}
766
767impl ExecutionPlan for MergeScanExec {
768 fn as_any(&self) -> &dyn Any {
769 self
770 }
771
772 fn schema(&self) -> ArrowSchemaRef {
773 self.arrow_schema.clone()
774 }
775
776 fn properties(&self) -> &Arc<PlanProperties> {
777 &self.properties
778 }
779
780 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
781 vec![]
782 }
783
784 fn with_new_children(
787 self: Arc<Self>,
788 _children: Vec<Arc<dyn ExecutionPlan>>,
789 ) -> Result<Arc<dyn ExecutionPlan>> {
790 Ok(self.clone())
791 }
792
793 fn handle_child_pushdown_result(
794 &self,
795 _phase: FilterPushdownPhase,
796 child_pushdown_result: ChildPushdownResult,
797 _config: &datafusion::config::ConfigOptions,
798 ) -> Result<FilterPushdownPropagation<Arc<dyn ExecutionPlan>>> {
799 let parent_filters = child_pushdown_result
800 .parent_filters
801 .into_iter()
802 .map(|filter| filter.filter)
803 .collect::<Vec<_>>();
804
805 if !remote_dyn_filter_enabled(&self.query_ctx)? {
806 self.captured_remote_dyn_filters.lock().unwrap().clear();
810 let new_self = Arc::new(self.clone());
811
812 return Ok(FilterPushdownPropagation {
813 filters: parent_filters.into_iter().map(|_| PushedDown::No).collect(),
814 updated_node: Some(new_self),
815 });
816 }
817
818 let Some(remote_dyn_filter_producer_id) = self.remote_dyn_filter_producer_id else {
819 common_telemetry::warn!(
821 "MergeScan remote dynamic filter producer id is not assigned; skipping remote dynamic filter pushdown"
822 );
823 self.captured_remote_dyn_filters.lock().unwrap().clear();
824 let new_self = Arc::new(self.clone());
825
826 return Ok(FilterPushdownPropagation {
827 filters: parent_filters.into_iter().map(|_| PushedDown::No).collect(),
828 updated_node: Some(new_self),
829 });
830 };
831 let remote_dyn_filter_pushdown =
832 capture_remote_dyn_filters_for_pushdown(remote_dyn_filter_producer_id, parent_filters);
833 *self.captured_remote_dyn_filters.lock().unwrap() =
834 remote_dyn_filter_pushdown.captured_dyn_filters;
835 let new_self = Arc::new(self.clone());
836
837 Ok(FilterPushdownPropagation {
838 filters: remote_dyn_filter_pushdown
839 .pushed_down
840 .into_iter()
841 .map(|pushdown_ready| {
842 if pushdown_ready {
843 PushedDown::Yes
844 } else {
845 PushedDown::No
846 }
847 })
848 .collect(),
849 updated_node: Some(new_self),
850 })
851 }
852
853 fn execute(
854 &self,
855 partition: usize,
856 context: Arc<TaskContext>,
857 ) -> Result<SendableRecordBatchStream> {
858 self.to_stream(context, partition)
859 }
860
861 fn metrics(&self) -> Option<MetricsSet> {
862 Some(self.metric.clone_inner())
863 }
864
865 fn name(&self) -> &str {
866 "MergeScanExec"
867 }
868}
869
870impl DisplayAs for MergeScanExec {
871 fn fmt_as(&self, t: DisplayFormatType, f: &mut std::fmt::Formatter) -> std::fmt::Result {
872 write!(f, "MergeScanExec: peers=[")?;
873 for region_id in self.regions.iter() {
874 write!(f, "{}, ", region_id)?;
875 }
876 write!(f, "]")?;
877
878 if matches!(t, DisplayFormatType::Verbose) {
879 let partition_metrics = self.partition_metrics();
880 if !partition_metrics.is_empty() {
881 write!(f, ", metrics={{")?;
882 for (i, pm) in partition_metrics.iter().enumerate() {
883 if i > 0 {
884 write!(f, ", ")?;
885 }
886 write!(
887 f,
888 "\"partition_{}\":{{\"regions\":{},\"total_poll_duration\":\"{:?}\",\"total_do_get_cost\":\"{:?}\",\"ready_time\":\"{:?}\",\"first_consume_time\":\"{:?}\",\"finish_time\":\"{:?}\",\"region_metrics\":[",
889 pm.partition,
890 pm.total_regions,
891 pm.total_poll_duration,
892 pm.total_do_get_cost,
893 pm.ready_time,
894 pm.first_consume_time,
895 pm.finish_time
896 )?;
897 for (j, rm) in pm.region_metrics.iter().enumerate() {
898 if j > 0 {
899 write!(f, ",")?;
900 }
901 write!(
902 f,
903 "{{\"region_id\":\"{}\",\"poll_duration\":\"{:?}\",\"do_get_cost\":\"{:?}\",\"total_cost\":\"{:?}\"}}",
904 rm.region_id, rm.poll_duration, rm.do_get_cost, rm.total_cost
905 )?;
906 }
907 write!(f, "]}}")?;
908 }
909 write!(f, "}}")?;
910 }
911 }
912
913 Ok(())
914 }
915}
916
917fn region_scan_load(metrics: &RecordBatchMetrics) -> ReadItem {
918 ReadItem {
919 cpu_time: metrics.elapsed_compute as u64,
920 table_scan: region_scan_output_bytes(metrics) as u64,
921 }
922}
923
924fn report_region_query_load(region_id: RegionId, load: &ReadItem) {
925 let region_id = region_id.to_string();
926 REGION_QUERY_CPU_TIME
927 .with_label_values(&[®ion_id])
928 .inc_by(load.cpu_time);
929 REGION_QUERY_SCANNED_BYTES
930 .with_label_values(&[®ion_id])
931 .inc_by(load.table_scan);
932}
933
934fn query_load_region_id(default_region_id: RegionId, metrics: &RecordBatchMetrics) -> RegionId {
935 metrics
936 .query_load_region_id
937 .map(RegionId::from_u64)
938 .unwrap_or(default_region_id)
939}
940
941impl Drop for MergeScanExec {
942 fn drop(&mut self) {
943 if !self.enable_per_region_metrics {
947 return;
948 }
949
950 let metrics = self.sub_stage_metrics.lock().unwrap();
951 for (region_id, metrics) in metrics.iter() {
952 let load = region_scan_load(metrics);
953 report_region_query_load(query_load_region_id(*region_id, metrics), &load);
954 }
955 }
956}
957
958#[derive(Debug, Clone)]
959struct MergeScanMetric {
960 ready_time: Time,
962 first_consume_time: Time,
964 finish_time: Time,
966 output_rows: Count,
968
969 greptime_exec_cost: Gauge,
971}
972
973impl MergeScanMetric {
974 pub fn new(metric: &ExecutionPlanMetricsSet) -> Self {
975 Self {
976 ready_time: MetricBuilder::new(metric).subset_time("ready_time", 1),
977 first_consume_time: MetricBuilder::new(metric).subset_time("first_consume_time", 1),
978 finish_time: MetricBuilder::new(metric).subset_time("finish_time", 1),
979 output_rows: MetricBuilder::new(metric).output_rows(1),
980 greptime_exec_cost: MetricBuilder::new(metric).gauge(GREPTIME_EXEC_READ_COST, 1),
981 }
982 }
983
984 pub fn ready_time(&self) -> &Time {
985 &self.ready_time
986 }
987
988 pub fn first_consume_time(&self) -> &Time {
989 &self.first_consume_time
990 }
991
992 pub fn finish_time(&self) -> &Time {
993 &self.finish_time
994 }
995
996 pub fn record_output_batch_rows(&self, num_rows: usize) {
997 self.output_rows.add(num_rows);
998 }
999
1000 pub fn record_greptime_exec_cost(&self, metrics: usize) {
1001 self.greptime_exec_cost.add(metrics);
1002 }
1003}
1004
1005#[cfg(test)]
1006mod tests {
1007 use std::collections::BTreeSet;
1008
1009 use async_trait::async_trait;
1010 use common_query::request::INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY;
1011 use common_recordbatch::adapter::{PlanMetrics, RecordBatchMetrics};
1012 use datafusion::config::ConfigOptions;
1013 use datafusion::execution::SessionStateBuilder;
1014 use datafusion::physical_plan::filter_pushdown::ChildFilterPushdownResult;
1015 use datafusion_common::TableReference;
1016 use datafusion_expr::{LogicalPlanBuilder, lit};
1017 use datafusion_physical_expr::Distribution;
1018 use datafusion_physical_expr::expressions::{
1019 Column, DynamicFilterPhysicalExpr, lit as physical_lit,
1020 };
1021 use session::ReadPreference;
1022 use session::context::QueryContext;
1023 use session::query_id::QueryId;
1024 use table::table::scan::REGION_SCAN_EXEC_NAME;
1025 use table::table_name::TableName;
1026 use uuid::Uuid;
1027
1028 use super::*;
1029 use crate::dist_plan::{DynFilterRegistryManager, Subscriber};
1030 use crate::region_query::RegionQueryHandler;
1031
1032 fn test_query_id(value: u128) -> QueryId {
1033 QueryId::from(Uuid::from_u128(value))
1034 }
1035
1036 #[test]
1037 fn remote_dyn_filter_region_query_context_registers_before_do_get() {
1038 let registry_manager = Arc::new(DynFilterRegistryManager::default());
1039 let query_ctx = QueryContext::arc();
1040 let query_id = query_ctx
1041 .remote_query_id_value()
1042 .expect("query context must have remote query id");
1043 let lease = registry_manager.acquire_lease(query_id);
1044 let region_id = RegionId::new(1024, 7);
1045 let dyn_filter = Arc::new(DynamicFilterPhysicalExpr::new(
1046 vec![Arc::new(Column::new("host", 0)) as Arc<_>],
1047 physical_lit(true) as _,
1048 )) as Arc<dyn datafusion_physical_expr::PhysicalExpr>;
1049 let captured = capture_remote_dyn_filters_for_pushdown(
1050 RemoteDynFilterProducerId::new(42),
1051 vec![dyn_filter],
1052 );
1053 assert_eq!(captured.captured_dyn_filters.len(), 1);
1054
1055 let region_query_ctx = query_context_for_remote_dyn_filter_region(
1056 &query_ctx,
1057 region_id,
1058 Some(&lease),
1059 &captured.captured_dyn_filters,
1060 );
1061
1062 let entries = lease.registry().entries();
1063 assert_eq!(entries.len(), 1);
1064 assert_eq!(entries[0].subscribers(), vec![Subscriber::new(region_id)]);
1065 assert!(
1066 !entries[0].fanout_started_for_test(),
1067 "fanout must start only after do_get succeeds"
1068 );
1069 assert!(
1070 region_query_ctx
1071 .extension(INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY)
1072 .is_some(),
1073 "initial RDF registrations must be present in the do_get query context"
1074 );
1075 }
1076
1077 #[test]
1078 fn remote_dyn_filter_registry_cleanup_waits_for_last_query_scoped_stream_drop() {
1079 let registry_manager = Arc::new(DynFilterRegistryManager::default());
1080 let query_id = test_query_id(1);
1081
1082 let first = registry_manager.acquire_lease(query_id);
1083 let second = registry_manager.acquire_lease(query_id);
1084
1085 drop(first);
1086 assert_eq!(registry_manager.registry_count(), 1);
1087
1088 drop(second);
1089 assert_eq!(registry_manager.registry_count(), 0);
1090 }
1091
1092 #[test]
1093 fn remote_dyn_filter_registry_cleanup_shares_query_scope_across_independent_leases() {
1094 let registry_manager = Arc::new(DynFilterRegistryManager::default());
1095 let query_id = test_query_id(1);
1096
1097 let first_exec_like_lease = registry_manager.acquire_lease(query_id);
1098 let second_exec_like_lease = registry_manager.acquire_lease(query_id);
1099
1100 drop(first_exec_like_lease);
1101 assert_eq!(registry_manager.registry_count(), 1);
1102
1103 drop(second_exec_like_lease);
1104 assert_eq!(registry_manager.registry_count(), 0);
1105 }
1106
1107 struct TestRegionQueryHandler;
1108
1109 #[async_trait]
1110 impl RegionQueryHandler for TestRegionQueryHandler {
1111 async fn do_get(
1112 &self,
1113 _read_preference: ReadPreference,
1114 _request: common_query::request::QueryRequest,
1115 ) -> crate::error::Result<common_recordbatch::SendableRecordBatchStream> {
1116 unimplemented!("test only")
1117 }
1118
1119 async fn handle_remote_dyn_filter_update(
1120 &self,
1121 _region_id: RegionId,
1122 _query_id: String,
1123 _update: api::v1::region::RemoteDynFilterUpdate,
1124 ) -> crate::error::Result<()> {
1125 unimplemented!("test only")
1126 }
1127
1128 async fn handle_remote_dyn_filter_unregister(
1129 &self,
1130 _region_id: RegionId,
1131 _query_id: String,
1132 _unregister: api::v1::region::RemoteDynFilterUnregister,
1133 ) -> crate::error::Result<()> {
1134 unimplemented!("test only")
1135 }
1136 }
1137
1138 #[test]
1139 fn try_with_new_distribution_preserves_remote_dyn_filter_producer_id() {
1140 let remote_dyn_filter_producer_id = RemoteDynFilterProducerId::new(42);
1141
1142 let plan = LogicalPlanBuilder::empty(true)
1144 .project(vec![lit(1i32).alias("col1")])
1145 .unwrap()
1146 .build()
1147 .unwrap();
1148
1149 let schema = plan.schema().as_arrow().clone();
1150 let table = TableName::new("catalog", "schema", "table");
1151 let regions = vec![RegionId::new(1024, 1)];
1152 let query_ctx = QueryContext::arc();
1153
1154 let mut partition_cols = AliasMapping::new();
1156 partition_cols.insert(
1157 "col1".to_string(),
1158 BTreeSet::from([ColumnExpr::new(Some(TableReference::bare("table")), "col1")]),
1159 );
1160
1161 let session_state = SessionStateBuilder::new().build();
1162
1163 let handler = Arc::new(TestRegionQueryHandler);
1164 let target_partition = 2;
1165
1166 let exec = MergeScanExec::new(
1167 &session_state,
1168 table,
1169 regions,
1170 plan,
1171 &schema,
1172 handler,
1173 query_ctx,
1174 target_partition,
1175 partition_cols,
1176 Some(remote_dyn_filter_producer_id),
1177 false,
1178 )
1179 .unwrap();
1180
1181 assert_eq!(
1182 exec.remote_dyn_filter_producer_id(),
1183 Some(remote_dyn_filter_producer_id)
1184 );
1185
1186 let new_dist = Distribution::HashPartitioned(vec![
1190 Arc::new(Column::new("col1", 0)),
1191 Arc::new(Column::new("col2", 1)),
1192 ]);
1193
1194 let cloned = exec
1195 .try_with_new_distribution(new_dist)
1196 .expect("expected a cloned exec with overlapping partition col");
1197
1198 assert_eq!(
1199 cloned.remote_dyn_filter_producer_id(),
1200 Some(remote_dyn_filter_producer_id),
1201 "try_with_new_distribution must preserve remote dynamic filter producer id"
1202 );
1203 }
1204
1205 #[test]
1206 fn remote_dyn_filter_preflight_removes_parent_filter_after_dn_runtime_is_ready() {
1207 let remote_dyn_filter_producer_id = RemoteDynFilterProducerId::new(42);
1208 let plan = LogicalPlanBuilder::empty(true)
1209 .project(vec![lit(1i32).alias("col1")])
1210 .unwrap()
1211 .build()
1212 .unwrap();
1213
1214 let schema = plan.schema().as_arrow().clone();
1215 let table = TableName::new("catalog", "schema", "table");
1216 let regions = vec![RegionId::new(1024, 1)];
1217 let query_ctx = QueryContext::arc();
1218 let session_state = SessionStateBuilder::new().build();
1219 let handler = Arc::new(TestRegionQueryHandler);
1220 let exec = MergeScanExec::new(
1221 &session_state,
1222 table,
1223 regions,
1224 plan,
1225 &schema,
1226 handler,
1227 query_ctx,
1228 1,
1229 AliasMapping::new(),
1230 Some(remote_dyn_filter_producer_id),
1231 false,
1232 )
1233 .unwrap();
1234 let dyn_filter = Arc::new(DynamicFilterPhysicalExpr::new(
1235 vec![Arc::new(Column::new("host", 0)) as Arc<_>],
1236 physical_lit(true) as _,
1237 )) as Arc<dyn datafusion_physical_expr::PhysicalExpr>;
1238
1239 let propagation = exec
1240 .handle_child_pushdown_result(
1241 FilterPushdownPhase::Post,
1242 ChildPushdownResult {
1243 parent_filters: vec![ChildFilterPushdownResult {
1244 filter: dyn_filter,
1245 child_results: vec![PushedDown::Yes],
1246 }],
1247 self_filters: Vec::new(),
1248 },
1249 &ConfigOptions::new(),
1250 )
1251 .unwrap();
1252
1253 assert_eq!(exec.captured_remote_dyn_filters().len(), 1);
1254 assert!(matches!(propagation.filters.as_slice(), [PushedDown::Yes]));
1255 }
1256
1257 #[test]
1258 fn scan_output_bytes_uses_plan_name() {
1259 let metrics = RecordBatchMetrics {
1260 plan_metrics: vec![PlanMetrics {
1261 plan: "SeqScan: region=1".to_string(),
1262 plan_name: REGION_SCAN_EXEC_NAME.to_string(),
1263 level: 0,
1264 metrics: vec![("output_bytes".to_string(), 42)],
1265 }],
1266 ..Default::default()
1267 };
1268
1269 assert_eq!(region_scan_output_bytes(&metrics), 42);
1270 }
1271
1272 #[test]
1273 fn scan_output_bytes_defaults_to_zero_without_region_scan() {
1274 let metrics = RecordBatchMetrics {
1275 plan_metrics: vec![PlanMetrics {
1276 plan: "ProjectionExec".to_string(),
1277 plan_name: "ProjectionExec".to_string(),
1278 level: 0,
1279 metrics: vec![("output_bytes".to_string(), 42)],
1280 }],
1281 ..Default::default()
1282 };
1283
1284 assert_eq!(region_scan_output_bytes(&metrics), 0);
1285 }
1286
1287 #[test]
1288 fn scan_output_bytes_sums_multiple_region_scans() {
1289 let metrics = RecordBatchMetrics {
1290 plan_metrics: vec![
1291 PlanMetrics {
1292 plan: "RegionScanExec: region=1".to_string(),
1293 plan_name: REGION_SCAN_EXEC_NAME.to_string(),
1294 level: 0,
1295 metrics: vec![("output_bytes".to_string(), 42)],
1296 },
1297 PlanMetrics {
1298 plan: "RegionScanExec: region=2".to_string(),
1299 plan_name: REGION_SCAN_EXEC_NAME.to_string(),
1300 level: 0,
1301 metrics: vec![("output_bytes".to_string(), 18)],
1302 },
1303 ],
1304 ..Default::default()
1305 };
1306
1307 assert_eq!(region_scan_output_bytes(&metrics), 60);
1308 }
1309
1310 #[test]
1311 fn merge_scan_reports_region_query_load_on_drop() {
1312 use store_api::metrics::{REGION_QUERY_CPU_TIME, REGION_QUERY_SCANNED_BYTES};
1313
1314 let region_id = RegionId::new(1024, 10002);
1315 let region_id_label = region_id.to_string();
1316 let labels = [®ion_id_label];
1317 let _ = REGION_QUERY_CPU_TIME.remove_label_values(&labels);
1318 let _ = REGION_QUERY_SCANNED_BYTES.remove_label_values(&labels);
1319
1320 let plan = LogicalPlanBuilder::empty(true)
1321 .project(vec![lit(1i32).alias("col1")])
1322 .unwrap()
1323 .build()
1324 .unwrap();
1325 let schema = plan.schema().as_arrow().clone();
1326 let exec = MergeScanExec::new(
1327 &SessionStateBuilder::new().build(),
1328 TableName::new("catalog", "schema", "table"),
1329 vec![region_id],
1330 plan,
1331 &schema,
1332 Arc::new(TestRegionQueryHandler),
1333 QueryContext::arc(),
1334 1,
1335 AliasMapping::new(),
1336 None,
1337 true,
1338 )
1339 .unwrap();
1340
1341 let metrics = RecordBatchMetrics {
1342 elapsed_compute: 42,
1343 plan_metrics: vec![PlanMetrics {
1344 plan: "RegionScanExec: region=1".to_string(),
1345 plan_name: REGION_SCAN_EXEC_NAME.to_string(),
1346 level: 0,
1347 metrics: vec![("output_bytes".to_string(), 24)],
1348 }],
1349 ..Default::default()
1350 };
1351 exec.sub_stage_metrics
1352 .lock()
1353 .unwrap()
1354 .insert(region_id, metrics);
1355
1356 assert_eq!(REGION_QUERY_CPU_TIME.with_label_values(&labels).get(), 0);
1357 assert_eq!(
1358 REGION_QUERY_SCANNED_BYTES.with_label_values(&labels).get(),
1359 0
1360 );
1361
1362 drop(exec);
1363
1364 assert_eq!(REGION_QUERY_CPU_TIME.with_label_values(&labels).get(), 42);
1365 assert_eq!(
1366 REGION_QUERY_SCANNED_BYTES.with_label_values(&labels).get(),
1367 24
1368 );
1369
1370 let _ = REGION_QUERY_CPU_TIME.remove_label_values(&labels);
1371 let _ = REGION_QUERY_SCANNED_BYTES.remove_label_values(&labels);
1372 }
1373
1374 #[test]
1375 fn merge_scan_reports_query_load_with_metrics_region_id() {
1376 use store_api::metrics::{REGION_QUERY_CPU_TIME, REGION_QUERY_SCANNED_BYTES};
1377
1378 let logical_region_id = RegionId::new(1024, 10002);
1379 let physical_region_id = RegionId::new(1024, 1);
1380 let logical_region_id_label = logical_region_id.to_string();
1381 let physical_region_id_label = physical_region_id.to_string();
1382 let logical_labels = [&logical_region_id_label];
1383 let physical_labels = [&physical_region_id_label];
1384 let _ = REGION_QUERY_CPU_TIME.remove_label_values(&logical_labels);
1385 let _ = REGION_QUERY_SCANNED_BYTES.remove_label_values(&logical_labels);
1386 let _ = REGION_QUERY_CPU_TIME.remove_label_values(&physical_labels);
1387 let _ = REGION_QUERY_SCANNED_BYTES.remove_label_values(&physical_labels);
1388
1389 let plan = LogicalPlanBuilder::empty(true)
1390 .project(vec![lit(1i32).alias("col1")])
1391 .unwrap()
1392 .build()
1393 .unwrap();
1394 let schema = plan.schema().as_arrow().clone();
1395 let exec = MergeScanExec::new(
1396 &SessionStateBuilder::new().build(),
1397 TableName::new("catalog", "schema", "table"),
1398 vec![logical_region_id],
1399 plan,
1400 &schema,
1401 Arc::new(TestRegionQueryHandler),
1402 QueryContext::arc(),
1403 1,
1404 AliasMapping::new(),
1405 None,
1406 true,
1407 )
1408 .unwrap();
1409
1410 let metrics = RecordBatchMetrics {
1411 elapsed_compute: 42,
1412 query_load_region_id: Some(physical_region_id.as_u64()),
1413 plan_metrics: vec![PlanMetrics {
1414 plan: "RegionScanExec: region=1".to_string(),
1415 plan_name: REGION_SCAN_EXEC_NAME.to_string(),
1416 level: 0,
1417 metrics: vec![("output_bytes".to_string(), 24)],
1418 }],
1419 ..Default::default()
1420 };
1421 exec.sub_stage_metrics
1422 .lock()
1423 .unwrap()
1424 .insert(logical_region_id, metrics);
1425
1426 drop(exec);
1427
1428 assert_eq!(
1429 REGION_QUERY_CPU_TIME
1430 .with_label_values(&logical_labels)
1431 .get(),
1432 0
1433 );
1434 assert_eq!(
1435 REGION_QUERY_SCANNED_BYTES
1436 .with_label_values(&logical_labels)
1437 .get(),
1438 0
1439 );
1440 assert_eq!(
1441 REGION_QUERY_CPU_TIME
1442 .with_label_values(&physical_labels)
1443 .get(),
1444 42
1445 );
1446 assert_eq!(
1447 REGION_QUERY_SCANNED_BYTES
1448 .with_label_values(&physical_labels)
1449 .get(),
1450 24
1451 );
1452
1453 let _ = REGION_QUERY_CPU_TIME.remove_label_values(&logical_labels);
1454 let _ = REGION_QUERY_SCANNED_BYTES.remove_label_values(&logical_labels);
1455 let _ = REGION_QUERY_CPU_TIME.remove_label_values(&physical_labels);
1456 let _ = REGION_QUERY_SCANNED_BYTES.remove_label_values(&physical_labels);
1457 }
1458}