1use std::fmt;
18use std::sync::Arc;
19use std::time::Instant;
20
21use async_stream::{stream, try_stream};
22use common_error::ext::BoxedError;
23use common_recordbatch::{RecordBatchStreamWrapper, SendableRecordBatchStream};
24use common_telemetry::{tracing, warn};
25use datafusion::physical_plan::metrics::ExecutionPlanMetricsSet;
26use datafusion::physical_plan::{DisplayAs, DisplayFormatType};
27use datatypes::arrow::record_batch::RecordBatch;
28use datatypes::schema::SchemaRef;
29use futures::{Stream, StreamExt};
30use snafu::ensure;
31use store_api::metadata::RegionMetadataRef;
32use store_api::region_engine::{
33 PrepareRequest, QueryScanContext, RegionScanner, ScannerProperties,
34};
35
36use crate::error::{PartitionOutOfRangeSnafu, Result};
37use crate::read::pruner::{PartitionPruner, Pruner};
38use crate::read::scan_region::{ScanInput, StreamContext};
39use crate::read::scan_util::{
40 PartitionMetrics, PartitionMetricsList, scan_flat_file_ranges, scan_flat_mem_ranges,
41};
42use crate::read::stream::{ConvertBatchStream, ScanBatch, ScanBatchStream};
43use crate::read::{ScannerMetrics, scan_util};
44
45pub struct UnorderedScan {
49 properties: ScannerProperties,
51 stream_ctx: Arc<StreamContext>,
53 pruner: Arc<Pruner>,
55 metrics_list: PartitionMetricsList,
57}
58
59impl UnorderedScan {
60 pub(crate) fn new(input: ScanInput) -> Self {
62 let mut properties = ScannerProperties::default()
63 .with_append_mode(input.append_mode)
64 .with_total_rows(input.total_rows())
65 .with_total_rows_is_exact(input.append_mode && input.total_rows_is_exact());
66 if let Some(counters) = input.query_stat_counters.clone() {
67 properties.set_query_stat_counters(counters);
68 }
69 let stream_ctx = Arc::new(StreamContext::unordered_scan_ctx(input));
70 properties.partitions = vec![stream_ctx.partition_ranges()];
71
72 let num_workers = common_stat::get_total_cpu_cores().max(1);
74 let pruner = Arc::new(Pruner::new(stream_ctx.clone(), num_workers));
75
76 Self {
77 properties,
78 stream_ctx,
79 pruner,
80 metrics_list: PartitionMetricsList::default(),
81 }
82 }
83
84 #[tracing::instrument(
86 skip_all,
87 fields(region_id = %self.stream_ctx.input.mapper.metadata().region_id)
88 )]
89 pub(crate) async fn build_stream(&self) -> Result<SendableRecordBatchStream, BoxedError> {
90 let metrics_set = ExecutionPlanMetricsSet::new();
91 let part_num = self.properties.num_partitions();
92 let ctx = QueryScanContext::default();
93 let streams = (0..part_num)
94 .map(|i| self.scan_partition(&ctx, &metrics_set, i))
95 .collect::<Result<Vec<_>, BoxedError>>()?;
96 let stream = stream! {
97 for mut stream in streams {
98 while let Some(rb) = stream.next().await {
99 yield rb;
100 }
101 }
102 };
103 let stream = Box::pin(RecordBatchStreamWrapper::new(
104 self.schema(),
105 Box::pin(stream),
106 ));
107 Ok(stream)
108 }
109
110 #[tracing::instrument(
112 skip_all,
113 fields(
114 region_id = %stream_ctx.input.region_metadata().region_id,
115 part_range_id = part_range_id
116 )
117 )]
118 fn scan_flat_partition_range(
119 stream_ctx: Arc<StreamContext>,
120 part_range_id: usize,
121 part_metrics: PartitionMetrics,
122 partition_pruner: Arc<PartitionPruner>,
123 ) -> impl Stream<Item = Result<RecordBatch>> {
124 try_stream! {
125 let range_meta = &stream_ctx.ranges[part_range_id];
127 let part_range = range_meta.new_partition_range(part_range_id);
128 let pre_filter_mode = stream_ctx.range_pre_filter_mode(&part_range);
129 for index in &range_meta.row_group_indices {
130 if stream_ctx.is_mem_range_index(*index) {
131 let stream = scan_flat_mem_ranges(
132 stream_ctx.clone(),
133 part_metrics.clone(),
134 *index,
135 range_meta.time_range,
136 );
137 for await record_batch in stream {
138 yield record_batch?;
139 }
140 } else if stream_ctx.is_file_range_index(*index) {
141 if partition_pruner
143 .try_skip_manifest_pruned_file_range(*index, &part_metrics)
144 {
145 continue;
146 }
147 let stream = scan_flat_file_ranges(
148 stream_ctx.clone(),
149 part_metrics.clone(),
150 *index,
151 "unordered_scan_files",
152 partition_pruner.clone(),
153 ).await?;
154 for await record_batch in stream {
155 yield record_batch?;
156 }
157 } else {
158 let stream = scan_util::maybe_scan_flat_other_ranges(
159 &stream_ctx,
160 *index,
161 &part_metrics,
162 pre_filter_mode,
163 ).await?;
164 for await record_batch in stream {
165 yield record_batch?;
166 }
167 }
168 }
169 }
170 }
171
172 pub(crate) fn scan_all_partitions(&self) -> Result<ScanBatchStream> {
174 let metrics_set = ExecutionPlanMetricsSet::new();
175
176 let streams = (0..self.properties.partitions.len())
177 .map(|partition| {
178 let metrics = self.partition_metrics(false, partition, &metrics_set);
179 self.scan_flat_batch_in_partition(partition, metrics)
180 })
181 .collect::<Result<Vec<_>>>()?;
182
183 Ok(Box::pin(futures::stream::iter(streams).flatten()))
184 }
185
186 fn partition_metrics(
187 &self,
188 explain_verbose: bool,
189 partition: usize,
190 metrics_set: &ExecutionPlanMetricsSet,
191 ) -> PartitionMetrics {
192 let part_metrics = PartitionMetrics::new(
193 self.stream_ctx.input.mapper.metadata().region_id,
194 partition,
195 "UnorderedScan",
196 self.stream_ctx.query_start,
197 explain_verbose,
198 metrics_set,
199 );
200 self.metrics_list.set(partition, part_metrics.clone());
201 part_metrics
202 }
203
204 #[tracing::instrument(
205 skip_all,
206 fields(
207 region_id = %self.stream_ctx.input.mapper.metadata().region_id,
208 partition = partition
209 )
210 )]
211 fn scan_partition_impl(
212 &self,
213 ctx: &QueryScanContext,
214 metrics_set: &ExecutionPlanMetricsSet,
215 partition: usize,
216 ) -> Result<SendableRecordBatchStream> {
217 if ctx.explain_verbose {
218 common_telemetry::info!(
219 "UnorderedScan partition {}, region_id: {}",
220 partition,
221 self.stream_ctx.input.region_metadata().region_id
222 );
223 }
224
225 let metrics = self.partition_metrics(ctx.explain_verbose, partition, metrics_set);
226 let input = &self.stream_ctx.input;
227
228 let batch_stream = self.scan_flat_batch_in_partition(partition, metrics.clone())?;
229
230 let record_batch_stream = ConvertBatchStream::new(
231 batch_stream,
232 input.mapper.clone(),
233 input.cache_strategy.clone(),
234 metrics,
235 );
236
237 Ok(Box::pin(RecordBatchStreamWrapper::new(
238 input.mapper.output_schema(),
239 Box::pin(record_batch_stream),
240 )))
241 }
242
243 #[tracing::instrument(
244 skip_all,
245 fields(
246 region_id = %self.stream_ctx.input.mapper.metadata().region_id,
247 partition = partition
248 )
249 )]
250 fn scan_flat_batch_in_partition(
251 &self,
252 partition: usize,
253 part_metrics: PartitionMetrics,
254 ) -> Result<ScanBatchStream> {
255 ensure!(
256 partition < self.properties.partitions.len(),
257 PartitionOutOfRangeSnafu {
258 given: partition,
259 all: self.properties.partitions.len(),
260 }
261 );
262
263 let stream_ctx = self.stream_ctx.clone();
264 let part_ranges = self.properties.partitions[partition].clone();
265 let pruner = self.pruner.clone();
266 pruner.add_partition_ranges(&part_ranges);
271 let partition_pruner = Arc::new(PartitionPruner::new(pruner, &part_ranges));
272
273 let stream = try_stream! {
274 part_metrics.on_first_poll();
275
276 for part_range in part_ranges {
278 let mut metrics = ScannerMetrics::default();
279 let mut fetch_start = Instant::now();
280
281 let stream = Self::scan_flat_partition_range(
282 stream_ctx.clone(),
283 part_range.identifier,
284 part_metrics.clone(),
285 partition_pruner.clone(),
286 );
287 for await record_batch in stream {
288 let record_batch = record_batch?;
289 metrics.scan_cost += fetch_start.elapsed();
290 metrics.num_batches += 1;
291 metrics.num_rows += record_batch.num_rows();
292
293 debug_assert!(record_batch.num_rows() > 0);
294 if record_batch.num_rows() == 0 {
295 continue;
296 }
297
298 let yield_start = Instant::now();
299 yield ScanBatch::RecordBatch(record_batch);
300 metrics.yield_cost += yield_start.elapsed();
301
302 fetch_start = Instant::now();
303 }
304
305 metrics.scan_cost += fetch_start.elapsed();
306 part_metrics.merge_metrics(&metrics);
307 }
308
309 part_metrics.on_finish();
310 };
311 Ok(Box::pin(stream))
312 }
313}
314
315impl RegionScanner for UnorderedScan {
316 fn name(&self) -> &str {
317 "UnorderedScan"
318 }
319
320 fn properties(&self) -> &ScannerProperties {
321 &self.properties
322 }
323
324 fn schema(&self) -> SchemaRef {
325 self.stream_ctx.input.mapper.output_schema()
326 }
327
328 fn metadata(&self) -> RegionMetadataRef {
329 self.stream_ctx.input.mapper.metadata().clone()
330 }
331
332 fn prepare(&mut self, request: PrepareRequest) -> Result<(), BoxedError> {
333 self.properties.prepare(request);
334
335 Ok(())
336 }
337
338 fn scan_partition(
339 &self,
340 ctx: &QueryScanContext,
341 metrics_set: &ExecutionPlanMetricsSet,
342 partition: usize,
343 ) -> Result<SendableRecordBatchStream, BoxedError> {
344 self.scan_partition_impl(ctx, metrics_set, partition)
345 .map_err(BoxedError::new)
346 }
347
348 fn has_predicate_without_region(&self) -> bool {
350 let predicate = self
351 .stream_ctx
352 .input
353 .predicate_group()
354 .predicate_without_region();
355 predicate.is_some()
356 }
357
358 fn add_dyn_filter_to_predicate(
359 &mut self,
360 filter_exprs: Vec<Arc<dyn datafusion::physical_plan::PhysicalExpr>>,
361 ) -> Vec<bool> {
362 self.stream_ctx.add_dyn_filter_to_predicate(filter_exprs)
363 }
364
365 fn reset_state(&mut self) {
366 self.stream_ctx.input.predicate.clear_dyn_filters();
367 let num_workers = common_stat::get_total_cpu_cores().max(1);
368 self.pruner = Arc::new(Pruner::new(self.stream_ctx.clone(), num_workers));
369 self.metrics_list = PartitionMetricsList::default();
370 }
371
372 fn set_logical_region(&mut self, logical_region: bool) {
373 self.properties.set_logical_region(logical_region);
374 }
375
376 fn set_query_load_region_id(&mut self, region_id: store_api::storage::RegionId) {
377 self.properties.set_query_load_region_id(region_id);
378 }
379
380 fn snapshot_sequence(&self) -> Option<u64> {
381 self.stream_ctx.input.snapshot_sequence
382 }
383}
384
385impl DisplayAs for UnorderedScan {
386 fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result {
387 write!(
388 f,
389 "UnorderedScan: region={}, ",
390 self.stream_ctx.input.mapper.metadata().region_id
391 )?;
392 match t {
393 DisplayFormatType::Default | DisplayFormatType::TreeRender => {
394 self.stream_ctx.format_for_explain(false, f)
395 }
396 DisplayFormatType::Verbose => {
397 self.stream_ctx.format_for_explain(true, f)?;
398 self.metrics_list.format_verbose_metrics(f)
399 }
400 }
401 }
402}
403
404impl fmt::Debug for UnorderedScan {
405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406 f.debug_struct("UnorderedScan")
407 .field("num_ranges", &self.stream_ctx.ranges.len())
408 .finish()
409 }
410}
411
412#[cfg(test)]
413impl UnorderedScan {
414 pub(crate) fn input(&self) -> &ScanInput {
416 &self.stream_ctx.input
417 }
418}