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