persistent_kv/snapshot_set/
admin.rs1use std::fs;
2
3use super::{FileSnapshotSet, SnapshotType};
4
5#[allow(dead_code)]
7pub trait SnapshotSetAdmin: Send {
8 fn prune_backup_snapshots(&mut self, max_backups_keep: usize) -> Result<(), std::io::Error>;
12
13 fn prune_not_completed_snapshots(&mut self) -> Result<(), std::io::Error>;
17}
18
19impl SnapshotSetAdmin for FileSnapshotSet {
20 fn prune_backup_snapshots(&mut self, max_backups_keep: usize) -> Result<(), std::io::Error> {
21 let mut full_backup_snapshots = self
22 .snapshots
23 .iter()
24 .filter(|snapshot| snapshot.snapshot_type == SnapshotType::FullCompleted)
25 .cloned()
26 .collect::<Vec<_>>();
27
28 full_backup_snapshots.sort_by_key(|snapshot| snapshot.ordinal);
29 let full_backup_snapshots = &full_backup_snapshots[..full_backup_snapshots.len() - 1];
31 if max_backups_keep >= full_backup_snapshots.len() {
32 return Ok(());
33 }
34
35 let num_snapshots_to_delete = full_backup_snapshots.len() - max_backups_keep;
36 for snapshot in full_backup_snapshots.iter().take(num_snapshots_to_delete) {
37 println!("Pruning backup snapshot: {:?}", snapshot.shard_paths);
38 for path in snapshot.shard_paths.iter() {
39 fs::remove_file(path)?
40 }
41 self.snapshots.retain(|s| s.ordinal != snapshot.ordinal);
42 }
43 Ok(())
44 }
45
46 fn prune_not_completed_snapshots(&mut self) -> Result<(), std::io::Error> {
47 let not_completed_snapshots = self
48 .snapshots
49 .iter()
50 .filter(|snapshot| snapshot.snapshot_type == SnapshotType::Pending)
51 .cloned()
52 .collect::<Vec<_>>();
53
54 for snapshot in not_completed_snapshots.iter() {
55 println!(
56 "Pruning not completed snapshots: {:?}",
57 snapshot.shard_paths
58 );
59 for path in snapshot.shard_paths.iter() {
60 fs::remove_file(path)?
61 }
62 self.snapshots.retain(|s| s.ordinal != snapshot.ordinal);
63 }
64 Ok(())
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use std::{
71 fs::File,
72 path::{Path, PathBuf},
73 };
74 use tempfile::TempDir;
75
76 use super::*;
77 use crate::snapshot_set::SnapshotOrdinal;
78
79 fn create_temp_dir() -> TempDir {
80 TempDir::new().unwrap()
81 }
82
83 fn create_snapshot_file(folder: &Path, name: &str) -> PathBuf {
84 let path = folder.join(name);
85 File::create(&path).unwrap();
86 path
87 }
88
89 #[test]
90 fn prunes_backup_snapshots() {
91 let tmp_dir = create_temp_dir();
92 create_snapshot_file(tmp_dir.path(), "snapshot_1_0-of-1_full.bin"); create_snapshot_file(tmp_dir.path(), "snapshot_2_0-of-1_full.bin"); create_snapshot_file(tmp_dir.path(), "snapshot_3_0-of-1_diff.bin");
95 create_snapshot_file(tmp_dir.path(), "snapshot_4_0-of-1_full.bin"); create_snapshot_file(tmp_dir.path(), "snapshot_5_0-of-1_full.bin"); let mut snapshot_set = FileSnapshotSet::new(tmp_dir.path()).unwrap();
99 assert_eq!(snapshot_set.snapshots.len(), 5);
100
101 snapshot_set.prune_backup_snapshots(3).unwrap();
102
103 assert_eq!(snapshot_set.snapshots.len(), 5);
104
105 snapshot_set.prune_backup_snapshots(1).unwrap();
106
107 assert_eq!(snapshot_set.snapshots.len(), 3);
108 assert_eq!(snapshot_set.snapshots[0].ordinal, SnapshotOrdinal(3));
109 assert_eq!(snapshot_set.snapshots[1].ordinal, SnapshotOrdinal(4));
110 assert_eq!(snapshot_set.snapshots[2].ordinal, SnapshotOrdinal(5));
111
112 drop(snapshot_set);
114 let mut snapshot_set = FileSnapshotSet::new(tmp_dir.path()).unwrap();
115 snapshot_set.prune_backup_snapshots(0).unwrap();
116
117 assert_eq!(snapshot_set.snapshots.len(), 2);
118 assert_eq!(snapshot_set.snapshots[0].ordinal, SnapshotOrdinal(3));
119 assert_eq!(snapshot_set.snapshots[1].ordinal, SnapshotOrdinal(5));
120 }
121
122 #[test]
123 fn prunes_not_completed_snapshots() {
124 let tmp_dir = create_temp_dir();
125 create_snapshot_file(tmp_dir.path(), "snapshot_3_0-of-1_pending.bin");
126 create_snapshot_file(tmp_dir.path(), "snapshot_1_0-of-1_pending.bin"); create_snapshot_file(tmp_dir.path(), "snapshot_2_0-of-1_full.bin"); let mut snapshot_set = FileSnapshotSet::new(tmp_dir.path()).unwrap();
130 assert_eq!(snapshot_set.snapshots.len(), 3);
131
132 snapshot_set.prune_not_completed_snapshots().unwrap();
133
134 assert_eq!(snapshot_set.snapshots.len(), 1);
135 assert_eq!(snapshot_set.snapshots[0].ordinal, SnapshotOrdinal(2));
136
137 drop(snapshot_set);
139 let snapshot_set = FileSnapshotSet::new(tmp_dir.path()).unwrap();
140
141 assert_eq!(snapshot_set.snapshots.len(), 1);
142 assert_eq!(snapshot_set.snapshots[0].ordinal, SnapshotOrdinal(2));
143 }
144}