1use std::fmt;
18use std::sync::atomic::{AtomicUsize, Ordering};
19use std::sync::{Arc, Mutex};
20use std::time::{Duration, Instant};
21
22use async_stream::try_stream;
23use common_error::ext::BoxedError;
24use common_recordbatch::util::ChainedRecordBatchStream;
25use common_recordbatch::{RecordBatchStreamWrapper, SendableRecordBatchStream};
26use common_telemetry::tracing::{self, Instrument};
27use common_telemetry::warn;
28use datafusion::execution::memory_pool::MemoryPool;
29use datafusion::physical_plan::metrics::ExecutionPlanMetricsSet;
30use datafusion::physical_plan::{DisplayAs, DisplayFormatType};
31use datatypes::arrow::array::BinaryArray;
32use datatypes::arrow::record_batch::RecordBatch;
33use datatypes::schema::SchemaRef;
34use futures::{StreamExt, TryStreamExt};
35use smallvec::SmallVec;
36use snafu::{OptionExt, ResultExt, ensure};
37use store_api::metadata::RegionMetadataRef;
38use store_api::region_engine::{
39 PartitionRange, PrepareRequest, QueryScanContext, RegionScanner, ScannerProperties,
40};
41use tokio::sync::Semaphore;
42use tokio::sync::mpsc::error::{SendTimeoutError, TrySendError};
43use tokio::sync::mpsc::{self, Receiver, Sender};
44
45use crate::error::{
46 Error, InvalidSenderSnafu, JoinSnafu, PartitionOutOfRangeSnafu, Result, ScanMultiTimesSnafu,
47 ScanSeriesSnafu, TooManyFilesToReadSnafu,
48};
49use crate::read::ScannerMetrics;
50use crate::read::pruner::{PartitionPruner, Pruner, PrunerOptions};
51use crate::read::scan_region::{ScanInput, StreamContext};
52use crate::read::scan_util::{
53 PartitionMetrics, PartitionMetricsList, SeriesDistributorMetrics, compute_average_batch_size,
54 compute_parallel_channel_size,
55};
56use crate::read::seq_scan::{FlatReadMode, SeqScan};
57use crate::read::series_candidate::{SeriesCandidateScanner, is_sparse_metric_metadata};
58use crate::read::series_reader::{AssignedSeriesBatch, SeriesBatchCollector, SeriesReader};
59use crate::read::stream::{ConvertBatchStream, ScanBatch, ScanBatchStream};
60use crate::sst::parquet::flat_format::primary_key_column_index;
61use crate::sst::parquet::format::PrimaryKeyArray;
62
63const SEND_TIMEOUT: Duration = Duration::from_micros(100);
65
66const CANDIDATE_SERIES_ASSIGNMENT_THRESHOLD: usize = 1_000_000;
68
69type LegacyReceiverList = Vec<Option<Receiver<Result<SeriesBatch>>>>;
71
72type CandidateReceiverList = Vec<Option<CandidateReceiver>>;
74
75type CandidateSenderList = Vec<Option<mpsc::UnboundedSender<Result<SeriesReaderInput>>>>;
77
78struct CandidateReceiver {
79 receiver: mpsc::UnboundedReceiver<Result<SeriesReaderInput>>,
80 active_receivers: Arc<AtomicUsize>,
81}
82
83impl CandidateReceiver {
84 async fn recv(&mut self) -> Option<Result<SeriesReaderInput>> {
85 self.receiver.recv().await
86 }
87}
88
89impl Drop for CandidateReceiver {
90 fn drop(&mut self) {
91 let previous = self.active_receivers.fetch_sub(1, Ordering::Relaxed);
92 debug_assert!(previous > 0);
93 }
94}
95
96struct SeriesReaderInput {
98 assigned_series: AssignedSeriesBatch,
99 partition_pruner: Arc<PartitionPruner>,
100 range_semaphore: Arc<Semaphore>,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104enum SeriesScanMode {
105 Legacy,
106 TwoPhase,
107}
108
109impl SeriesScanMode {
110 fn as_str(self) -> &'static str {
111 match self {
112 Self::Legacy => "legacy",
113 Self::TwoPhase => "two_phase",
114 }
115 }
116}
117
118pub struct SeriesScan {
124 mode: SeriesScanMode,
126 properties: ScannerProperties,
128 stream_ctx: Arc<StreamContext>,
130 pruner: Arc<Pruner>,
132 legacy_receivers: Mutex<LegacyReceiverList>,
134 candidate_receivers: Mutex<CandidateReceiverList>,
136 metrics_list: Arc<PartitionMetricsList>,
139}
140
141impl SeriesScan {
142 pub(crate) fn new(input: ScanInput, experimental_series_scan_v2: bool) -> Self {
144 let mode = if experimental_series_scan_v2 && Self::supports_two_phase(&input) {
145 SeriesScanMode::TwoPhase
146 } else {
147 SeriesScanMode::Legacy
148 };
149 let mut properties = ScannerProperties::default()
150 .with_append_mode(input.append_mode)
151 .with_total_rows(input.total_rows())
152 .with_total_rows_is_exact(input.append_mode && input.total_rows_is_exact());
153 if let Some(counters) = input.query_stat_counters.clone() {
154 properties.set_query_stat_counters(counters);
155 }
156 let stream_ctx = Arc::new(StreamContext::seq_scan_ctx(input));
157 properties.partitions = vec![stream_ctx.partition_ranges()];
158
159 let num_workers = common_stat::get_total_cpu_cores().max(1);
161 let pruner = match mode {
162 SeriesScanMode::Legacy => Arc::new(Pruner::new(stream_ctx.clone(), num_workers)),
163 SeriesScanMode::TwoPhase => Arc::new(Pruner::new_with_options(
164 stream_ctx.clone(),
165 num_workers,
166 PrunerOptions {
167 retain_builders: true,
168 enable_predicate_prefilter: false,
169 },
170 )),
171 };
172
173 Self {
174 mode,
175 properties,
176 stream_ctx,
177 pruner,
178 legacy_receivers: Mutex::new(Vec::new()),
179 candidate_receivers: Mutex::new(Vec::new()),
180 metrics_list: Arc::new(PartitionMetricsList::default()),
181 }
182 }
183
184 fn supports_two_phase(input: &ScanInput) -> bool {
185 if !is_sparse_metric_metadata(input.region_metadata()) {
186 return false;
187 }
188 #[cfg(feature = "enterprise")]
189 if !input.extension_ranges().is_empty() {
190 return false;
191 }
192 true
193 }
194
195 #[tracing::instrument(
196 skip_all,
197 fields(
198 region_id = %self.stream_ctx.input.mapper.metadata().region_id,
199 partition = partition
200 )
201 )]
202 fn scan_partition_impl(
203 &self,
204 ctx: &QueryScanContext,
205 metrics_set: &ExecutionPlanMetricsSet,
206 partition: usize,
207 ) -> Result<SendableRecordBatchStream> {
208 let metrics = new_partition_metrics(
209 &self.stream_ctx,
210 ctx.explain_verbose,
211 metrics_set,
212 partition,
213 &self.metrics_list,
214 );
215
216 let batch_stream =
217 self.scan_batch_in_partition(ctx, partition, metrics.clone(), metrics_set)?;
218
219 let input = &self.stream_ctx.input;
220 let record_batch_stream = ConvertBatchStream::new(
221 batch_stream,
222 input.mapper.clone(),
223 input.cache_strategy.clone(),
224 metrics,
225 );
226
227 Ok(Box::pin(RecordBatchStreamWrapper::new(
228 input.mapper.output_schema(),
229 Box::pin(record_batch_stream),
230 )))
231 }
232
233 #[tracing::instrument(
234 skip_all,
235 fields(
236 region_id = %self.stream_ctx.input.mapper.metadata().region_id,
237 partition = partition
238 )
239 )]
240 fn scan_batch_in_partition(
241 &self,
242 ctx: &QueryScanContext,
243 partition: usize,
244 part_metrics: PartitionMetrics,
245 metrics_set: &ExecutionPlanMetricsSet,
246 ) -> Result<ScanBatchStream> {
247 if ctx.explain_verbose {
248 common_telemetry::info!(
249 "SeriesScan partition {}, region_id: {}",
250 partition,
251 self.stream_ctx.input.region_metadata().region_id
252 );
253 }
254
255 ensure!(
256 partition < self.properties.num_partitions(),
257 PartitionOutOfRangeSnafu {
258 given: partition,
259 all: self.properties.num_partitions(),
260 }
261 );
262
263 match self.mode {
264 SeriesScanMode::Legacy => self.scan_legacy_batch_in_partition(
265 partition,
266 part_metrics,
267 metrics_set,
268 ctx.explain_verbose,
269 ),
270 SeriesScanMode::TwoPhase => {
271 self.scan_two_phase_batch_in_partition(ctx, partition, part_metrics, metrics_set)
272 }
273 }
274 }
275
276 fn scan_legacy_batch_in_partition(
277 &self,
278 partition: usize,
279 part_metrics: PartitionMetrics,
280 metrics_set: &ExecutionPlanMetricsSet,
281 explain_verbose: bool,
282 ) -> Result<ScanBatchStream> {
283 self.maybe_start_legacy_distributor(metrics_set, explain_verbose);
284 let mut receiver = self.legacy_receivers.lock().unwrap()[partition]
285 .take()
286 .context(ScanMultiTimesSnafu { partition })?;
287 let stream = try_stream! {
288 part_metrics.on_first_poll();
289
290 let mut fetch_start = Instant::now();
291 let mut metrics = ScannerMetrics::default();
292 while let Some(series) = receiver.recv().await {
293 let series = series?;
294 metrics.scan_cost += fetch_start.elapsed();
295 metrics.num_batches += series.num_batches();
296 metrics.num_rows += series.num_rows();
297
298 let yield_start = Instant::now();
299 yield ScanBatch::Series(series);
300 metrics.yield_cost += yield_start.elapsed();
301 fetch_start = Instant::now();
302 }
303
304 part_metrics.merge_metrics(&metrics);
305 part_metrics.on_finish();
306 };
307 Ok(Box::pin(stream))
308 }
309
310 fn scan_two_phase_batch_in_partition(
311 &self,
312 ctx: &QueryScanContext,
313 partition: usize,
314 part_metrics: PartitionMetrics,
315 metrics_set: &ExecutionPlanMetricsSet,
316 ) -> Result<ScanBatchStream> {
317 self.maybe_start_candidate_distributor(ctx, metrics_set);
318 let mut receiver = self.candidate_receivers.lock().unwrap()[partition]
321 .take()
322 .context(ScanMultiTimesSnafu { partition })?;
323 let stream_ctx = self.stream_ctx.clone();
324 let partition_ranges = self
325 .properties
326 .partitions
327 .iter()
328 .flatten()
329 .copied()
330 .collect::<Vec<_>>();
331 let stream = try_stream! {
332 part_metrics.on_first_poll();
333
334 let mut fetch_start = Instant::now();
335 let mut metrics = ScannerMetrics::default();
336 while let Some(input) = receiver.recv().await {
337 let input = input?;
338 metrics.scan_cost += fetch_start.elapsed();
339
340 let build_start = Instant::now();
341 let reader = SeriesReader::try_new(
342 stream_ctx.clone(),
343 partition_ranges.clone(),
344 input.assigned_series,
345 input.partition_pruner,
346 input.range_semaphore,
347 part_metrics.clone(),
348 )?;
349 let mut reader_stream = reader.build_stream().await?;
350 metrics.scan_cost += build_start.elapsed();
351 fetch_start = Instant::now();
352 while let Some(record_batch) = reader_stream.try_next().await? {
353 metrics.scan_cost += fetch_start.elapsed();
354 metrics.num_batches += 1;
355 metrics.num_rows += record_batch.num_rows();
356
357 let yield_start = Instant::now();
358 yield ScanBatch::RecordBatch(record_batch);
359 metrics.yield_cost += yield_start.elapsed();
360 fetch_start = Instant::now();
361 }
362 }
363 metrics.scan_cost += fetch_start.elapsed();
364
365 part_metrics.merge_metrics(&metrics);
366 part_metrics.on_finish();
367 };
368 Ok(Box::pin(stream))
369 }
370
371 fn maybe_start_legacy_distributor(
372 &self,
373 metrics_set: &ExecutionPlanMetricsSet,
374 explain_verbose: bool,
375 ) {
376 let mut rx_list = self.legacy_receivers.lock().unwrap();
377 if !rx_list.is_empty() {
378 return;
379 }
380
381 let (senders, receivers) = new_legacy_channel_list(self.properties.num_partitions());
382 let mut distributor = SeriesDistributor {
383 stream_ctx: self.stream_ctx.clone(),
384 range_semaphore: Some(Arc::new(Semaphore::new(self.properties.num_partitions()))),
385 final_merge_semaphore: Some(Arc::new(Semaphore::new(self.properties.num_partitions()))),
386 partitions: self.properties.partitions.clone(),
387 pruner: self.pruner.clone(),
388 senders,
389 metrics_set: metrics_set.clone(),
390 metrics_list: self.metrics_list.clone(),
391 explain_verbose,
392 };
393 let region_id = distributor.stream_ctx.input.mapper.metadata().region_id;
394 let span = tracing::info_span!("SeriesScan::distributor", region_id = %region_id);
395 common_runtime::spawn_query(
396 async move {
397 distributor.execute().await;
398 }
399 .instrument(span),
400 );
401
402 *rx_list = receivers;
403 }
404
405 fn maybe_start_candidate_distributor(
406 &self,
407 ctx: &QueryScanContext,
408 metrics_set: &ExecutionPlanMetricsSet,
409 ) {
410 let mut rx_list = self.candidate_receivers.lock().unwrap();
411 if !rx_list.is_empty() {
412 return;
413 }
414
415 let (senders, receivers, active_receivers) =
416 new_candidate_channel_list(self.properties.num_partitions());
417 let mut distributor = SeriesCandidateDistributor {
420 memory_pool: ctx.memory_pool.clone(),
421 stream_ctx: self.stream_ctx.clone(),
422 range_semaphore: Arc::new(Semaphore::new(self.properties.num_partitions())),
423 partitions: self.properties.partitions.clone(),
424 pruner: self.pruner.clone(),
425 senders,
426 active_receivers,
427 metrics_set: metrics_set.clone(),
428 metrics_list: self.metrics_list.clone(),
429 explain_verbose: ctx.explain_verbose,
430 };
431 let region_id = distributor.stream_ctx.input.mapper.metadata().region_id;
432 let span = tracing::info_span!("SeriesScan::candidate_distributor", region_id = %region_id);
433 common_runtime::spawn_query(
434 async move {
435 distributor.execute().await;
436 }
437 .instrument(span),
438 );
439
440 *rx_list = receivers;
441 }
442
443 #[tracing::instrument(
445 skip_all,
446 fields(region_id = %self.stream_ctx.input.mapper.metadata().region_id)
447 )]
448 pub(crate) async fn build_stream(&self) -> Result<SendableRecordBatchStream, BoxedError> {
449 let part_num = self.properties.num_partitions();
450 let metrics_set = ExecutionPlanMetricsSet::default();
451 let ctx = QueryScanContext::default();
452 let streams = (0..part_num)
453 .map(|i| self.scan_partition(&ctx, &metrics_set, i))
454 .collect::<Result<Vec<_>, BoxedError>>()?;
455 let chained_stream = ChainedRecordBatchStream::new(streams).map_err(BoxedError::new)?;
456 Ok(Box::pin(chained_stream))
457 }
458
459 pub(crate) fn scan_all_partitions(&self) -> Result<ScanBatchStream> {
461 let metrics_set = ExecutionPlanMetricsSet::new();
462
463 let ctx = QueryScanContext::default();
464 let streams = (0..self.properties.partitions.len())
465 .map(|partition| {
466 let metrics = new_partition_metrics(
467 &self.stream_ctx,
468 false,
469 &metrics_set,
470 partition,
471 &self.metrics_list,
472 );
473
474 self.scan_batch_in_partition(&ctx, partition, metrics, &metrics_set)
475 })
476 .collect::<Result<Vec<_>>>()?;
477
478 Ok(Box::pin(futures::stream::iter(streams).flatten()))
479 }
480
481 pub(crate) fn check_scan_limit(&self) -> Result<()> {
483 if self.stream_ctx.input.uses_scan_memory_limit() {
484 return Ok(());
485 }
486 let total_files: usize = self
488 .properties
489 .partitions
490 .iter()
491 .flat_map(|partition| partition.iter())
492 .map(|part_range| {
493 let range_meta = &self.stream_ctx.ranges[part_range.identifier];
494 range_meta.indices.len()
495 })
496 .sum();
497
498 let max_concurrent_files = self.stream_ctx.input.max_concurrent_scan_files;
499 if total_files > max_concurrent_files {
500 return TooManyFilesToReadSnafu {
501 actual: total_files,
502 max: max_concurrent_files,
503 }
504 .fail();
505 }
506
507 Ok(())
508 }
509}
510
511fn new_legacy_channel_list(num_partitions: usize) -> (SenderList, LegacyReceiverList) {
512 let (senders, receivers): (Vec<_>, Vec<_>) = (0..num_partitions)
513 .map(|_| {
514 let (sender, receiver) = mpsc::channel(1);
515 (Some(sender), Some(receiver))
516 })
517 .unzip();
518 (SenderList::new(senders), receivers)
519}
520
521fn new_candidate_channel_list(
522 num_partitions: usize,
523) -> (CandidateSenderList, CandidateReceiverList, Arc<AtomicUsize>) {
524 let active_receivers = Arc::new(AtomicUsize::new(num_partitions));
525 let (senders, receivers) = (0..num_partitions)
526 .map(|_| {
527 let (sender, receiver) = mpsc::unbounded_channel();
531 let receiver = CandidateReceiver {
532 receiver,
533 active_receivers: active_receivers.clone(),
534 };
535 (Some(sender), Some(receiver))
536 })
537 .unzip();
538 (senders, receivers, active_receivers)
539}
540
541impl RegionScanner for SeriesScan {
542 fn name(&self) -> &str {
543 "SeriesScan"
544 }
545
546 fn properties(&self) -> &ScannerProperties {
547 &self.properties
548 }
549
550 fn schema(&self) -> SchemaRef {
551 self.stream_ctx.input.mapper.output_schema()
552 }
553
554 fn metadata(&self) -> RegionMetadataRef {
555 self.stream_ctx.input.mapper.metadata().clone()
556 }
557
558 fn scan_partition(
559 &self,
560 ctx: &QueryScanContext,
561 metrics_set: &ExecutionPlanMetricsSet,
562 partition: usize,
563 ) -> Result<SendableRecordBatchStream, BoxedError> {
564 self.scan_partition_impl(ctx, metrics_set, partition)
565 .map_err(BoxedError::new)
566 }
567
568 fn prepare(&mut self, request: PrepareRequest) -> Result<(), BoxedError> {
569 self.properties.prepare(request);
570
571 self.check_scan_limit().map_err(BoxedError::new)?;
572
573 Ok(())
574 }
575
576 fn has_predicate_without_region(&self) -> bool {
577 let predicate = self
578 .stream_ctx
579 .input
580 .predicate_group()
581 .predicate_without_region();
582 predicate.is_some()
583 }
584
585 fn add_dyn_filter_to_predicate(
586 &mut self,
587 filter_exprs: Vec<Arc<dyn datafusion::physical_plan::PhysicalExpr>>,
588 ) -> Vec<bool> {
589 self.stream_ctx.add_dyn_filter_to_predicate(filter_exprs)
590 }
591
592 fn reset_state(&mut self) {
593 self.stream_ctx.input.predicate.clear_dyn_filters();
594 let num_workers = common_stat::get_total_cpu_cores().max(1);
595 self.pruner = match self.mode {
596 SeriesScanMode::Legacy => Arc::new(Pruner::new(self.stream_ctx.clone(), num_workers)),
597 SeriesScanMode::TwoPhase => Arc::new(Pruner::new_with_options(
598 self.stream_ctx.clone(),
599 num_workers,
600 PrunerOptions {
601 retain_builders: true,
602 enable_predicate_prefilter: false,
603 },
604 )),
605 };
606 self.legacy_receivers.lock().unwrap().clear();
607 self.candidate_receivers.lock().unwrap().clear();
608 self.metrics_list = Arc::new(PartitionMetricsList::default());
609 }
610
611 fn set_logical_region(&mut self, logical_region: bool) {
612 self.properties.set_logical_region(logical_region);
613 }
614
615 fn set_query_load_region_id(&mut self, region_id: store_api::storage::RegionId) {
616 self.properties.set_query_load_region_id(region_id);
617 }
618
619 fn snapshot_sequence(&self) -> Option<u64> {
620 self.stream_ctx.input.snapshot_sequence
621 }
622}
623
624impl DisplayAs for SeriesScan {
625 fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result {
626 write!(
627 f,
628 "SeriesScan: region={}, ",
629 self.stream_ctx.input.mapper.metadata().region_id
630 )?;
631 match t {
632 DisplayFormatType::Default | DisplayFormatType::TreeRender => {
633 self.stream_ctx.format_for_explain(false, f)?;
634 }
635 DisplayFormatType::Verbose => {
636 self.stream_ctx.format_for_explain(true, f)?;
637 }
638 }
639 write!(f, ", \"mode\":\"{}\"", self.mode.as_str())?;
640 if matches!(t, DisplayFormatType::Verbose) {
641 self.metrics_list.format_verbose_metrics(f)?;
642 }
643 Ok(())
644 }
645}
646
647impl fmt::Debug for SeriesScan {
648 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649 f.debug_struct("SeriesScan")
650 .field("mode", &self.mode)
651 .field("num_ranges", &self.stream_ctx.ranges.len())
652 .finish()
653 }
654}
655
656#[cfg(test)]
657impl SeriesScan {
658 pub(crate) fn input(&self) -> &ScanInput {
660 &self.stream_ctx.input
661 }
662
663 pub(crate) fn mode(&self) -> &'static str {
665 self.mode.as_str()
666 }
667}
668
669struct SeriesDistributor {
671 stream_ctx: Arc<StreamContext>,
673 range_semaphore: Option<Arc<Semaphore>>,
675 final_merge_semaphore: Option<Arc<Semaphore>>,
680 partitions: Vec<Vec<PartitionRange>>,
682 pruner: Arc<Pruner>,
684 senders: SenderList,
686 metrics_set: ExecutionPlanMetricsSet,
692 metrics_list: Arc<PartitionMetricsList>,
693 explain_verbose: bool,
695}
696
697impl SeriesDistributor {
698 #[tracing::instrument(
700 skip_all,
701 fields(region_id = %self.stream_ctx.input.mapper.metadata().region_id)
702 )]
703 async fn execute(&mut self) {
704 if let Err(e) = self.scan_partitions_flat().await {
705 self.senders.send_error(e).await;
706 }
707 }
708
709 #[tracing::instrument(
711 skip_all,
712 fields(region_id = %self.stream_ctx.input.mapper.metadata().region_id)
713 )]
714 async fn scan_partitions_flat(&mut self) -> Result<()> {
715 for partition_ranges in &self.partitions {
717 self.pruner.add_partition_ranges(partition_ranges);
718 }
719
720 let all_partition_ranges: Vec<_> = self.partitions.iter().flatten().cloned().collect();
722 let needs_merge = all_partition_ranges.len() > 1;
723 let partition_pruner = Arc::new(PartitionPruner::new(
724 self.pruner.clone(),
725 &all_partition_ranges,
726 ));
727
728 let part_metrics = new_partition_metrics(
729 &self.stream_ctx,
730 self.explain_verbose,
731 &self.metrics_set,
732 self.partitions.len(),
733 &self.metrics_list,
734 );
735 part_metrics.on_first_poll();
736 let mut fetch_start = Instant::now();
739
740 let build_start = Instant::now();
742 let mut tasks = Vec::new();
743 for partition in &self.partitions {
744 for part_range in partition {
745 let stream_ctx = self.stream_ctx.clone();
746 let part_range = *part_range;
747 let part_metrics = part_metrics.clone();
748 let partition_pruner = partition_pruner.clone();
749 let file_scan_semaphore = self.range_semaphore.clone();
750 let merge_semaphore = self.range_semaphore.clone();
751 tasks.push(common_runtime::spawn_query(async move {
752 SeqScan::build_flat_partition_range_read(
753 &stream_ctx,
754 &part_range,
755 if needs_merge {
756 FlatReadMode::CrossRangeMerge
757 } else {
758 FlatReadMode::Sequential
759 },
760 &part_metrics,
761 partition_pruner,
762 file_scan_semaphore,
763 merge_semaphore,
764 )
765 .await
766 }));
767 }
768 }
769 let mut range_streams = Vec::with_capacity(tasks.len());
770 let mut estimated_batch_sizes = Vec::with_capacity(tasks.len());
771 for task in tasks {
772 let (stream, estimated_batch_size) = task.await.context(JoinSnafu)??;
773 range_streams.push(stream);
774 estimated_batch_sizes.push(estimated_batch_size);
775 }
776 let channel_size =
777 compute_parallel_channel_size(compute_average_batch_size(estimated_batch_sizes));
778 common_telemetry::debug!(
779 "SeriesDistributor built {} range_streams, region: {}, build cost: {:?}, channel_size: {}",
780 range_streams.len(),
781 self.stream_ctx.input.region_metadata().region_id,
782 build_start.elapsed(),
783 channel_size,
784 );
785
786 let mut reader = SeqScan::build_flat_reader_from_sources(
790 &self.stream_ctx,
791 range_streams,
792 self.final_merge_semaphore.clone(),
793 Some(&part_metrics),
794 true,
795 channel_size,
796 )
797 .await?;
798 let mut metrics = SeriesDistributorMetrics::default();
799
800 let mut divider = FlatSeriesBatchDivider::default();
801 while let Some(record_batch) = reader.try_next().await? {
802 metrics.scan_cost += fetch_start.elapsed();
803 metrics.num_batches += 1;
804 metrics.num_rows += record_batch.num_rows();
805
806 debug_assert!(record_batch.num_rows() > 0);
807 if record_batch.num_rows() == 0 {
808 fetch_start = Instant::now();
809 continue;
810 }
811
812 let divider_start = Instant::now();
814 let series_batch = divider.push(record_batch);
815 metrics.divider_cost += divider_start.elapsed();
816 if let Some(series_batch) = series_batch {
817 let yield_start = Instant::now();
818 self.senders
819 .send_batch(SeriesBatch::Flat(series_batch))
820 .await?;
821 metrics.yield_cost += yield_start.elapsed();
822 }
823 fetch_start = Instant::now();
824 }
825
826 let divider_start = Instant::now();
828 let series_batch = divider.finish();
829 metrics.divider_cost += divider_start.elapsed();
830 if let Some(series_batch) = series_batch {
831 let yield_start = Instant::now();
832 self.senders
833 .send_batch(SeriesBatch::Flat(series_batch))
834 .await?;
835 metrics.yield_cost += yield_start.elapsed();
836 }
837
838 metrics.scan_cost += fetch_start.elapsed();
839 metrics.num_series_send_timeout = self.senders.num_timeout;
840 metrics.num_series_send_full = self.senders.num_full;
841 part_metrics.set_distributor_metrics(&metrics);
842
843 part_metrics.on_finish();
844
845 Ok(())
846 }
847}
848
849struct SeriesCandidateDistributor {
851 memory_pool: Arc<dyn MemoryPool>,
852 stream_ctx: Arc<StreamContext>,
853 range_semaphore: Arc<Semaphore>,
854 partitions: Vec<Vec<PartitionRange>>,
855 pruner: Arc<Pruner>,
856 senders: CandidateSenderList,
857 active_receivers: Arc<AtomicUsize>,
858 metrics_set: ExecutionPlanMetricsSet,
859 metrics_list: Arc<PartitionMetricsList>,
860 explain_verbose: bool,
861}
862
863impl SeriesCandidateDistributor {
864 async fn execute(&mut self) {
865 if let Err(e) = self.distribute().await {
866 self.send_error(e);
867 }
868 }
869
870 async fn distribute(&mut self) -> Result<()> {
871 let part_metrics = new_partition_metrics(
872 &self.stream_ctx,
873 self.explain_verbose,
874 &self.metrics_set,
875 self.partitions.len(),
876 &self.metrics_list,
877 );
878 part_metrics.on_first_poll();
879
880 let candidate_scanner = SeriesCandidateScanner::try_new(
881 self.stream_ctx.clone(),
882 self.partitions.clone(),
883 self.pruner.clone(),
884 self.range_semaphore.clone(),
885 self.memory_pool.clone(),
886 self.metrics_set.clone(),
887 part_metrics.clone(),
888 )?;
889 let partition_pruner = candidate_scanner.partition_pruner();
890 let mut candidates = candidate_scanner.build_stream().await?;
891 let mut collector =
892 SeriesBatchCollector::new(self.partitions.len()).context(InvalidSenderSnafu)?;
893 let mut chunked = false;
894 while let Some(batch) = candidates.try_next().await? {
895 if !self.should_fetch_candidates() {
896 part_metrics.on_finish();
897 return Ok(());
898 }
899 collector.push(batch);
900 if collector.len() >= CANDIDATE_SERIES_ASSIGNMENT_THRESHOLD {
901 chunked = true;
902 self.send_assignments(collector.finish(false), &partition_pruner);
903 if !self.should_fetch_candidates() {
904 part_metrics.on_finish();
905 return Ok(());
906 }
907 collector =
908 SeriesBatchCollector::new(self.partitions.len()).context(InvalidSenderSnafu)?;
909 }
910 }
911
912 if collector.len() > 0 {
913 self.send_assignments(collector.finish(!chunked), &partition_pruner);
914 }
915 part_metrics.on_finish();
916 Ok(())
917 }
918
919 fn send_assignments(
920 &mut self,
921 assignments: Vec<AssignedSeriesBatch>,
922 partition_pruner: &Arc<PartitionPruner>,
923 ) {
924 for (partition, assigned_series) in assignments.into_iter().enumerate() {
925 if assigned_series.series().is_empty() {
926 continue;
927 }
928 let Some(sender) = self.senders[partition].as_ref() else {
929 continue;
930 };
931 let sent = sender
932 .send(Ok(SeriesReaderInput {
933 assigned_series,
934 partition_pruner: partition_pruner.clone(),
935 range_semaphore: self.range_semaphore.clone(),
936 }))
937 .is_ok();
938 if !sent {
939 self.senders[partition] = None;
940 }
941 }
942 }
943
944 fn should_fetch_candidates(&self) -> bool {
945 self.active_receivers.load(Ordering::Relaxed) > 0
946 }
947
948 fn send_error(&mut self, error: Error) {
949 let error = Arc::new(error);
950 for sender in self.senders.iter_mut().filter_map(Option::take) {
951 let result = Err(error.clone()).context(ScanSeriesSnafu);
952 let _ = sender.send(result);
953 }
954 }
955}
956
957#[derive(Debug)]
959pub enum SeriesBatch {
960 Flat(FlatSeriesBatch),
961}
962
963impl SeriesBatch {
964 pub fn num_batches(&self) -> usize {
966 match self {
967 SeriesBatch::Flat(flat_batch) => flat_batch.batches.len(),
968 }
969 }
970
971 pub fn num_rows(&self) -> usize {
973 match self {
974 SeriesBatch::Flat(flat_batch) => flat_batch.batches.iter().map(|x| x.num_rows()).sum(),
975 }
976 }
977}
978
979#[derive(Default, Debug)]
981pub struct FlatSeriesBatch {
982 pub batches: SmallVec<[RecordBatch; 4]>,
983}
984
985struct SenderList {
987 senders: Vec<Option<Sender<Result<SeriesBatch>>>>,
988 num_nones: usize,
990 sender_idx: usize,
992 num_timeout: usize,
994 num_full: usize,
996}
997
998impl SenderList {
999 fn new(senders: Vec<Option<Sender<Result<SeriesBatch>>>>) -> Self {
1000 let num_nones = senders.iter().filter(|sender| sender.is_none()).count();
1001 Self {
1002 senders,
1003 num_nones,
1004 sender_idx: 0,
1005 num_timeout: 0,
1006 num_full: 0,
1007 }
1008 }
1009
1010 fn try_send_batch(&mut self, mut batch: SeriesBatch) -> Result<Option<SeriesBatch>> {
1013 for _ in 0..self.senders.len() {
1014 ensure!(self.num_nones < self.senders.len(), InvalidSenderSnafu);
1015
1016 let sender_idx = self.fetch_add_sender_idx();
1017 let Some(sender) = &self.senders[sender_idx] else {
1018 continue;
1019 };
1020
1021 match sender.try_send(Ok(batch)) {
1022 Ok(()) => return Ok(None),
1023 Err(TrySendError::Full(res)) => {
1024 self.num_full += 1;
1025 batch = res.unwrap();
1027 }
1028 Err(TrySendError::Closed(res)) => {
1029 self.senders[sender_idx] = None;
1030 self.num_nones += 1;
1031 batch = res.unwrap();
1033 }
1034 }
1035 }
1036
1037 Ok(Some(batch))
1038 }
1039
1040 async fn send_batch(&mut self, mut batch: SeriesBatch) -> Result<()> {
1042 match self.try_send_batch(batch)? {
1044 Some(b) => {
1045 batch = b;
1047 }
1048 None => {
1049 return Ok(());
1050 }
1051 }
1052
1053 loop {
1054 ensure!(self.num_nones < self.senders.len(), InvalidSenderSnafu);
1055
1056 let sender_idx = self.fetch_add_sender_idx();
1057 let Some(sender) = &self.senders[sender_idx] else {
1058 continue;
1059 };
1060 match sender.send_timeout(Ok(batch), SEND_TIMEOUT).await {
1065 Ok(()) => break,
1066 Err(SendTimeoutError::Timeout(res)) => {
1067 self.num_timeout += 1;
1068 batch = res.unwrap();
1070 }
1071 Err(SendTimeoutError::Closed(res)) => {
1072 self.senders[sender_idx] = None;
1073 self.num_nones += 1;
1074 batch = res.unwrap();
1076 }
1077 }
1078 }
1079
1080 Ok(())
1081 }
1082
1083 async fn send_error(&self, error: Error) {
1084 let error = Arc::new(error);
1085 for sender in self.senders.iter().flatten() {
1086 let result = Err(error.clone()).context(ScanSeriesSnafu);
1087 let _ = sender.send(result).await;
1088 }
1089 }
1090
1091 fn fetch_add_sender_idx(&mut self) -> usize {
1092 let sender_idx = self.sender_idx;
1093 self.sender_idx = (self.sender_idx + 1) % self.senders.len();
1094 sender_idx
1095 }
1096}
1097
1098fn new_partition_metrics(
1099 stream_ctx: &StreamContext,
1100 explain_verbose: bool,
1101 metrics_set: &ExecutionPlanMetricsSet,
1102 partition: usize,
1103 metrics_list: &PartitionMetricsList,
1104) -> PartitionMetrics {
1105 let metrics = PartitionMetrics::new(
1106 stream_ctx.input.mapper.metadata().region_id,
1107 partition,
1108 "SeriesScan",
1109 stream_ctx.query_start,
1110 explain_verbose,
1111 metrics_set,
1112 );
1113
1114 metrics_list.set(partition, metrics.clone());
1115 metrics
1116}
1117
1118#[derive(Default)]
1123struct FlatSeriesBatchDivider {
1124 buffer: FlatSeriesBatch,
1125}
1126
1127impl FlatSeriesBatchDivider {
1128 fn push(&mut self, batch: RecordBatch) -> Option<FlatSeriesBatch> {
1132 if self.buffer.batches.is_empty() {
1134 self.buffer.batches.push(batch);
1135 return None;
1136 }
1137
1138 let pk_column_idx = primary_key_column_index(batch.num_columns());
1140 let batch_pk_column = batch.column(pk_column_idx);
1141 let batch_pk_array = batch_pk_column
1142 .as_any()
1143 .downcast_ref::<PrimaryKeyArray>()
1144 .unwrap();
1145 let batch_pk_values = batch_pk_array
1146 .values()
1147 .as_any()
1148 .downcast_ref::<BinaryArray>()
1149 .unwrap();
1150 let batch_last_pk =
1152 primary_key_at(batch_pk_array, batch_pk_values, batch_pk_array.len() - 1);
1153 let buffer_last_batch = self.buffer.batches.last().unwrap();
1156 let buffer_pk_column = buffer_last_batch.column(pk_column_idx);
1157 let buffer_pk_array = buffer_pk_column
1158 .as_any()
1159 .downcast_ref::<PrimaryKeyArray>()
1160 .unwrap();
1161 let buffer_pk_values = buffer_pk_array
1162 .values()
1163 .as_any()
1164 .downcast_ref::<BinaryArray>()
1165 .unwrap();
1166 let buffer_last_pk =
1167 primary_key_at(buffer_pk_array, buffer_pk_values, buffer_pk_array.len() - 1);
1168
1169 if batch_last_pk == buffer_last_pk {
1171 self.buffer.batches.push(batch);
1172 return None;
1173 }
1174 let batch_pk_keys = batch_pk_array.keys();
1177 let pk_indices = batch_pk_keys.values();
1178 let mut change_offset = 0;
1179 for (i, &key) in pk_indices.iter().enumerate() {
1180 let batch_pk = batch_pk_values.value(key as usize);
1181
1182 if buffer_last_pk != batch_pk {
1183 change_offset = i;
1184 break;
1185 }
1186 }
1187
1188 let (first_part, remaining_part) = if change_offset > 0 {
1190 let first_part = batch.slice(0, change_offset);
1191 let remaining_part = batch.slice(change_offset, batch.num_rows() - change_offset);
1192 (Some(first_part), Some(remaining_part))
1193 } else {
1194 (None, Some(batch))
1195 };
1196
1197 let mut result = std::mem::take(&mut self.buffer);
1199 if let Some(first_part) = first_part {
1200 result.batches.push(first_part);
1201 }
1202
1203 if let Some(remaining_part) = remaining_part {
1205 self.buffer.batches.push(remaining_part);
1206 }
1207
1208 Some(result)
1209 }
1210
1211 fn finish(&mut self) -> Option<FlatSeriesBatch> {
1213 if self.buffer.batches.is_empty() {
1214 None
1215 } else {
1216 Some(std::mem::take(&mut self.buffer))
1217 }
1218 }
1219}
1220
1221fn primary_key_at<'a>(
1223 primary_key: &PrimaryKeyArray,
1224 primary_key_values: &'a BinaryArray,
1225 index: usize,
1226) -> &'a [u8] {
1227 let key = primary_key.keys().value(index);
1228 primary_key_values.value(key as usize)
1229}
1230
1231#[cfg(test)]
1232mod tests {
1233 use std::sync::Arc;
1234
1235 use api::v1::OpType;
1236 use datatypes::arrow::array::{
1237 ArrayRef, BinaryDictionaryBuilder, Int64Array, StringDictionaryBuilder,
1238 TimestampMillisecondArray, UInt8Array, UInt64Array,
1239 };
1240 use datatypes::arrow::datatypes::{DataType, Field, Schema, SchemaRef, TimeUnit, UInt32Type};
1241 use datatypes::arrow::record_batch::RecordBatch;
1242
1243 use super::*;
1244 use crate::read::flat_projection::FlatProjectionMapper;
1245 use crate::read::scan_region::PredicateGroup;
1246 use crate::test_util::scheduler_util::SchedulerEnv;
1247 use crate::test_util::sst_util::sst_region_metadata_with_encoding;
1248
1249 #[tokio::test]
1250 async fn two_phase_eligibility_allows_exact_sequence_range() {
1251 let env = SchedulerEnv::new().await;
1252 let metadata = Arc::new(sst_region_metadata_with_encoding(
1253 store_api::codec::PrimaryKeyEncoding::Sparse,
1254 ));
1255 let predicate = PredicateGroup::default();
1256
1257 let eligible = ScanInput::builder(
1258 env.access_layer.clone(),
1259 FlatProjectionMapper::new(&metadata, [0]).unwrap(),
1260 )
1261 .with_predicate(predicate.clone())
1262 .build();
1263 assert!(SeriesScan::supports_two_phase(&eligible));
1264
1265 let exact_sequence = ScanInput::builder(
1266 env.access_layer.clone(),
1267 FlatProjectionMapper::new(&metadata, [0]).unwrap(),
1268 )
1269 .with_predicate(predicate)
1270 .with_sequence_range(Some(store_api::storage::SequenceRange::GtLtEq {
1271 min: 1,
1272 max: 2,
1273 }))
1274 .build();
1275 assert!(SeriesScan::supports_two_phase(&exact_sequence));
1276 }
1277
1278 #[test]
1279 fn candidate_distributor_stops_after_all_receivers_close() {
1280 let (_senders, mut receivers, active_receivers) = new_candidate_channel_list(2);
1281 assert_eq!(2, active_receivers.load(Ordering::Relaxed));
1282
1283 drop(receivers[0].take());
1284 assert_eq!(1, active_receivers.load(Ordering::Relaxed));
1285
1286 drop(receivers[1].take());
1287 assert_eq!(0, active_receivers.load(Ordering::Relaxed));
1288 }
1289
1290 fn new_test_record_batch(
1291 primary_keys: &[&[u8]],
1292 timestamps: &[i64],
1293 sequences: &[u64],
1294 op_types: &[OpType],
1295 fields: &[u64],
1296 ) -> RecordBatch {
1297 let num_rows = timestamps.len();
1298 debug_assert_eq!(sequences.len(), num_rows);
1299 debug_assert_eq!(op_types.len(), num_rows);
1300 debug_assert_eq!(fields.len(), num_rows);
1301 debug_assert_eq!(primary_keys.len(), num_rows);
1302
1303 let columns: Vec<ArrayRef> = vec![
1304 build_test_pk_string_dict_array(primary_keys),
1305 Arc::new(Int64Array::from_iter(
1306 fields.iter().map(|v| Some(*v as i64)),
1307 )),
1308 Arc::new(TimestampMillisecondArray::from_iter_values(
1309 timestamps.iter().copied(),
1310 )),
1311 build_test_pk_array(primary_keys),
1312 Arc::new(UInt64Array::from_iter_values(sequences.iter().copied())),
1313 Arc::new(UInt8Array::from_iter_values(
1314 op_types.iter().map(|v| *v as u8),
1315 )),
1316 ];
1317
1318 RecordBatch::try_new(build_test_flat_schema(), columns).unwrap()
1319 }
1320
1321 fn build_test_pk_string_dict_array(primary_keys: &[&[u8]]) -> ArrayRef {
1322 let mut builder = StringDictionaryBuilder::<UInt32Type>::new();
1323 for &pk in primary_keys {
1324 let pk_str = std::str::from_utf8(pk).unwrap();
1325 builder.append(pk_str).unwrap();
1326 }
1327 Arc::new(builder.finish())
1328 }
1329
1330 fn build_test_pk_array(primary_keys: &[&[u8]]) -> ArrayRef {
1331 let mut builder = BinaryDictionaryBuilder::<UInt32Type>::new();
1332 for &pk in primary_keys {
1333 builder.append(pk).unwrap();
1334 }
1335 Arc::new(builder.finish())
1336 }
1337
1338 fn build_test_flat_schema() -> SchemaRef {
1339 let fields = vec![
1340 Field::new(
1341 "k0",
1342 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Utf8)),
1343 false,
1344 ),
1345 Field::new("field0", DataType::Int64, true),
1346 Field::new(
1347 "ts",
1348 DataType::Timestamp(TimeUnit::Millisecond, None),
1349 false,
1350 ),
1351 Field::new(
1352 "__primary_key",
1353 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Binary)),
1354 false,
1355 ),
1356 Field::new("__sequence", DataType::UInt64, false),
1357 Field::new("__op_type", DataType::UInt8, false),
1358 ];
1359 Arc::new(Schema::new(fields))
1360 }
1361
1362 #[test]
1363 fn test_empty_buffer_first_push() {
1364 let mut divider = FlatSeriesBatchDivider::default();
1365 let result = divider.finish();
1366 assert!(result.is_none());
1367
1368 let mut divider = FlatSeriesBatchDivider::default();
1369 let batch = new_test_record_batch(
1370 &[b"series1", b"series1"],
1371 &[1000, 2000],
1372 &[1, 2],
1373 &[OpType::Put, OpType::Put],
1374 &[10, 20],
1375 );
1376 let result = divider.push(batch);
1377 assert!(result.is_none());
1378 assert_eq!(divider.buffer.batches.len(), 1);
1379 }
1380
1381 #[test]
1382 fn test_same_series_accumulation() {
1383 let mut divider = FlatSeriesBatchDivider::default();
1384
1385 let batch1 = new_test_record_batch(
1386 &[b"series1", b"series1"],
1387 &[1000, 2000],
1388 &[1, 2],
1389 &[OpType::Put, OpType::Put],
1390 &[10, 20],
1391 );
1392
1393 let batch2 = new_test_record_batch(
1394 &[b"series1", b"series1"],
1395 &[3000, 4000],
1396 &[3, 4],
1397 &[OpType::Put, OpType::Put],
1398 &[30, 40],
1399 );
1400
1401 divider.push(batch1);
1402 let result = divider.push(batch2);
1403 assert!(result.is_none());
1404 let series_batch = divider.finish().unwrap();
1405 assert_eq!(series_batch.batches.len(), 2);
1406 }
1407
1408 #[test]
1409 fn test_series_boundary_detection() {
1410 let mut divider = FlatSeriesBatchDivider::default();
1411
1412 let batch1 = new_test_record_batch(
1413 &[b"series1", b"series1"],
1414 &[1000, 2000],
1415 &[1, 2],
1416 &[OpType::Put, OpType::Put],
1417 &[10, 20],
1418 );
1419
1420 let batch2 = new_test_record_batch(
1421 &[b"series2", b"series2"],
1422 &[3000, 4000],
1423 &[3, 4],
1424 &[OpType::Put, OpType::Put],
1425 &[30, 40],
1426 );
1427
1428 divider.push(batch1);
1429 let series_batch = divider.push(batch2).unwrap();
1430 assert_eq!(series_batch.batches.len(), 1);
1431
1432 assert_eq!(divider.buffer.batches.len(), 1);
1433 }
1434
1435 #[test]
1436 fn test_series_boundary_within_batch() {
1437 let mut divider = FlatSeriesBatchDivider::default();
1438
1439 let batch1 = new_test_record_batch(
1440 &[b"series1", b"series1"],
1441 &[1000, 2000],
1442 &[1, 2],
1443 &[OpType::Put, OpType::Put],
1444 &[10, 20],
1445 );
1446
1447 let batch2 = new_test_record_batch(
1448 &[b"series1", b"series2"],
1449 &[3000, 4000],
1450 &[3, 4],
1451 &[OpType::Put, OpType::Put],
1452 &[30, 40],
1453 );
1454
1455 divider.push(batch1);
1456 let series_batch = divider.push(batch2).unwrap();
1457 assert_eq!(series_batch.batches.len(), 2);
1458 assert_eq!(series_batch.batches[0].num_rows(), 2);
1459 assert_eq!(series_batch.batches[1].num_rows(), 1);
1460
1461 assert_eq!(divider.buffer.batches.len(), 1);
1462 assert_eq!(divider.buffer.batches[0].num_rows(), 1);
1463 }
1464
1465 #[test]
1466 fn test_series_splitting() {
1467 let mut divider = FlatSeriesBatchDivider::default();
1468
1469 let batch1 = new_test_record_batch(&[b"series1"], &[1000], &[1], &[OpType::Put], &[10]);
1470
1471 let batch2 = new_test_record_batch(
1472 &[b"series1", b"series2", b"series2", b"series3"],
1473 &[2000, 3000, 4000, 5000],
1474 &[2, 3, 4, 5],
1475 &[OpType::Put, OpType::Put, OpType::Put, OpType::Put],
1476 &[20, 30, 40, 50],
1477 );
1478
1479 divider.push(batch1);
1480 let series_batch = divider.push(batch2).unwrap();
1481 assert_eq!(series_batch.batches.len(), 2);
1482
1483 let total_rows: usize = series_batch.batches.iter().map(|b| b.num_rows()).sum();
1484 assert_eq!(total_rows, 2);
1485
1486 let final_batch = divider.finish().unwrap();
1487 assert_eq!(final_batch.batches.len(), 1);
1488 assert_eq!(final_batch.batches[0].num_rows(), 3);
1489 }
1490}