Line data Source code
1 : //! An LSM tree consists of multiple levels, each exponentially larger than the
2 : //! previous level. And each level consists of multiple "tiers". With tiered
3 : //! compaction, a level is compacted when it has accumulated more than N tiers,
4 : //! forming one tier on the next level.
5 : //!
6 : //! In the pageserver, we don't explicitly track the levels and tiers. Instead,
7 : //! we identify them by looking at the shapes of the layers. It's an easy task
8 : //! for a human, but it's not straightforward to come up with the exact
9 : //! rules. Especially if there are cases like interrupted, half-finished
10 : //! compactions, or highly skewed data distributions that have let us "skip"
11 : //! some levels. It's not critical to classify all cases correctly; at worst we
12 : //! delay some compaction work, and suffer from more read amplification, or we
13 : //! perform some unnecessary compaction work.
14 : //!
15 : //! `identify_level` performs that shape-matching.
16 : //!
17 : //! It returns a Level struct, which has `depth()` function to count the number
18 : //! of "tiers" in the level. The tier count is the max depth of stacked layers
19 : //! within the level. That's a good measure, because the point of compacting is
20 : //! to reduce read amplification, and the depth is what determines that.
21 : //!
22 : //! One interesting effect of this is that if we generate very small delta
23 : //! layers at L0, e.g. because the L0 layers are flushed by timeout rather than
24 : //! because they reach the target size, the L0 compaction will combine them to
25 : //! one larger file. But if the combined file is still smaller than the target
26 : //! file size, the file will still be considered to be part of L0 at the next
27 : //! iteration.
28 :
29 : use anyhow::bail;
30 : use std::collections::BTreeSet;
31 : use std::ops::Range;
32 : use utils::lsn::Lsn;
33 :
34 : use crate::interface::*;
35 :
36 : use tracing::{info, trace};
37 :
38 : pub struct Level<L> {
39 : pub lsn_range: Range<Lsn>,
40 : pub layers: Vec<L>,
41 : }
42 :
43 : /// Identify an LSN > `end_lsn` that partitions the LSN space, so that there are
44 : /// no layers that cross the boundary LSN.
45 : ///
46 : /// A further restriction is that all layers in the returned partition cover at
47 : /// most 'lsn_max_size' LSN bytes.
48 2022 : pub async fn identify_level<K, L>(
49 2022 : all_layers: Vec<L>,
50 2022 : end_lsn: Lsn,
51 2022 : lsn_max_size: u64,
52 2022 : ) -> anyhow::Result<Option<Level<L>>>
53 2022 : where
54 2022 : K: CompactionKey,
55 2022 : L: CompactionLayer<K> + Clone,
56 2022 : {
57 2022 : // filter out layers that are above the `end_lsn`, they are completely irrelevant.
58 2022 : let mut layers = Vec::new();
59 14078 : for l in all_layers {
60 12058 : if l.lsn_range().start < end_lsn && l.lsn_range().end > end_lsn {
61 : // shouldn't happen. Indicates that the caller passed a bogus
62 : // end_lsn.
63 2 : bail!("identify_level() called with end_lsn that does not partition the LSN space: end_lsn {} intersects with layer {}", end_lsn, l.short_id());
64 12056 : }
65 12056 : // include image layers sitting exacty at `end_lsn`.
66 12056 : let is_image = !l.is_delta();
67 12056 : if (is_image && l.lsn_range().start > end_lsn)
68 12056 : || (!is_image && l.lsn_range().start >= end_lsn)
69 : {
70 10 : continue;
71 12046 : }
72 12046 : layers.push(l);
73 : }
74 : // All the remaining layers either belong to this level, or are below it.
75 2020 : info!(
76 0 : "identify level at {}, size {}, num layers below: {}",
77 0 : end_lsn,
78 0 : lsn_max_size,
79 0 : layers.len()
80 : );
81 2020 : if layers.is_empty() {
82 180 : return Ok(None);
83 1840 : }
84 1840 :
85 1840 : // Walk the ranges in LSN order.
86 1840 : //
87 1840 : // ----- end_lsn
88 1840 : // |
89 1840 : // |
90 1840 : // v
91 1840 : //
92 20524 : layers.sort_by_key(|l| l.lsn_range().end);
93 1840 : let mut candidate_start_lsn = end_lsn;
94 1840 : let mut candidate_layers: Vec<L> = Vec::new();
95 1840 : let mut current_best_start_lsn = end_lsn;
96 1840 : let mut current_best_layers: Vec<L> = Vec::new();
97 1840 : let mut iter = layers.into_iter();
98 : loop {
99 4736 : let Some(l) = iter.next_back() else {
100 : // Reached end. Accept the last candidate
101 564 : current_best_start_lsn = candidate_start_lsn;
102 564 : current_best_layers.extend_from_slice(&std::mem::take(&mut candidate_layers));
103 564 : break;
104 : };
105 4172 : trace!(
106 0 : "inspecting {} for candidate {}, current best {}",
107 0 : l.short_id(),
108 : candidate_start_lsn,
109 : current_best_start_lsn
110 : );
111 :
112 4172 : let r = l.lsn_range();
113 4172 :
114 4172 : // Image layers don't restrict our choice of cutoff LSN
115 4172 : if l.is_delta() {
116 : // Is this candidate workable? In other words, are there any
117 : // delta layers that span across this LSN
118 : //
119 : // Valid: Not valid:
120 : // + +
121 : // | | +
122 : // + <- candidate + | <- candidate
123 : // + +
124 : // |
125 : // +
126 4170 : if r.end <= candidate_start_lsn {
127 4140 : // Hooray, there are no crossing LSNs. And we have visited
128 4140 : // through all the layers within candidate..end_lsn. The
129 4140 : // current candidate can be accepted.
130 4140 : current_best_start_lsn = r.end;
131 4140 : current_best_layers.extend_from_slice(&std::mem::take(&mut candidate_layers));
132 4140 : candidate_start_lsn = r.start;
133 4140 : }
134 :
135 : // Is it small enough to be considered part of this level?
136 4170 : if r.end.0 - r.start.0 > lsn_max_size {
137 : // Too large, this layer belongs to next level. Stop.
138 1276 : trace!(
139 0 : "too large {}, size {} vs {}",
140 0 : l.short_id(),
141 0 : r.end.0 - r.start.0,
142 : lsn_max_size
143 : );
144 1276 : break;
145 2894 : }
146 2894 :
147 2894 : // If this crosses the candidate lsn, push it down.
148 2894 : if r.start < candidate_start_lsn {
149 4 : trace!(
150 0 : "layer {} prevents from stopping at {}",
151 0 : l.short_id(),
152 : candidate_start_lsn
153 : );
154 4 : candidate_start_lsn = r.start;
155 2890 : }
156 2 : }
157 :
158 : // Include this layer in our candidate
159 2896 : candidate_layers.push(l);
160 : }
161 :
162 1840 : Ok(if current_best_start_lsn == end_lsn {
163 : // empty level
164 362 : None
165 : } else {
166 1478 : Some(Level {
167 1478 : lsn_range: current_best_start_lsn..end_lsn,
168 1478 : layers: current_best_layers,
169 1478 : })
170 : })
171 2022 : }
172 :
173 : impl<L> Level<L> {
174 : /// Count the number of deltas stacked on each other.
175 1478 : pub fn depth<K>(&self) -> u64
176 1478 : where
177 1478 : K: CompactionKey,
178 1478 : L: CompactionLayer<K>,
179 1478 : {
180 1478 : struct Event<K> {
181 1478 : key: K,
182 1478 : layer_idx: usize,
183 1478 : start: bool,
184 1478 : }
185 1478 : let mut events: Vec<Event<K>> = Vec::new();
186 2890 : for (idx, l) in self.layers.iter().enumerate() {
187 2890 : let key_range = l.key_range();
188 2890 : if key_range.end == key_range.start.next() && l.is_delta() {
189 : // Ignore single-key delta layers as they can be stacked on top of each other
190 : // as that is the only way to cut further.
191 24 : continue;
192 2866 : }
193 2866 : events.push(Event {
194 2866 : key: l.key_range().start,
195 2866 : layer_idx: idx,
196 2866 : start: true,
197 2866 : });
198 2866 : events.push(Event {
199 2866 : key: l.key_range().end,
200 2866 : layer_idx: idx,
201 2866 : start: false,
202 2866 : });
203 : }
204 12936 : events.sort_by_key(|e| (e.key, e.start));
205 1478 :
206 1478 : // Sweep the key space left to right. Stop at each distinct key, and
207 1478 : // count the number of deltas on top of the highest image at that key.
208 1478 : //
209 1478 : // This is a little inefficient, as we walk through the active_set on
210 1478 : // every key. We could increment/decrement a counter on each step
211 1478 : // instead, but that'd require a bit more complex bookkeeping.
212 1478 : let mut active_set: BTreeSet<(Lsn, bool, usize)> = BTreeSet::new();
213 1478 : let mut max_depth = 0;
214 1478 : let mut events_iter = events.iter().peekable();
215 7210 : while let Some(e) = events_iter.next() {
216 5732 : let l = &self.layers[e.layer_idx];
217 5732 : let is_image = !l.is_delta();
218 5732 :
219 5732 : // update the active set
220 5732 : if e.start {
221 2866 : active_set.insert((l.lsn_range().end, is_image, e.layer_idx));
222 2866 : } else {
223 2866 : active_set.remove(&(l.lsn_range().end, is_image, e.layer_idx));
224 2866 : }
225 :
226 : // recalculate depth if this was the last event at this point
227 5732 : let more_events_at_this_key = events_iter
228 5732 : .peek()
229 5732 : .map_or(false, |next_e| next_e.key == e.key);
230 5732 : if !more_events_at_this_key {
231 2977 : let mut active_depth = 0;
232 2977 : for (_end_lsn, is_image, _idx) in active_set.iter().rev() {
233 2875 : if *is_image {
234 4 : break;
235 2871 : }
236 2871 : active_depth += 1;
237 : }
238 2977 : if active_depth > max_depth {
239 1481 : max_depth = active_depth;
240 1496 : }
241 2755 : }
242 : }
243 1478 : debug_assert_eq!(active_set, BTreeSet::new());
244 1478 : max_depth
245 1478 : }
246 : }
247 :
248 : #[cfg(test)]
249 : mod tests {
250 : use super::*;
251 : use crate::simulator::{Key, MockDeltaLayer, MockImageLayer, MockLayer};
252 : use std::sync::{Arc, Mutex};
253 :
254 40 : fn delta(key_range: Range<Key>, lsn_range: Range<Lsn>) -> MockLayer {
255 40 : MockLayer::Delta(Arc::new(MockDeltaLayer {
256 40 : key_range,
257 40 : lsn_range,
258 40 : // identify_level() doesn't pay attention to the rest of the fields
259 40 : file_size: 0,
260 40 : deleted: Mutex::new(false),
261 40 : records: vec![],
262 40 : }))
263 40 : }
264 :
265 2 : fn image(key_range: Range<Key>, lsn: Lsn) -> MockLayer {
266 2 : MockLayer::Image(Arc::new(MockImageLayer {
267 2 : key_range,
268 2 : lsn_range: lsn..(lsn + 1),
269 2 : // identify_level() doesn't pay attention to the rest of the fields
270 2 : file_size: 0,
271 2 : deleted: Mutex::new(false),
272 2 : }))
273 2 : }
274 :
275 : #[tokio::test]
276 2 : async fn test_identify_level() -> anyhow::Result<()> {
277 2 : let layers = vec![
278 2 : delta(Key::MIN..Key::MAX, Lsn(0x8000)..Lsn(0x9000)),
279 2 : delta(Key::MIN..Key::MAX, Lsn(0x5000)..Lsn(0x7000)),
280 2 : delta(Key::MIN..Key::MAX, Lsn(0x4000)..Lsn(0x5000)),
281 2 : delta(Key::MIN..Key::MAX, Lsn(0x3000)..Lsn(0x4000)),
282 2 : delta(Key::MIN..Key::MAX, Lsn(0x2000)..Lsn(0x3000)),
283 2 : delta(Key::MIN..Key::MAX, Lsn(0x1000)..Lsn(0x2000)),
284 2 : ];
285 2 :
286 2 : // All layers fit in the max file size
287 2 : let level = identify_level(layers.clone(), Lsn(0x10000), 0x2000)
288 2 : .await?
289 2 : .unwrap();
290 2 : assert_eq!(level.depth(), 6);
291 2 :
292 2 : // Same LSN with smaller max file size. The second layer from the top is larger
293 2 : // and belongs to next level.
294 2 : let level = identify_level(layers.clone(), Lsn(0x10000), 0x1000)
295 2 : .await?
296 2 : .unwrap();
297 2 : assert_eq!(level.depth(), 1);
298 2 :
299 2 : // Call with a smaller LSN
300 2 : let level = identify_level(layers.clone(), Lsn(0x3000), 0x1000)
301 2 : .await?
302 2 : .unwrap();
303 2 : assert_eq!(level.depth(), 2);
304 2 :
305 2 : // Call with an LSN that doesn't partition the space
306 2 : let result = identify_level(layers, Lsn(0x6000), 0x1000).await;
307 2 : assert!(result.is_err());
308 2 : Ok(())
309 2 : }
310 :
311 : #[tokio::test]
312 2 : async fn test_overlapping_lsn_ranges() -> anyhow::Result<()> {
313 2 : // The files LSN ranges overlap, so even though there are more files that
314 2 : // fit under the file size, they are not included in the level because they
315 2 : // overlap so that we'd need to include the oldest file, too, which is
316 2 : // larger
317 2 : let layers = vec![
318 2 : delta(Key::MIN..Key::MAX, Lsn(0x4000)..Lsn(0x5000)),
319 2 : delta(Key::MIN..Key::MAX, Lsn(0x3000)..Lsn(0x4000)), // overlap
320 2 : delta(Key::MIN..Key::MAX, Lsn(0x2500)..Lsn(0x3500)), // overlap
321 2 : delta(Key::MIN..Key::MAX, Lsn(0x2000)..Lsn(0x3000)), // overlap
322 2 : delta(Key::MIN..Key::MAX, Lsn(0x1000)..Lsn(0x2500)), // larger
323 2 : ];
324 2 :
325 2 : let level = identify_level(layers.clone(), Lsn(0x10000), 0x1000)
326 2 : .await?
327 2 : .unwrap();
328 2 : assert_eq!(level.depth(), 1);
329 2 :
330 2 : Ok(())
331 2 : }
332 :
333 : #[tokio::test]
334 2 : async fn test_depth_nonoverlapping() -> anyhow::Result<()> {
335 2 : // The key ranges don't overlap, so depth is only 1.
336 2 : let layers = vec![
337 2 : delta(4000..5000, Lsn(0x6000)..Lsn(0x7000)),
338 2 : delta(3000..4000, Lsn(0x7000)..Lsn(0x8000)),
339 2 : delta(1000..2000, Lsn(0x8000)..Lsn(0x9000)),
340 2 : ];
341 2 :
342 2 : let level = identify_level(layers.clone(), Lsn(0x10000), 0x2000)
343 2 : .await?
344 2 : .unwrap();
345 2 : assert_eq!(level.layers.len(), 3);
346 2 : assert_eq!(level.depth(), 1);
347 2 :
348 2 : // Staggered. The 1st and 3rd layer don't overlap with each other.
349 2 : let layers = vec![
350 2 : delta(1000..2000, Lsn(0x8000)..Lsn(0x9000)),
351 2 : delta(1500..2500, Lsn(0x7000)..Lsn(0x8000)),
352 2 : delta(2000..3000, Lsn(0x6000)..Lsn(0x7000)),
353 2 : ];
354 2 :
355 2 : let level = identify_level(layers.clone(), Lsn(0x10000), 0x2000)
356 2 : .await?
357 2 : .unwrap();
358 2 : assert_eq!(level.layers.len(), 3);
359 2 : assert_eq!(level.depth(), 2);
360 2 : Ok(())
361 2 : }
362 :
363 : #[tokio::test]
364 2 : async fn test_depth_images() -> anyhow::Result<()> {
365 2 : let layers: Vec<MockLayer> = vec![
366 2 : delta(1000..2000, Lsn(0x8000)..Lsn(0x9000)),
367 2 : delta(1500..2500, Lsn(0x7000)..Lsn(0x8000)),
368 2 : delta(2000..3000, Lsn(0x6000)..Lsn(0x7000)),
369 2 : // This covers the same key range as the 2nd delta layer. The depth
370 2 : // in that key range is therefore 0.
371 2 : image(1500..2500, Lsn(0x9000)),
372 2 : ];
373 2 :
374 2 : let level = identify_level(layers.clone(), Lsn(0x10000), 0x2000)
375 2 : .await?
376 2 : .unwrap();
377 2 : assert_eq!(level.layers.len(), 4);
378 2 : assert_eq!(level.depth(), 1);
379 2 : Ok(())
380 2 : }
381 : }
|