Skip to content

Commit 0033895

Browse files
committed
[ENH] Make snapshot be recursive.
Before this PR, the status quo was to stop at a depth of 2. This allows for a small-ish log. With this change, the snapshots can be of arbitrary depth, although in practice they will be log(n)/log(1846) levels deep.
1 parent d50cd02 commit 0033895

File tree

1 file changed

+55
-54
lines changed

1 file changed

+55
-54
lines changed

rust/wal3/src/manifest.rs

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -267,69 +267,70 @@ impl Manifest {
267267
writer: &str,
268268
) -> Option<Snapshot> {
269269
let writer = writer.to_string();
270-
let can_snapshot_snapshots = self.snapshots.iter().filter(|s| s.depth < 2).count()
271-
>= snapshot_options.snapshot_rollover_threshold;
272-
let can_snapshot_fragments =
273-
self.fragments.len() >= snapshot_options.fragment_rollover_threshold;
274-
if can_snapshot_snapshots || can_snapshot_fragments {
270+
let mut snapshot_depth = self.snapshots.iter().map(|s| s.depth).max().unwrap_or(0);
271+
while snapshot_depth > 0 {
275272
// NOTE(rescrv): We _either_ compact a snapshot of snapshots or a snapshot of log
276273
// fragments. We don't do both as interior snapshot nodes only refer to objects of the
277274
// same type. Manifests are the only objects to refer to both fragments and snapshots.
278275
let mut snapshots = vec![];
279-
let mut fragments = vec![];
280276
let mut setsum = Setsum::default();
281-
let depth = if can_snapshot_snapshots {
282-
for snapshot in self.snapshots.iter() {
283-
if snapshot.depth < 2
284-
&& snapshots.len() < snapshot_options.snapshot_rollover_threshold
285-
{
286-
setsum += snapshot.setsum;
287-
snapshots.push(snapshot.clone());
288-
}
277+
for snapshot in self.snapshots.iter().filter(|s| s.depth == snapshot_depth) {
278+
if snapshots.len() < snapshot_options.snapshot_rollover_threshold {
279+
setsum += snapshot.setsum;
280+
snapshots.push(snapshot.clone());
289281
}
290-
2
291-
} else if can_snapshot_fragments {
292-
for fragment in self.fragments.iter() {
293-
// NOTE(rescrv): When taking a snapshot, it's important that we keep around
294-
// one fragment so that the max seq no is always calculable.
295-
//
296-
// Otherwise, a low-traffic log could be compacted into a state where all of
297-
// its fragments have been compacted and therefore the implicit fragment seq no
298-
// for each fragment is zero. This wedges the manifest manager.
299-
//
300-
// The fix is to keep around the last fragment.
301-
if fragments.len() < snapshot_options.fragment_rollover_threshold
302-
&& self
303-
.fragments
304-
.iter()
305-
.map(|f| f.seq_no)
306-
.max()
307-
.unwrap_or(FragmentSeqNo(0))
308-
!= fragment.seq_no
309-
{
310-
setsum += fragment.setsum;
311-
fragments.push(fragment.clone());
312-
}
282+
}
283+
if snapshots.len() >= snapshot_options.snapshot_rollover_threshold {
284+
let path = unprefixed_snapshot_path(setsum);
285+
return Some(Snapshot {
286+
path,
287+
depth: snapshot_depth + 1,
288+
setsum,
289+
writer,
290+
snapshots,
291+
fragments: vec![],
292+
});
293+
}
294+
snapshot_depth -= 1;
295+
}
296+
if self.fragments.len() >= snapshot_options.fragment_rollover_threshold {
297+
let mut setsum = Setsum::default();
298+
let mut fragments = vec![];
299+
for fragment in self.fragments.iter() {
300+
// NOTE(rescrv): When taking a snapshot, it's important that we keep around
301+
// one fragment so that the max seq no is always calculable.
302+
//
303+
// Otherwise, a low-traffic log could be compacted into a state where all of
304+
// its fragments have been compacted and therefore the implicit fragment seq no
305+
// for each fragment is zero. This wedges the manifest manager.
306+
//
307+
// The fix is to keep around the last fragment.
308+
if fragments.len() < snapshot_options.fragment_rollover_threshold
309+
&& self
310+
.fragments
311+
.iter()
312+
.map(|f| f.seq_no)
313+
.max()
314+
.unwrap_or(FragmentSeqNo(0))
315+
!= fragment.seq_no
316+
{
317+
setsum += fragment.setsum;
318+
fragments.push(fragment.clone());
313319
}
314-
1
315-
} else {
316-
unreachable!("I checked A || B above, then checked A and B separately; should not reach here.");
317-
};
318-
if snapshots.is_empty() && fragments.is_empty() {
319-
return None;
320320
}
321-
let path = unprefixed_snapshot_path(setsum);
322-
Some(Snapshot {
323-
path,
324-
depth,
325-
setsum,
326-
writer,
327-
snapshots,
328-
fragments,
329-
})
330-
} else {
331-
None
321+
if fragments.len() >= snapshot_options.fragment_rollover_threshold {
322+
let path = unprefixed_snapshot_path(setsum);
323+
return Some(Snapshot {
324+
path,
325+
depth: 1,
326+
setsum,
327+
writer,
328+
snapshots: vec![],
329+
fragments,
330+
});
331+
}
332332
}
333+
None
333334
}
334335

335336
/// Given a snapshot, apply it to the manifest. This modifies the manifest to refer to the

0 commit comments

Comments
 (0)