mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
Purgatory and rejected-event snapshots were consumed on successful startup and only rewritten during graceful shutdown. A crash or power loss after restore therefore discarded all durable recovery history, allowing unresolved work and rejection backoff to restart from an empty state. Retain restored checkpoints, replace them atomically every 60 seconds, and write a final snapshot only after background mutation tasks have stopped. Snapshot replacement syncs a complete temporary file, renames it, and syncs the parent directory so interruption leaves either the old or new complete state. The checkpoint interval bounds mutation loss without adding synchronous disk I/O to event handling. Addressable purgatory restoration is idempotent and timestamp adjustment continues to preserve remaining lifetimes. Database durability, git repository state, and general LMDB backup policy are deliberately excluded. Validation: cargo test --lib passed all 668 tests before commit. Purgatory and rejected-index tests verify the checkpoint survives restore and can initialize a second fresh instance; the atomic writer test verifies complete replacement. Nix validation follows after rebasing onto the passive-ownership proposal.
35 lines
1.0 KiB
Rust
35 lines
1.0 KiB
Rust
//! Crash-safe replacement of small state snapshots.
|
|
|
|
use std::fs::File;
|
|
use std::io::{self, Write};
|
|
use std::path::Path;
|
|
|
|
/// Replace `path` only after the complete new contents are durable.
|
|
pub(crate) fn write(path: &Path, contents: &[u8]) -> io::Result<()> {
|
|
let parent = path.parent().ok_or_else(|| {
|
|
io::Error::new(io::ErrorKind::InvalidInput, "snapshot path has no parent")
|
|
})?;
|
|
let mut temporary = tempfile::NamedTempFile::new_in(parent)?;
|
|
temporary.write_all(contents)?;
|
|
temporary.as_file_mut().sync_all()?;
|
|
temporary.persist(path).map_err(|error| error.error)?;
|
|
File::open(parent)?.sync_all()?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn atomically_replaces_an_existing_snapshot() {
|
|
let directory = tempfile::tempdir().unwrap();
|
|
let path = directory.path().join("state.json");
|
|
std::fs::write(&path, b"old").unwrap();
|
|
|
|
write(&path, b"complete new snapshot").unwrap();
|
|
|
|
assert_eq!(std::fs::read(path).unwrap(), b"complete new snapshot");
|
|
}
|
|
}
|