1use std::collections::HashSet;
18use std::sync::Arc;
19use std::time::Instant;
20
21use async_stream::try_stream;
22use futures::TryStreamExt;
23use mito_codec::row_converter::{PrimaryKeyFilter, SparsePrimaryKeyCodec};
24use snafu::ResultExt;
25use store_api::region_engine::PartitionRange;
26use store_api::storage::SequenceRange;
27use tokio::sync::Semaphore;
28
29#[cfg(feature = "enterprise")]
30use crate::error::InvalidRequestSnafu;
31use crate::error::{JoinSnafu, Result, UnexpectedSnafu};
32use crate::read::BoxedRecordBatchStream;
33use crate::read::pruner::PartitionPruner;
34use crate::read::range_cache::{CachedRangeReader, build_series_range_cache_key};
35use crate::read::scan_region::StreamContext;
36use crate::read::scan_util::{
37 PartitionMetrics, SplitRecordBatchStream, compute_average_batch_size,
38 compute_parallel_channel_size, filter_flat_batch_by_sequence, new_filter_metrics,
39 prepare_flat_mem_stream, scan_flat_mem_ranges, should_split_flat_batches_for_merge,
40};
41use crate::read::seq_scan::SeqScan;
42use crate::read::series_candidate::validate_metric_metadata;
43use crate::series_index::MetricSeriesId;
44use crate::sst::parquet::DEFAULT_READ_BATCH_SIZE;
45use crate::sst::parquet::flat_format::primary_key_column_index;
46use crate::sst::parquet::prefilter::prefilter_flat_batch_by_primary_key;
47use crate::sst::parquet::reader::ReaderMetrics;
48use crate::sst::parquet::row_group::ParquetFetchMetrics;
49
50const TSID_DOMAIN_END: u128 = 1u128 << u64::BITS;
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub(crate) struct SeriesRange {
55 start: u128,
56 end: u128,
57}
58
59impl SeriesRange {
60 pub(crate) fn new(partition: usize, partitions: usize) -> Option<Self> {
61 if partitions == 0 || partition >= partitions {
62 return None;
63 }
64
65 let partitions = partitions as u128;
66 let partition = partition as u128;
67 let boundary = |partition: u128| {
68 let numerator = partition * TSID_DOMAIN_END;
69 numerator.div_ceil(partitions)
70 };
71 Some(Self {
72 start: boundary(partition),
73 end: boundary(partition + 1),
74 })
75 }
76
77 fn partition_for(tsid: u64, partitions: usize) -> usize {
78 ((tsid as u128 * partitions as u128) >> u64::BITS) as usize
79 }
80}
81
82#[derive(Debug)]
84pub(crate) struct AssignedSeriesBatch {
85 range: SeriesRange,
86 series: Vec<MetricSeriesId>,
87 enable_range_cache: bool,
88}
89
90impl AssignedSeriesBatch {
91 fn new(range: SeriesRange, series: Vec<MetricSeriesId>, enable_range_cache: bool) -> Self {
92 Self {
93 range,
94 series,
95 enable_range_cache,
96 }
97 }
98
99 pub(crate) fn range(&self) -> SeriesRange {
100 self.range
101 }
102
103 pub(crate) fn series(&self) -> &[MetricSeriesId] {
104 &self.series
105 }
106
107 pub(crate) fn enable_range_cache(&self) -> bool {
108 self.enable_range_cache
109 }
110}
111
112pub(crate) struct SeriesBatchCollector {
114 assignments: Vec<Vec<MetricSeriesId>>,
115 num_series: usize,
116}
117
118impl SeriesBatchCollector {
119 pub(crate) fn new(partitions: usize) -> Option<Self> {
120 (partitions > 0).then(|| Self {
121 assignments: (0..partitions).map(|_| Vec::new()).collect(),
122 num_series: 0,
123 })
124 }
125
126 pub(crate) fn push(&mut self, batch: Vec<MetricSeriesId>) {
127 self.num_series += batch.len();
128 let partitions = self.assignments.len();
129 for series in batch {
130 let partition = SeriesRange::partition_for(series.tsid, partitions);
131 self.assignments[partition].push(series);
132 }
133 }
134
135 pub(crate) fn len(&self) -> usize {
136 self.num_series
137 }
138
139 pub(crate) fn finish(self, enable_range_cache: bool) -> Vec<AssignedSeriesBatch> {
140 let partitions = self.assignments.len();
141 self.assignments
142 .into_iter()
143 .enumerate()
144 .map(|(partition, series)| {
145 AssignedSeriesBatch::new(
146 SeriesRange::new(partition, partitions).unwrap(),
147 series,
148 enable_range_cache,
149 )
150 })
151 .collect()
152 }
153}
154
155#[derive(Clone, Debug)]
157struct MetricSeriesFilter {
158 range: SeriesRange,
159 series: Arc<HashSet<MetricSeriesId>>,
160 sorted_series: Arc<Vec<MetricSeriesId>>,
161 enable_range_cache: bool,
162}
163
164impl MetricSeriesFilter {
165 fn new(assigned: &AssignedSeriesBatch) -> Self {
166 let mut sorted_series = assigned.series().to_vec();
167 sorted_series.sort_unstable();
168 sorted_series.dedup();
169 let series = sorted_series.iter().copied().collect();
170 Self {
171 range: assigned.range(),
172 series: Arc::new(series),
173 sorted_series: Arc::new(sorted_series),
174 enable_range_cache: assigned.enable_range_cache(),
175 }
176 }
177
178 fn primary_key_filter(&self, codec: SparsePrimaryKeyCodec) -> Box<dyn PrimaryKeyFilter> {
179 Box::new(MetricSeriesPrimaryKeyFilter {
180 codec,
181 series: self.series.clone(),
182 last_primary_key: Vec::new(),
183 last_match: None,
184 })
185 }
186
187 fn overlaps_encoded_bounds(
188 &self,
189 codec: &SparsePrimaryKeyCodec,
190 encoded_min: &[u8],
191 encoded_max: &[u8],
192 ) -> Option<bool> {
193 let (min_table_id, min_tsid) = codec.decode_ids(encoded_min).ok()?;
194 let (max_table_id, max_tsid) = codec.decode_ids(encoded_max).ok()?;
195 let min = MetricSeriesId {
196 table_id: min_table_id,
197 tsid: min_tsid,
198 };
199 let max = MetricSeriesId {
200 table_id: max_table_id,
201 tsid: max_tsid,
202 };
203 if min > max {
204 return None;
205 }
206 if min_table_id != max_table_id {
207 return Some(true);
208 }
209
210 Some(u128::from(min_tsid) < self.range.end && u128::from(max_tsid) >= self.range.start)
211 }
212}
213
214struct MetricSeriesPrimaryKeyFilter {
215 codec: SparsePrimaryKeyCodec,
216 series: Arc<HashSet<MetricSeriesId>>,
217 last_primary_key: Vec<u8>,
218 last_match: Option<bool>,
219}
220
221impl PrimaryKeyFilter for MetricSeriesPrimaryKeyFilter {
222 fn matches(&mut self, primary_key: &[u8]) -> mito_codec::error::Result<bool> {
223 if let Some(last_match) = self.last_match
224 && self.last_primary_key == primary_key
225 {
226 return Ok(last_match);
227 }
228
229 let (table_id, tsid) = self.codec.decode_ids(primary_key)?;
230 let matched = self.series.contains(&MetricSeriesId { table_id, tsid });
231 self.last_primary_key.clear();
232 self.last_primary_key.extend_from_slice(primary_key);
233 self.last_match = Some(matched);
234 Ok(matched)
235 }
236}
237
238fn filter_flat_stream_by_series(
239 mut input: BoxedRecordBatchStream,
240 codec: SparsePrimaryKeyCodec,
241 filter: MetricSeriesFilter,
242) -> BoxedRecordBatchStream {
243 Box::pin(try_stream! {
244 let mut primary_key_filter = filter.primary_key_filter(codec);
245 while let Some(batch) = input.try_next().await? {
246 let pk_idx = primary_key_column_index(batch.num_columns());
247 if let Some(batch) = prefilter_flat_batch_by_primary_key(
248 batch,
249 pk_idx,
250 primary_key_filter.as_mut(),
251 )? {
252 yield batch;
253 }
254 }
255 })
256}
257
258pub(crate) struct SeriesReader {
260 stream_ctx: Arc<StreamContext>,
261 partition_ranges: Vec<PartitionRange>,
262 filter: MetricSeriesFilter,
263 codec: SparsePrimaryKeyCodec,
264 partition_pruner: Arc<PartitionPruner>,
265 range_semaphore: Arc<Semaphore>,
266 part_metrics: PartitionMetrics,
267}
268
269impl SeriesReader {
270 pub(crate) fn try_new(
280 stream_ctx: Arc<StreamContext>,
281 partition_ranges: Vec<PartitionRange>,
282 assigned_series: AssignedSeriesBatch,
283 partition_pruner: Arc<PartitionPruner>,
284 range_semaphore: Arc<Semaphore>,
285 part_metrics: PartitionMetrics,
286 ) -> Result<Self> {
287 validate_metric_metadata(&stream_ctx)?;
288 #[cfg(feature = "enterprise")]
289 snafu::ensure!(
290 stream_ctx.input.extension_ranges().is_empty(),
291 InvalidRequestSnafu {
292 region_id: stream_ctx.input.region_metadata().region_id,
293 reason: "series reader does not support extension ranges",
294 }
295 );
296
297 let filter = MetricSeriesFilter::new(&assigned_series);
298 let codec = SparsePrimaryKeyCodec::new(stream_ctx.input.region_metadata());
299 Ok(Self {
300 stream_ctx,
301 partition_ranges,
302 filter,
303 codec,
304 partition_pruner,
305 range_semaphore,
306 part_metrics,
307 })
308 }
309
310 pub(crate) async fn build_stream(&self) -> Result<BoxedRecordBatchStream> {
311 if self.partition_ranges.is_empty() || self.filter.series.is_empty() {
312 return Ok(Box::pin(futures::stream::empty()));
313 }
314
315 let mut tasks = Vec::with_capacity(self.partition_ranges.len());
316 for part_range in self.partition_ranges.iter().copied() {
317 let stream_ctx = self.stream_ctx.clone();
318 let filter = self.filter.clone();
319 let codec = self.codec.clone();
320 let partition_pruner = self.partition_pruner.clone();
321 let range_semaphore = self.range_semaphore.clone();
322 let part_metrics = self.part_metrics.clone();
323 tasks.push(common_runtime::spawn_query(async move {
324 let _permit = range_semaphore.acquire().await.map_err(|error| {
325 UnexpectedSnafu {
326 reason: format!("failed to acquire series range permit: {error}"),
327 }
328 .build()
329 })?;
330 build_series_partition_range(
331 stream_ctx,
332 part_range,
333 filter,
334 codec,
335 partition_pruner,
336 part_metrics,
337 )
338 .await
339 }));
340 }
341
342 let mut range_streams = Vec::with_capacity(tasks.len());
343 let mut estimated_batch_sizes = Vec::with_capacity(tasks.len());
344 for task in tasks {
345 let (stream, estimated_batch_size) = task.await.context(JoinSnafu)??;
346 range_streams.push(stream);
347 estimated_batch_sizes.push(estimated_batch_size);
348 }
349
350 let estimated_batch_size = compute_average_batch_size(estimated_batch_sizes);
353 SeqScan::build_flat_reader_from_sources(
354 &self.stream_ctx,
355 range_streams,
356 Some(self.range_semaphore.clone()),
357 Some(&self.part_metrics),
358 true,
359 compute_parallel_channel_size(estimated_batch_size),
360 )
361 .await
362 }
363}
364
365async fn build_series_partition_range(
366 stream_ctx: Arc<StreamContext>,
367 part_range: PartitionRange,
368 filter: MetricSeriesFilter,
369 codec: SparsePrimaryKeyCodec,
370 partition_pruner: Arc<PartitionPruner>,
371 part_metrics: PartitionMetrics,
372) -> Result<(BoxedRecordBatchStream, usize)> {
373 let range = filter.range;
374 let cache_key = filter
375 .enable_range_cache
376 .then(|| build_series_range_cache_key(&stream_ctx, &part_range, range))
377 .flatten();
378 let ctx = stream_ctx.clone();
379 let metrics = part_metrics.clone();
380 CachedRangeReader {
381 stream_ctx: &stream_ctx,
382 part_range: &part_range,
383 key: cache_key,
384 metrics: &part_metrics,
385 }
386 .build(move || async move {
387 build_uncached_series_partition_range(
388 ctx,
389 part_range,
390 filter,
391 codec,
392 partition_pruner,
393 metrics,
394 )
395 .await
396 })
397 .await
398}
399
400async fn build_uncached_series_partition_range(
401 stream_ctx: Arc<StreamContext>,
402 part_range: PartitionRange,
403 filter: MetricSeriesFilter,
404 codec: SparsePrimaryKeyCodec,
405 partition_pruner: Arc<PartitionPruner>,
406 part_metrics: PartitionMetrics,
407) -> Result<(BoxedRecordBatchStream, usize)> {
408 let range_meta = &stream_ctx.ranges[part_range.identifier];
409 let split_batch_size = should_split_flat_batches_for_merge(&stream_ctx, range_meta);
410 let mut sources = Vec::with_capacity(range_meta.row_group_indices.len());
411
412 for index in range_meta.row_group_indices.iter().copied() {
413 if stream_ctx.is_mem_range_index(index) {
414 let stream = scan_flat_mem_ranges(
415 stream_ctx.clone(),
416 part_metrics.clone(),
417 index,
418 range_meta.time_range,
419 );
420 let stream = prepare_flat_mem_stream(stream_ctx.clone(), stream);
421 sources.push(filter_flat_stream_by_series(
422 stream,
423 codec.clone(),
424 filter.clone(),
425 ));
426 continue;
427 }
428
429 if stream_ctx.is_file_range_index(index) {
430 let file = stream_ctx.input.file_from_index(index);
431 if matches!(
432 file.primary_key_range(stream_ctx.input.primary_key_mapper())
433 .and_then(|(min, max)| { filter.overlaps_encoded_bounds(&codec, &min, &max) }),
434 Some(false)
435 ) {
436 continue;
437 }
438
439 if partition_pruner.try_skip_manifest_pruned_file_range(index, &part_metrics) {
440 continue;
441 }
442 let mut reader_metrics = ReaderMetrics {
443 filter_metrics: new_filter_metrics(part_metrics.explain_verbose()),
444 ..Default::default()
445 };
446 let file_ranges = partition_pruner
447 .build_file_ranges(index, &part_metrics, &mut reader_metrics)
448 .await?;
449 part_metrics.inc_num_file_ranges(file_ranges.len());
450 part_metrics.merge_reader_metrics(&reader_metrics, None);
451 let ranges = file_ranges
452 .iter()
453 .filter(|file_range| {
454 !matches!(
455 file_range.primary_key_range().and_then(|(min, max)| {
456 filter.overlaps_encoded_bounds(&codec, min, max)
457 }),
458 Some(false)
459 )
460 })
461 .cloned()
462 .collect::<smallvec::SmallVec<[_; 2]>>();
463 if ranges.is_empty() {
464 continue;
465 }
466
467 let stream = scan_series_file_ranges(
468 part_metrics.clone(),
469 ranges,
470 filter.clone(),
471 codec.clone(),
472 stream_ctx.input.sequence_range,
473 stream_ctx.input.region_metadata().region_id,
474 );
475 sources.push(Box::pin(stream) as BoxedRecordBatchStream);
476 continue;
477 }
478
479 return UnexpectedSnafu {
480 reason: format!(
481 "series reader received unsupported range index {}",
482 index.index
483 ),
484 }
485 .fail();
486 }
487
488 if split_batch_size.is_some() {
489 sources = sources
490 .into_iter()
491 .map(|stream| Box::pin(SplitRecordBatchStream::new(stream)) as BoxedRecordBatchStream)
492 .collect();
493 }
494 let estimated_batch_size = split_batch_size.unwrap_or(DEFAULT_READ_BATCH_SIZE);
495 let stream = SeqScan::build_flat_reader_from_sources(
496 &stream_ctx,
497 sources,
498 None,
499 Some(&part_metrics),
500 false,
501 compute_parallel_channel_size(estimated_batch_size),
502 )
503 .await?;
504 Ok((stream, estimated_batch_size))
505}
506
507fn scan_series_file_ranges(
508 part_metrics: PartitionMetrics,
509 ranges: smallvec::SmallVec<[crate::sst::parquet::file_range::FileRange; 2]>,
510 filter: MetricSeriesFilter,
511 codec: SparsePrimaryKeyCodec,
512 sequence_range: Option<SequenceRange>,
513 region_id: store_api::storage::RegionId,
514) -> impl futures::Stream<Item = Result<datatypes::arrow::record_batch::RecordBatch>> {
515 try_stream! {
516 let fetch_metrics = part_metrics
517 .explain_verbose()
518 .then(|| Arc::new(ParquetFetchMetrics::default()));
519 let mut reader_metrics = ReaderMetrics {
520 fetch_metrics: fetch_metrics.clone(),
521 ..Default::default()
522 };
523 let mut primary_key_filter = filter.primary_key_filter(codec);
524
525 for range in ranges {
526 let build_start = Instant::now();
527 let searcher = range.range_index_searcher().await?;
528 let reader = if let Some(searcher) = searcher {
529 let row_group_id = u32::try_from(range.row_group_index()).map_err(|_| UnexpectedSnafu {
530 reason: format!("row group index exceeds u32: {}", range.row_group_index()),
531 }.build())?;
532 let selected = searcher.search(row_group_id, &filter.sorted_series).await?;
533 range.reader_by_row_ranges(selected, fetch_metrics.as_deref()).await?
534 } else {
535 range.reader_by_primary_key(primary_key_filter.as_mut(), fetch_metrics.as_deref()).await?
536 };
537 let build_cost = build_start.elapsed();
538 reader_metrics.build_cost += build_cost;
539 part_metrics.inc_build_reader_cost(build_cost);
540 let Some(mut reader) = reader else {
541 continue;
542 };
543
544 let scan_start = Instant::now();
545 let file_sequence_trusted = range
546 .file_handle()
547 .is_effective_target_sequence_trusted(region_id);
548 while let Some(record_batch) = reader.next_batch().await? {
549 reader_metrics.num_record_batches += 1;
550 reader_metrics.num_batches += 1;
551 reader_metrics.num_rows += record_batch.num_rows();
552
553 let Some(record_batch) = filter_flat_batch_by_sequence(
554 record_batch,
555 sequence_range,
556 file_sequence_trusted,
557 )? else {
558 continue;
559 };
560
561 let num_rows_before_filter = record_batch.num_rows();
562 let Some(record_batch) = range.precise_filter_flat(
563 record_batch,
564 range.pre_filter_mode().skip_fields(),
565 true,
566 )? else {
567 reader_metrics.filter_metrics.rows_precise_filtered +=
568 num_rows_before_filter;
569 continue;
570 };
571 reader_metrics.filter_metrics.rows_precise_filtered +=
572 num_rows_before_filter - record_batch.num_rows();
573
574 let record_batch = if let Some(mapper) = range.compaction_projection_mapper() {
575 mapper.project(record_batch)?
576 } else {
577 record_batch
578 };
579 if let Some(compat) = range.compat_batch() {
580 yield compat.compat(record_batch)?;
581 } else {
582 yield record_batch;
583 }
584 }
585 reader_metrics.scan_cost += scan_start.elapsed();
586 }
587
588 reader_metrics.observe_rows("series_data");
589 reader_metrics.filter_metrics.observe();
590 part_metrics.merge_reader_metrics(&reader_metrics, None);
591 }
592}
593
594#[cfg(test)]
595mod tests {
596 use store_api::codec::PrimaryKeyEncoding;
597
598 use super::*;
599 use crate::error::DecodeSnafu;
600 use crate::test_util::sst_util::sst_region_metadata_with_encoding;
601
602 fn series(table_id: u32, tsid: u64) -> MetricSeriesId {
603 MetricSeriesId { table_id, tsid }
604 }
605
606 fn assigned_batch(
607 partitions: usize,
608 partition: usize,
609 series: Vec<MetricSeriesId>,
610 ) -> AssignedSeriesBatch {
611 let mut collector = SeriesBatchCollector::new(partitions).unwrap();
612 collector.push(series);
613 collector.finish(true).remove(partition)
614 }
615
616 #[test]
617 fn series_range_assignment_is_stable_across_batch_boundaries() {
618 let input = vec![
619 series(1, 0),
620 series(2, 1u64 << 62),
621 series(1, 1u64 << 63),
622 series(2, 3u64 << 62),
623 series(1, u64::MAX),
624 series(3, 0),
625 ];
626 let mut first = SeriesBatchCollector::new(4).unwrap();
627 first.push(input.clone());
628 let first = first.finish(true);
629
630 let mut second = SeriesBatchCollector::new(4).unwrap();
631 for chunk in input.chunks(2) {
632 second.push(chunk.to_vec());
633 }
634 let second = second.finish(true);
635
636 assert_eq!(
637 first
638 .iter()
639 .map(AssignedSeriesBatch::series)
640 .collect::<Vec<_>>(),
641 second
642 .iter()
643 .map(AssignedSeriesBatch::series)
644 .collect::<Vec<_>>()
645 );
646 assert_eq!(&[series(1, 0), series(3, 0)], first[0].series());
647 assert_eq!(&[series(2, 1u64 << 62)], first[1].series());
648 assert_eq!(
649 input.len(),
650 first
651 .iter()
652 .map(|batch| batch.series().len())
653 .sum::<usize>()
654 );
655 }
656
657 #[test]
658 fn collector_tracks_size_and_cache_policy() {
659 let mut collector = SeriesBatchCollector::new(2).unwrap();
660 collector.push(vec![series(1, 1), series(1, u64::MAX)]);
661 collector.push(vec![series(2, 2)]);
662 assert_eq!(3, collector.len());
663
664 let assignments = collector.finish(false);
665 assert_eq!(
666 3,
667 assignments
668 .iter()
669 .map(|batch| batch.series().len())
670 .sum::<usize>()
671 );
672 assert!(assignments.iter().all(|batch| !batch.enable_range_cache()));
673 }
674
675 #[test]
676 fn series_ranges_cover_the_tsid_domain() {
677 let ranges = (0..3)
678 .map(|partition| SeriesRange::new(partition, 3).unwrap())
679 .collect::<Vec<_>>();
680 assert_eq!(0, ranges[0].start);
681 assert_eq!(TSID_DOMAIN_END, ranges[2].end);
682 assert_eq!(ranges[0].end, ranges[1].start);
683 assert_eq!(ranges[1].end, ranges[2].start);
684 assert_eq!(0, SeriesRange::partition_for(0, 3));
685 assert_eq!(2, SeriesRange::partition_for(u64::MAX, 3));
686
687 for (partition, range) in ranges.iter().enumerate() {
688 assert_eq!(partition, SeriesRange::partition_for(range.start as u64, 3));
689 if range.end < TSID_DOMAIN_END {
690 assert_eq!(
691 partition + 1,
692 SeriesRange::partition_for(range.end as u64, 3)
693 );
694 }
695 }
696 }
697
698 #[test]
699 fn metric_series_filter_matches_encoded_primary_key() {
700 let metadata = Arc::new(sst_region_metadata_with_encoding(
701 PrimaryKeyEncoding::Sparse,
702 ));
703 let assigned = assigned_batch(1, 0, vec![series(1, 10), series(2, 20)]);
704 let filter = MetricSeriesFilter::new(&assigned);
705 let codec = SparsePrimaryKeyCodec::new(&metadata);
706 let mut primary_key_filter = filter.primary_key_filter(codec);
707
708 for selected in [series(1, 10), series(2, 20)] {
709 assert!(
710 primary_key_filter
711 .matches(&encode_series(&metadata, selected.table_id, selected.tsid))
712 .context(DecodeSnafu)
713 .unwrap()
714 );
715 }
716 for unselected in [series(2, 10), series(1, 20)] {
717 assert!(
718 !primary_key_filter
719 .matches(&encode_series(
720 &metadata,
721 unselected.table_id,
722 unselected.tsid,
723 ))
724 .context(DecodeSnafu)
725 .unwrap()
726 );
727 }
728 }
729
730 fn encode_series(
731 metadata: &store_api::metadata::RegionMetadataRef,
732 table_id: u32,
733 tsid: u64,
734 ) -> Vec<u8> {
735 let codec = SparsePrimaryKeyCodec::new(metadata);
736 let mut primary_key = Vec::new();
737 codec
738 .encode_internal(table_id, tsid, &mut primary_key)
739 .unwrap();
740 primary_key
741 }
742
743 #[test]
744 fn series_range_overlaps_single_table_primary_key_bounds() {
745 let metadata = Arc::new(sst_region_metadata_with_encoding(
746 PrimaryKeyEncoding::Sparse,
747 ));
748 let assigned = assigned_batch(2, 0, vec![series(1, 10), series(2, 20)]);
749 let filter = MetricSeriesFilter::new(&assigned);
750 let codec = SparsePrimaryKeyCodec::new(&metadata);
751
752 assert_eq!(
754 Some(true),
755 filter.overlaps_encoded_bounds(
756 &codec,
757 &encode_series(&metadata, 1, 100),
758 &encode_series(&metadata, 1, 200),
759 )
760 );
761 assert_eq!(
762 Some(false),
763 filter.overlaps_encoded_bounds(
764 &codec,
765 &encode_series(&metadata, 1, 1u64 << 63),
766 &encode_series(&metadata, 1, u64::MAX),
767 )
768 );
769 assert_eq!(
770 Some(true),
771 filter.overlaps_encoded_bounds(
772 &codec,
773 &encode_series(&metadata, 3, 10),
774 &encode_series(&metadata, 3, 20),
775 )
776 );
777 }
778
779 #[test]
780 fn series_range_keeps_multiple_table_primary_key_bounds() {
781 let metadata = Arc::new(sst_region_metadata_with_encoding(
782 PrimaryKeyEncoding::Sparse,
783 ));
784 let assigned = assigned_batch(2, 0, vec![series(1, 10), series(2, 20)]);
785 let filter = MetricSeriesFilter::new(&assigned);
786 let codec = SparsePrimaryKeyCodec::new(&metadata);
787
788 assert_eq!(
789 Some(true),
790 filter.overlaps_encoded_bounds(
791 &codec,
792 &encode_series(&metadata, 1, 1u64 << 63),
793 &encode_series(&metadata, 2, u64::MAX),
794 )
795 );
796 }
797
798 #[test]
799 fn series_statistics_keep_invalid_or_inverted_bounds() {
800 let metadata = Arc::new(sst_region_metadata_with_encoding(
801 PrimaryKeyEncoding::Sparse,
802 ));
803 let assigned = assigned_batch(2, 0, vec![series(1, 10)]);
804 let filter = MetricSeriesFilter::new(&assigned);
805 let codec = SparsePrimaryKeyCodec::new(&metadata);
806
807 assert_eq!(
808 None,
809 filter.overlaps_encoded_bounds(&codec, b"invalid", b"bounds")
810 );
811 assert_eq!(
812 None,
813 filter.overlaps_encoded_bounds(
814 &codec,
815 &encode_series(&metadata, 2, 0),
816 &encode_series(&metadata, 1, 0),
817 )
818 );
819 }
820}