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