diff --git a/crates/paths/src/server.rs b/crates/paths/src/server.rs index 5da628544..6aa7bec13 100644 --- a/crates/paths/src/server.rs +++ b/crates/paths/src/server.rs @@ -206,6 +206,11 @@ path_type! { SnapshotDirPath: dir } +path_type! { + /// An archived snapshot directory. `{data-dir}/replica/$replica_id/snapshots/$tx_offset.archived_snapshot` + ArchivedSnapshotDirPath: dir +} + impl SnapshotDirPath { pub fn snapshot_file(&self, tx_offset: u64) -> SnapshotFilePath { let file_name = format!("{tx_offset:0>20}.snapshot_bsatn"); @@ -221,6 +226,12 @@ impl SnapshotDirPath { fs::rename(self, invalid_path) } + pub fn rename_as_archived(&self) -> io::Result { + let path = self.0.with_extension("archived_snapshot"); + fs::rename(self, &path)?; + Ok(ArchivedSnapshotDirPath(path)) + } + pub fn tx_offset(&self) -> Option { self.0 .file_stem() diff --git a/crates/snapshot/src/lib.rs b/crates/snapshot/src/lib.rs index 3fa32cb33..5f0c8eabf 100644 --- a/crates/snapshot/src/lib.rs +++ b/crates/snapshot/src/lib.rs @@ -30,7 +30,7 @@ use spacetimedb_fs_utils::{ lockfile::{Lockfile, LockfileError}, }; use spacetimedb_lib::Identity; -use spacetimedb_paths::server::{SnapshotDirPath, SnapshotFilePath, SnapshotsPath}; +use spacetimedb_paths::server::{ArchivedSnapshotDirPath, SnapshotDirPath, SnapshotFilePath, SnapshotsPath}; use spacetimedb_paths::FromPathUnchecked; use spacetimedb_primitives::TableId; use spacetimedb_sats::{bsatn, de::Deserialize, ser::Serialize}; @@ -40,6 +40,7 @@ use spacetimedb_table::{ page_pool::PagePool, table::Table, }; +use std::fs; use std::{ collections::BTreeMap, collections::HashMap, @@ -145,6 +146,9 @@ pub const SNAPSHOT_FILE_EXT: &str = "snapshot_bsatn"; /// File extension of snapshots which have been marked invalid by [`SnapshotRepository::invalidate_newer_snapshots`]. pub const INVALID_SNAPSHOT_DIR_EXT: &str = "invalid_snapshot"; +/// File extension of snapshots which have been archived +pub const ARCHIVED_SNAPSHOT_EXT: &str = "archived_snapshot"; + #[derive(Clone, Serialize, Deserialize)] /// The hash and refcount of a single blob in the blob store. struct BlobEntry { @@ -890,6 +894,27 @@ impl SnapshotRepository { })) } + /// Return an interator of [`ArchivedSnapshotDirPath`] for all the archived snapshot directories on disk + pub fn all_archived_snapshots(&self) -> Result, SnapshotError> { + Ok(self + .root + // Item = Result + .read_dir()? + // Item = DirEntry + .filter_map(Result::ok) + // Item = PathBuf + .map(|dirent| dirent.path()) + // Ignore entries not shaped like snapshot directories. + .filter(|path| path.extension() == Some(OsStr::new(ARCHIVED_SNAPSHOT_EXT))) + // Item = ArchivedSnapshotDirPath + .map(ArchivedSnapshotDirPath::from_path_unchecked)) + } + + /// Delete an archived snapshot from disk + pub fn remove_archived_snapshot(path: &ArchivedSnapshotDirPath) -> Result<(), SnapshotError> { + fs::remove_dir_all(path).map_err(SnapshotError::Io) + } + /// Return the `TxOffset` of the highest-offset complete snapshot in the repository. /// /// Does not verify that the snapshot of the returned `TxOffset` is valid and uncorrupted,