1use std::cmp::Ordering;
19use std::collections::BinaryHeap;
20
21use bytes::{Buf, Bytes};
22use common_base::BitVec;
23use common_time::Timestamp;
24
25use crate::sst::file::FileHandle;
26use crate::sst::primary_key::PrimaryKeyRangeMapper;
27
28pub trait Ranged {
30 type BoundType: Ord + Copy;
31
32 fn range(&self) -> (Self::BoundType, Self::BoundType);
34
35 fn overlap(&self, other: &Self) -> bool {
36 let (lhs_start, lhs_end) = self.range();
37 let (rhs_start, rhs_end) = other.range();
38
39 lhs_start.max(rhs_start) < lhs_end.min(rhs_end)
40 }
41
42 fn overlap_inclusive(&self, other: &Self) -> bool {
45 let (lhs_start, lhs_end) = self.range();
46 let (rhs_start, rhs_end) = other.range();
47
48 lhs_start.max(rhs_start) <= lhs_end.min(rhs_end)
49 }
50}
51
52pub(crate) fn primary_key_ranges_overlap(lhs: &(Bytes, Bytes), rhs: &(Bytes, Bytes)) -> bool {
53 lhs.0.chunk().max(rhs.0.chunk()) <= lhs.1.chunk().min(rhs.1.chunk())
54}
55
56pub(crate) fn merge_primary_key_ranges(
57 lhs: Option<(Bytes, Bytes)>,
58 rhs: Option<(Bytes, Bytes)>,
59) -> Option<(Bytes, Bytes)> {
60 match (lhs, rhs) {
61 (Some((lhs_min, lhs_max)), Some((rhs_min, rhs_max))) => {
62 Some((lhs_min.min(rhs_min), lhs_max.max(rhs_max)))
63 }
64 _ => None,
65 }
66}
67
68pub fn find_overlapping_items<T: Item + Clone>(
70 l: &mut SortedRun<T>,
71 r: &mut SortedRun<T>,
72 result: &mut Vec<T>,
73 overlaps: impl Fn(&T, &T) -> bool,
74) {
75 if l.items.is_empty() || r.items.is_empty() {
76 return;
77 }
78
79 result.clear();
80 result.reserve(l.items.len() + r.items.len());
81
82 if !l.sorted {
84 sort_ranged_items(&mut l.items);
85 l.sorted = true;
86 }
87 if !r.sorted {
88 sort_ranged_items(&mut r.items);
89 r.sorted = true;
90 }
91
92 let mut r_idx = 0;
93
94 let mut selected = BitVec::repeat(false, r.items().len() + l.items.len());
95
96 for (lhs_idx, lhs) in l.items.iter().enumerate() {
97 let (lhs_start, lhs_end) = lhs.range();
98
99 while r_idx < r.items.len() {
101 let (_, rhs_end) = r.items[r_idx].range();
102 if rhs_end < lhs_start {
103 r_idx += 1;
104 } else {
105 break;
106 }
107 }
108
109 let mut j = r_idx;
111 while j < r.items.len() {
112 let (rhs_start, _rhs_end) = r.items[j].range();
113
114 if rhs_start > lhs_end {
116 break;
117 }
118
119 if overlaps(lhs, &r.items[j]) {
121 if !selected[lhs_idx] {
122 result.push(lhs.clone());
123 selected.set(lhs_idx, true);
124 }
125
126 let rhs_selected_idx = l.items.len() + j;
127 if !selected[rhs_selected_idx] {
128 result.push(r.items[j].clone());
129 selected.set(rhs_selected_idx, true);
130 }
131 }
132
133 j += 1;
134 }
135 }
136}
137
138fn sort_ranged_items<T: Ranged>(values: &mut [T]) {
140 values.sort_unstable_by(|l, r| {
141 let (l_start, l_end) = l.range();
142 let (r_start, r_end) = r.range();
143 l_start.cmp(&r_start).then(r_end.cmp(&l_end))
144 });
145}
146
147pub trait Item: Ranged + Clone {
149 fn size(&self) -> usize;
151}
152
153impl Ranged for FileHandle {
155 type BoundType = Timestamp;
156
157 fn range(&self) -> (Self::BoundType, Self::BoundType) {
158 self.time_range()
159 }
160}
161
162pub(crate) fn files_overlap(
164 lhs: &FileHandle,
165 rhs: &FileHandle,
166 mapper: &PrimaryKeyRangeMapper,
167) -> bool {
168 lhs.overlap(rhs) && file_primary_keys_overlap(lhs, rhs, mapper)
169}
170
171pub(crate) fn files_overlap_inclusive(
173 lhs: &FileHandle,
174 rhs: &FileHandle,
175 mapper: &PrimaryKeyRangeMapper,
176) -> bool {
177 lhs.overlap_inclusive(rhs) && file_primary_keys_overlap(lhs, rhs, mapper)
178}
179
180fn file_primary_keys_overlap(
181 lhs: &FileHandle,
182 rhs: &FileHandle,
183 mapper: &PrimaryKeyRangeMapper,
184) -> bool {
185 match (lhs.primary_key_range(mapper), rhs.primary_key_range(mapper)) {
186 (Some(lhs), Some(rhs)) => primary_key_ranges_overlap(&lhs, &rhs),
187 _ => true,
188 }
189}
190
191impl Item for FileHandle {
192 fn size(&self) -> usize {
193 self.size() as usize
194 }
195}
196
197#[derive(Debug, Clone)]
199pub struct SortedRun<T: Item> {
200 items: Vec<T>,
202 size: usize,
204 start: Option<T::BoundType>,
206 end: Option<T::BoundType>,
208 sorted: bool,
210}
211
212impl<T: Item> From<Vec<T>> for SortedRun<T> {
213 fn from(items: Vec<T>) -> Self {
214 let mut r = Self {
215 items: Vec::with_capacity(items.len()),
216 size: 0,
217 start: None,
218 end: None,
219 sorted: false,
220 };
221 for item in items {
222 r.push_item(item);
223 }
224
225 r
226 }
227}
228
229impl<T> Default for SortedRun<T>
230where
231 T: Item,
232{
233 fn default() -> Self {
234 Self {
235 items: vec![],
236 size: 0,
237 start: None,
238 end: None,
239 sorted: false,
240 }
241 }
242}
243
244impl<T> SortedRun<T>
245where
246 T: Item,
247{
248 pub fn items(&self) -> &[T] {
249 &self.items
250 }
251
252 fn push_item(&mut self, t: T) {
253 let (file_start, file_end) = t.range();
254 self.size += t.size();
255 self.items.push(t);
256 self.start = Some(self.start.map_or(file_start, |v| v.min(file_start)));
257 self.end = Some(self.end.map_or(file_end, |v| v.max(file_end)));
258 }
259}
260
261pub fn find_sorted_runs<T>(items: &mut [T], overlaps: impl Fn(&T, &T) -> bool) -> Vec<SortedRun<T>>
263where
264 T: Item,
265{
266 if items.is_empty() {
267 return vec![];
268 }
269 sort_ranged_items(items);
271
272 let mut current_run = SortedRun::default();
273 let mut runs = vec![];
274 let mut active_run_item_indices = Vec::new();
275
276 let mut selection = BitVec::repeat(false, items.len());
277 while !selection.all() {
278 let mut last_pruned_start = None;
280 for (item, mut selected) in items.iter().zip(selection.iter_mut()) {
281 if *selected {
282 continue;
284 }
285 if current_run.items.is_empty() {
286 selected.set(true);
288 current_run.push_item(item.clone());
289 active_run_item_indices.push(current_run.items.len() - 1);
290 } else {
291 let (item_start, _) = item.range();
295 if last_pruned_start != Some(item_start) {
296 active_run_item_indices.retain(|idx| {
297 let (_, run_item_end) = current_run.items[*idx].range();
298 run_item_end > item_start
299 });
300 last_pruned_start = Some(item_start);
301 }
302
303 let mut overlaps_any = false;
304 for idx in &active_run_item_indices {
305 let run_item = ¤t_run.items[*idx];
306 if overlaps(run_item, item) {
307 overlaps_any = true;
308 break;
309 }
310 }
311 if !overlaps_any {
312 selected.set(true);
314 let item_idx = current_run.items.len();
315 current_run.push_item(item.clone());
316 active_run_item_indices.push(item_idx);
317 }
318 }
319 }
320 runs.push(std::mem::take(&mut current_run));
322 active_run_item_indices.clear();
323 }
324 runs
325}
326
327#[cfg(any(test, feature = "test", feature = "testing"))]
328pub fn find_sorted_runs_original<T>(items: &mut [T]) -> Vec<SortedRun<T>>
329where
330 T: Item,
331{
332 if items.is_empty() {
333 return vec![];
334 }
335 sort_ranged_items(items);
337
338 let mut current_run = SortedRun::default();
339 let mut runs = vec![];
340
341 let mut selection = BitVec::repeat(false, items.len());
342 while !selection.all() {
343 for (item, mut selected) in items.iter().zip(selection.iter_mut()) {
345 if *selected {
346 continue;
348 }
349 if current_run.items.is_empty() {
350 selected.set(true);
352 current_run.push_item(item.clone());
353 } else {
354 let overlaps_any = current_run.items.iter().any(|i| i.overlap(item));
358 if !overlaps_any {
359 selected.set(true);
361 current_run.push_item(item.clone());
362 }
363 }
364 }
365 runs.push(std::mem::take(&mut current_run));
367 }
368 runs
369}
370
371pub(crate) fn find_sorted_runs_by_time_range<T>(items: &mut [T]) -> Vec<SortedRun<T>>
372where
373 T: Item,
374{
375 if items.is_empty() {
376 return vec![];
377 }
378 sort_ranged_items(items);
379
380 use derive_more::{Eq, PartialEq};
381
382 #[derive(PartialEq, Eq)]
384 struct Run<T: Item> {
385 i: usize,
386 #[partial_eq(skip)]
387 run: SortedRun<T>,
388 }
389
390 impl<T: Item> Run<T> {
391 fn new(i: usize, item: &T) -> Run<T> {
392 let mut run = SortedRun::default();
393 run.push_item(item.clone());
394 Run { i, run }
395 }
396
397 fn push_item(&mut self, item: &T) {
398 self.run.push_item(item.clone());
399 }
400 }
401
402 impl<T: Item> PartialOrd for Run<T> {
403 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
404 Some(self.cmp(other))
405 }
406 }
407
408 impl<T: Item> Ord for Run<T> {
410 fn cmp(&self, other: &Self) -> Ordering {
411 let l_run = &self.run;
412 let r_run = &other.run;
413
414 let l_end = l_run.end.unwrap();
419 let r_end = r_run.end.unwrap();
420 r_end
421 .cmp(&l_end)
422 .then_with(|| {
423 let l_start = l_run.start.unwrap();
424 let r_start = r_run.start.unwrap();
425 l_start.cmp(&r_start)
426 })
427 .then_with(|| self.i.cmp(&other.i))
428 }
429 }
430
431 #[derive(PartialEq, Eq)]
433 struct Wrapper<T: Item>(Run<T>);
434
435 impl<T: Item> PartialOrd for Wrapper<T> {
436 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
437 Some(self.cmp(other))
438 }
439 }
440
441 impl<T: Item> Ord for Wrapper<T> {
442 fn cmp(&self, other: &Self) -> Ordering {
443 other.0.i.cmp(&self.0.i)
444 }
445 }
446
447 let mut runs_sort_by_end = BinaryHeap::<Run<T>>::new();
472 let mut runs_sort_by_index = BinaryHeap::<Wrapper<T>>::new();
473 let mut i = 0;
474
475 for item in items {
476 let (start, _) = item.range();
477
478 while runs_sort_by_end
479 .peek()
480 .is_some_and(|x| x.run.end.unwrap() <= start)
481 {
482 let run = runs_sort_by_end.pop().unwrap();
483 runs_sort_by_index.push(Wrapper(run));
484 }
485
486 let Some(mut run) = runs_sort_by_index.pop() else {
487 i += 1;
488 runs_sort_by_end.push(Run::new(i, item));
489 continue;
490 };
491
492 run.0.push_item(item);
493 runs_sort_by_end.push(run.0);
494 }
495
496 let mut runs = runs_sort_by_end.into_vec();
497 runs.extend(runs_sort_by_index.into_vec().into_iter().map(|x| x.0));
498 runs.sort_unstable_by_key(|run| run.i);
499 runs.into_iter().map(|x| x.run).collect()
500}
501
502#[cfg(test)]
503mod tests {
504 use store_api::storage::FileId;
505
506 use super::*;
507 use crate::compaction::test_util::{
508 new_file_handle_with_size_sequence_and_primary_key_range, pk_range,
509 primary_key_mapper_for_test,
510 };
511
512 #[derive(Clone, Debug, PartialEq)]
513 struct MockFile {
514 start: i64,
515 end: i64,
516 size: usize,
517 }
518
519 impl Ranged for MockFile {
520 type BoundType = i64;
521
522 fn range(&self) -> (Self::BoundType, Self::BoundType) {
523 (self.start, self.end)
524 }
525 }
526
527 impl Item for MockFile {
528 fn size(&self) -> usize {
529 self.size
530 }
531 }
532
533 fn build_items(ranges: &[(i64, i64)]) -> Vec<MockFile> {
534 ranges
535 .iter()
536 .map(|(start, end)| MockFile {
537 start: *start,
538 end: *end,
539 size: (*end - *start) as usize,
540 })
541 .collect()
542 }
543
544 fn check_sorted_runs(
545 ranges: &[(i64, i64)],
546 expected_runs: &[Vec<(i64, i64)>],
547 ) -> Vec<SortedRun<MockFile>> {
548 let mut files = build_items(ranges);
549 let mut files_clone = files.clone();
550
551 let runs = find_sorted_runs(&mut files, Ranged::overlap);
552
553 let result_file_ranges: Vec<Vec<_>> = runs
554 .iter()
555 .map(|r| r.items.iter().map(|f| f.range()).collect())
556 .collect();
557 assert_eq!(&expected_runs, &result_file_ranges);
558
559 let runs_by_time_range = find_sorted_runs_by_time_range(&mut files_clone);
560 let results: Vec<Vec<_>> = runs_by_time_range
561 .iter()
562 .map(|r| r.items.iter().map(|f| f.range()).collect())
563 .collect();
564 assert_eq!(&expected_runs, &results);
565 runs
566 }
567
568 fn sorted_run_ranges<T: Item>(runs: &[SortedRun<T>]) -> Vec<Vec<T::BoundType>> {
569 runs.iter()
570 .map(|r| {
571 r.items
572 .iter()
573 .flat_map(|f| {
574 let (start, end) = f.range();
575 [start, end]
576 })
577 .collect()
578 })
579 .collect()
580 }
581
582 fn check_find_sorted_runs_consistency(ranges: &[(i64, i64)]) {
583 let mut files = build_items(ranges);
584 let mut files_for_original = files.clone();
585
586 let runs = find_sorted_runs(&mut files, Ranged::overlap);
587 let original_runs = find_sorted_runs_original(&mut files_for_original);
588
589 assert_eq!(sorted_run_ranges(&original_runs), sorted_run_ranges(&runs));
590 }
591
592 #[test]
593 fn test_find_sorted_runs() {
594 check_sorted_runs(&[], &[]);
595 check_sorted_runs(&[(1, 1), (2, 2)], &[vec![(1, 1), (2, 2)]]);
596 check_sorted_runs(&[(1, 2)], &[vec![(1, 2)]]);
597 check_sorted_runs(&[(1, 2), (2, 3)], &[vec![(1, 2), (2, 3)]]);
598 check_sorted_runs(&[(1, 2), (3, 4)], &[vec![(1, 2), (3, 4)]]);
599 check_sorted_runs(&[(2, 4), (1, 3)], &[vec![(1, 3)], vec![(2, 4)]]);
600 check_sorted_runs(
601 &[(1, 3), (2, 4), (4, 5)],
602 &[vec![(1, 3), (4, 5)], vec![(2, 4)]],
603 );
604
605 check_sorted_runs(
606 &[(1, 2), (3, 4), (3, 5)],
607 &[vec![(1, 2), (3, 5)], vec![(3, 4)]],
608 );
609
610 check_sorted_runs(
611 &[(1, 3), (2, 4), (5, 6)],
612 &[vec![(1, 3), (5, 6)], vec![(2, 4)]],
613 );
614
615 check_sorted_runs(
616 &[(1, 2), (3, 5), (4, 6)],
617 &[vec![(1, 2), (3, 5)], vec![(4, 6)]],
618 );
619
620 check_sorted_runs(
621 &[(1, 2), (3, 4), (4, 6), (7, 8)],
622 &[vec![(1, 2), (3, 4), (4, 6), (7, 8)]],
623 );
624 check_sorted_runs(
625 &[(1, 2), (3, 4), (5, 6), (3, 6), (7, 8), (8, 9)],
626 &[vec![(1, 2), (3, 6), (7, 8), (8, 9)], vec![(3, 4), (5, 6)]],
627 );
628
629 check_sorted_runs(
630 &[(10, 19), (20, 21), (20, 29), (30, 39)],
631 &[vec![(10, 19), (20, 29), (30, 39)], vec![(20, 21)]],
632 );
633
634 check_sorted_runs(
635 &[(10, 19), (20, 29), (21, 22), (30, 39), (31, 32), (32, 42)],
636 &[
637 vec![(10, 19), (20, 29), (30, 39)],
638 vec![(21, 22), (31, 32), (32, 42)],
639 ],
640 );
641 }
642
643 #[test]
644 fn test_find_sorted_runs_matches_original_impl() {
645 for ranges in [
646 &[][..],
647 &[(1, 1), (2, 2)],
648 &[(1, 2), (2, 3)],
649 &[(2, 4), (1, 3)],
650 &[(1, 3), (2, 4), (4, 5)],
651 &[(1, 2), (3, 4), (3, 5)],
652 &[(1, 3), (2, 4), (5, 6)],
653 &[(1, 2), (3, 5), (4, 6)],
654 &[(1, 2), (3, 4), (4, 6), (7, 8)],
655 &[(1, 2), (3, 4), (5, 6), (3, 6), (7, 8), (8, 9)],
656 &[(10, 19), (20, 21), (20, 29), (30, 39)],
657 &[(10, 19), (20, 29), (21, 22), (30, 39), (31, 32), (32, 42)],
658 &[(32, 42), (10, 19), (31, 32), (20, 29), (21, 22), (30, 39)],
659 ] {
660 check_find_sorted_runs_consistency(ranges);
661 }
662 }
663
664 #[test]
665 fn test_find_overlapping_items() {
666 let mut result = Vec::new();
667
668 find_overlapping_items(
670 &mut SortedRun::from(Vec::<MockFile>::new()),
671 &mut SortedRun::from(Vec::<MockFile>::new()),
672 &mut result,
673 Ranged::overlap_inclusive,
674 );
675 assert_eq!(result, Vec::<MockFile>::new());
676
677 let files1 = build_items(&[(1, 3)]);
678 find_overlapping_items(
679 &mut SortedRun::from(files1.clone()),
680 &mut SortedRun::from(Vec::<MockFile>::new()),
681 &mut result,
682 Ranged::overlap_inclusive,
683 );
684 assert_eq!(result, Vec::<MockFile>::new());
685
686 find_overlapping_items(
687 &mut SortedRun::from(Vec::<MockFile>::new()),
688 &mut SortedRun::from(files1.clone()),
689 &mut result,
690 Ranged::overlap_inclusive,
691 );
692 assert_eq!(result, Vec::<MockFile>::new());
693
694 let files1 = build_items(&[(1, 3), (5, 7)]);
696 let files2 = build_items(&[(10, 12), (15, 20)]);
697 find_overlapping_items(
698 &mut SortedRun::from(files1),
699 &mut SortedRun::from(files2),
700 &mut result,
701 Ranged::overlap_inclusive,
702 );
703 assert_eq!(result, Vec::<MockFile>::new());
704
705 let files1 = build_items(&[(1, 5)]);
707 let files2 = build_items(&[(3, 7)]);
708 find_overlapping_items(
709 &mut SortedRun::from(files1),
710 &mut SortedRun::from(files2),
711 &mut result,
712 Ranged::overlap_inclusive,
713 );
714 assert_eq!(result.len(), 2);
715 assert_eq!(result[0].range(), (1, 5));
716 assert_eq!(result[1].range(), (3, 7));
717
718 let files1 = build_items(&[(1, 5), (8, 12), (15, 20)]);
720 let files2 = build_items(&[(3, 6), (7, 10), (18, 25)]);
721 find_overlapping_items(
722 &mut SortedRun::from(files1),
723 &mut SortedRun::from(files2),
724 &mut result,
725 Ranged::overlap_inclusive,
726 );
727 assert_eq!(result.len(), 6);
728
729 let files1 = build_items(&[(1, 5)]);
731 let files2 = build_items(&[(5, 10)]); find_overlapping_items(
733 &mut SortedRun::from(files1),
734 &mut SortedRun::from(files2),
735 &mut result,
736 Ranged::overlap_inclusive,
737 );
738 assert_eq!(result.len(), 2); let files1 = build_items(&[(1, 10)]);
742 let files2 = build_items(&[(3, 7)]);
743 find_overlapping_items(
744 &mut SortedRun::from(files1),
745 &mut SortedRun::from(files2),
746 &mut result,
747 Ranged::overlap_inclusive,
748 );
749 assert_eq!(result.len(), 2);
750
751 let files1 = build_items(&[(1, 5)]);
753 let files2 = build_items(&[(1, 5)]);
754 find_overlapping_items(
755 &mut SortedRun::from(files1),
756 &mut SortedRun::from(files2),
757 &mut result,
758 Ranged::overlap_inclusive,
759 );
760 assert_eq!(result.len(), 2);
761
762 let files1 = build_items(&[(5, 10), (1, 3)]); let files2 = build_items(&[(2, 7), (8, 12)]); find_overlapping_items(
766 &mut SortedRun::from(files1),
767 &mut SortedRun::from(files2),
768 &mut result,
769 Ranged::overlap_inclusive,
770 );
771 assert_eq!(result.len(), 4); }
773
774 #[test]
775 fn test_file_overlap_time_overlap_pk_disjoint() {
776 let lhs = new_file_handle_with_size_sequence_and_primary_key_range(
777 FileId::random(),
778 0,
779 100,
780 0,
781 1,
782 10,
783 pk_range(b"a", b"f"),
784 );
785 let rhs = new_file_handle_with_size_sequence_and_primary_key_range(
786 FileId::random(),
787 50,
788 150,
789 0,
790 2,
791 10,
792 pk_range(b"x", b"z"),
793 );
794
795 assert!(!files_overlap(&lhs, &rhs, &primary_key_mapper_for_test()));
796 }
797
798 #[test]
799 fn test_find_sorted_runs_collapses_pk_disjoint_files_into_one_run() {
800 let mut files = vec![
801 new_file_handle_with_size_sequence_and_primary_key_range(
802 FileId::random(),
803 0,
804 100,
805 0,
806 1,
807 10,
808 pk_range(b"a", b"f"),
809 ),
810 new_file_handle_with_size_sequence_and_primary_key_range(
811 FileId::random(),
812 50,
813 150,
814 0,
815 2,
816 10,
817 pk_range(b"x", b"z"),
818 ),
819 ];
820
821 let ranges = primary_key_mapper_for_test();
822 let runs = find_sorted_runs(&mut files, |lhs, rhs| files_overlap(lhs, rhs, &ranges));
823
824 assert_eq!(1, runs.len());
825 assert_eq!(2, runs[0].items().len());
826 }
827
828 #[test]
829 fn test_find_sorted_runs_handles_2d_transitivity_break() {
830 let mut files = vec![
831 new_file_handle_with_size_sequence_and_primary_key_range(
832 FileId::random(),
833 0,
834 100,
835 0,
836 1,
837 10,
838 pk_range(b"a", b"f"),
839 ),
840 new_file_handle_with_size_sequence_and_primary_key_range(
841 FileId::random(),
842 50,
843 150,
844 0,
845 2,
846 10,
847 pk_range(b"x", b"z"),
848 ),
849 new_file_handle_with_size_sequence_and_primary_key_range(
850 FileId::random(),
851 50,
852 150,
853 0,
854 3,
855 10,
856 pk_range(b"a", b"f"),
857 ),
858 ];
859
860 let ranges = primary_key_mapper_for_test();
861 let runs = find_sorted_runs(&mut files, |lhs, rhs| files_overlap(lhs, rhs, &ranges));
862
863 assert_eq!(2, runs.len());
864 assert_eq!(2, runs[0].items().len());
865 assert_eq!(1, runs[1].items().len());
866 }
867
868 #[test]
869 fn test_find_overlapping_items_skips_pk_disjoint_pairs() {
870 let mut left = SortedRun::from(vec![
871 new_file_handle_with_size_sequence_and_primary_key_range(
872 FileId::random(),
873 0,
874 100,
875 0,
876 1,
877 10,
878 pk_range(b"a", b"f"),
879 ),
880 ]);
881 let mut right = SortedRun::from(vec![
882 new_file_handle_with_size_sequence_and_primary_key_range(
883 FileId::random(),
884 50,
885 150,
886 0,
887 2,
888 10,
889 pk_range(b"x", b"z"),
890 ),
891 ]);
892 let mut result = Vec::new();
893
894 let ranges = primary_key_mapper_for_test();
895 find_overlapping_items(&mut left, &mut right, &mut result, |lhs, rhs| {
896 files_overlap_inclusive(lhs, rhs, &ranges)
897 });
898
899 assert!(result.is_empty());
900 }
901
902 #[test]
903 fn test_file_touching_time_boundary_with_same_pk_is_not_overlap() {
904 let lhs = new_file_handle_with_size_sequence_and_primary_key_range(
905 FileId::random(),
906 0,
907 100,
908 0,
909 1,
910 10,
911 pk_range(b"a", b"f"),
912 );
913 let rhs = new_file_handle_with_size_sequence_and_primary_key_range(
914 FileId::random(),
915 100,
916 150,
917 0,
918 2,
919 10,
920 pk_range(b"a", b"f"),
921 );
922
923 assert!(!files_overlap(&lhs, &rhs, &primary_key_mapper_for_test()));
924 }
925}