mito2/compaction/
buckets.rs1use common_time::Timestamp;
16use common_time::timestamp::TimeUnit;
17
18use crate::sst::file::FileHandle;
19
20pub(crate) fn infer_time_bucket<'a>(files: impl Iterator<Item = &'a FileHandle>) -> i64 {
24 let mut max_ts = Timestamp::new(i64::MIN, TimeUnit::Second);
25 let mut min_ts = Timestamp::new(i64::MAX, TimeUnit::Second);
26
27 for f in files {
28 let (start, end) = f.time_range();
29 min_ts = min_ts.min(start);
30 max_ts = max_ts.max(end);
31 }
32
33 let min_sec = min_ts.convert_to(TimeUnit::Second).unwrap().value();
35 let max_sec = max_ts.convert_to(TimeUnit::Second).unwrap().value();
36
37 max_sec
38 .checked_sub(min_sec)
39 .map(|span| TIME_BUCKETS.fit_time_bucket(span)) .unwrap_or_else(|| TIME_BUCKETS.max()) }
42
43pub(crate) struct TimeBuckets([i64; 5]);
44
45impl TimeBuckets {
46 fn fit_time_bucket(&self, span_sec: i64) -> i64 {
49 assert!(span_sec >= 0);
50 match self.0.binary_search(&span_sec) {
51 Ok(idx) => self.0[idx],
52 Err(idx) => {
53 if idx < self.0.len() {
54 self.0[idx]
55 } else {
56 self.0.last().copied().unwrap()
57 }
58 }
59 }
60 }
61
62 #[cfg(test)]
63 fn get(&self, idx: usize) -> i64 {
64 self.0[idx]
65 }
66
67 fn max(&self) -> i64 {
68 self.0.last().copied().unwrap()
69 }
70}
71
72pub(crate) const TIME_BUCKETS: TimeBuckets = TimeBuckets([
74 60 * 60, 2 * 60 * 60, 12 * 60 * 60, 24 * 60 * 60, 7 * 24 * 60 * 60, ]);
80
81#[cfg(test)]
82mod tests {
83 use store_api::storage::FileId;
84
85 use super::*;
86 use crate::compaction::test_util::new_file_handle;
87
88 #[test]
89 fn test_time_bucket() {
90 assert_eq!(TIME_BUCKETS.get(0), TIME_BUCKETS.fit_time_bucket(1));
91 assert_eq!(TIME_BUCKETS.get(0), TIME_BUCKETS.fit_time_bucket(60 * 60));
92 assert_eq!(
93 TIME_BUCKETS.get(1),
94 TIME_BUCKETS.fit_time_bucket(60 * 60 + 1)
95 );
96
97 assert_eq!(
98 TIME_BUCKETS.get(2),
99 TIME_BUCKETS.fit_time_bucket(TIME_BUCKETS.get(2) - 1)
100 );
101 assert_eq!(
102 TIME_BUCKETS.get(2),
103 TIME_BUCKETS.fit_time_bucket(TIME_BUCKETS.get(2))
104 );
105 assert_eq!(
106 TIME_BUCKETS.get(3),
107 TIME_BUCKETS.fit_time_bucket(TIME_BUCKETS.get(3) - 1)
108 );
109 assert_eq!(TIME_BUCKETS.get(4), TIME_BUCKETS.fit_time_bucket(i64::MAX));
110 }
111
112 #[test]
113 fn test_infer_time_buckets() {
114 assert_eq!(
115 TIME_BUCKETS.get(0),
116 infer_time_bucket(
117 [
118 new_file_handle(FileId::random(), 0, TIME_BUCKETS.get(0) * 1000 - 1, 0),
119 new_file_handle(FileId::random(), 1, 10_000, 0)
120 ]
121 .iter()
122 )
123 );
124 }
125}