Skip to main content

powdb_backup/
full.rs

1use crate::manifest::{BackupManifest, FileEntry};
2use powdb_storage::catalog::Catalog;
3use std::io;
4use std::path::Path;
5use std::time::{SystemTime, UNIX_EPOCH};
6
7/// Take a consistent full snapshot of `catalog`'s data dir into `dest`.
8///
9/// Consistency model: `checkpoint()` flushes every dirty heap page + index
10/// and truncates the WAL, producing a clean-shutdown image. We then copy the
11/// durable files. The brief write-quiesce is the duration of the checkpoint,
12/// held by the caller's `&mut` borrow.
13pub fn full_backup(catalog: &mut Catalog, dest: &Path) -> io::Result<BackupManifest> {
14    catalog.checkpoint()?;
15    let source_lsn = catalog.max_lsn();
16    let src = catalog.data_dir().to_path_buf();
17    std::fs::create_dir_all(dest)?;
18
19    let mut files = Vec::new();
20    for entry in std::fs::read_dir(&src)? {
21        let entry = entry?;
22        let name = entry.file_name().to_string_lossy().to_string();
23        // Durable state only: catalog + heaps + indexes. wal.log was just
24        // truncated; manifest.json is ours.
25        let is_durable = name == "catalog.bin" || name.ends_with(".heap") || name.ends_with(".idx");
26        if !is_durable {
27            continue;
28        }
29        let bytes = std::fs::read(entry.path())?;
30        let hash = blake3::hash(&bytes).to_hex().to_string();
31        std::fs::write(dest.join(&name), &bytes)?;
32        files.push(FileEntry {
33            name,
34            len: bytes.len() as u64,
35            blake3_hex: hash,
36        });
37    }
38    files.sort_by(|a, b| a.name.cmp(&b.name));
39
40    let manifest = BackupManifest {
41        format_version: BackupManifest::FORMAT_VERSION,
42        created_unix_secs: SystemTime::now()
43            .duration_since(UNIX_EPOCH)
44            .map(|d| d.as_secs())
45            .unwrap_or(0),
46        source_lsn,
47        files,
48    };
49    manifest.write(dest)?;
50    Ok(manifest)
51}