use std::collections::HashSet; use crate::{ domain::{Entry, EntryKind, Manifest}, storage::StateStore, }; impl StateStore { /// Returns every blob hash that must be protected from garbage collection. /// /// A blob is live when it is referenced by a current manifest, a retained /// manifest-history revision (so `appa folder restore` still works), or a /// pending materialization (an interrupted write that will be replayed). pub fn live_blob_hashes(&self) -> anyhow::Result> { let mut live = HashSet::new(); for folder in self.folders()? { let manifest = self.load_manifest(folder.id)?; collect_manifest_blob_hashes(&manifest, &mut live); for revision in self.manifest_history(folder.id)? { collect_manifest_blob_hashes(&revision.manifest, &mut live); } if let Some(pending) = self.pending_materialization(folder.id)? { for entry in &pending.entries { collect_entry_blob_hashes(entry, &mut live); } collect_manifest_blob_hashes(&pending.resulting_manifest, &mut live); } } Ok(live) } /// Like [`live_blob_hashes`](Self::live_blob_hashes) but driven from a fresh /// connection opened against `database_path`. Used by the garbage collector /// callback, which runs on the blob store's own runtime and cannot borrow the /// daemon's `StateStore`. pub(crate) fn live_blob_hashes_for_gc( database_path: &std::path::Path, ) -> anyhow::Result> { StateStore::open_for_gc(database_path)?.live_blob_hashes() } } fn collect_manifest_blob_hashes(manifest: &Manifest, live: &mut HashSet) { for entry in manifest.entries.values() { collect_entry_blob_hashes(entry, live); } } fn collect_entry_blob_hashes(entry: &Entry, live: &mut HashSet) { if entry.kind == EntryKind::File && let Some(blob_hash) = &entry.blob_hash { live.insert(blob_hash.clone()); } }