Skip to content

Commit 88b558d

Browse files
authored
Add atomic multi-file mode with write-ahead journal and recovery (#19)
* Add atomic multi-file two-phase commit with write-ahead journal Implement --atomic flag for all-or-nothing multi-file operations using a two-phase commit protocol with a JSON write-ahead journal. When enabled, all file changes are first staged to temp files (phase 1), then atomically renamed over originals (phase 2). If the process is interrupted, a subsequent --recover run can roll back or --recover-forward to complete. Key features: - --atomic flag implies --backup via hard-links for safe rollback - --no-backup opt-out with explicit warning about rollback limitations - .toggle-atomic.journal WAL in CWD for crash recovery - .toggle-atomic.lock advisory lock prevents concurrent --atomic runs - Signal handling (SIGTERM/SIGINT) for graceful interrupt with journal preservation - Platform helpers: macOS F_FULLFSYNC, Windows rename retry with backoff - SHA-256 integrity verification for forward recovery - into_temp_path() fd release pattern for large batch support (>500 files) - CLI validation: --atomic rejects --dry-run, --no-backup requires --atomic - 13 new integration tests covering happy path, recovery, validation, and edge cases New files: src/journal.rs, src/platform.rs New deps: sha2, signal-hook, fd-lock https://claude.ai/code/session_01JTuqZjgo3pPPF5YUeGKarz * Fix clippy warnings and formatting for CI compliance - Replace io::Error::new(io::ErrorKind::Other, ...) with io::Error::other() - Remove identical if/else branches in stage() encoding check - Prefix unused encoding parameter with underscore - Apply cargo fmt formatting https://claude.ai/code/session_01JTuqZjgo3pPPF5YUeGKarz * Add libc dependency for macOS F_FULLFSYNC support The platform.rs macOS cfg block uses libc::fcntl and libc::F_FULLFSYNC but libc was missing from Cargo.toml, causing macOS CI builds to fail.
1 parent ec4ad31 commit 88b558d

9 files changed

Lines changed: 1738 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 186 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ serde = { version = "1", features = ["derive"] }
3636
serde_json = "1"
3737
# Encoding support
3838
encoding_rs = "0.8"
39+
# SHA-256 checksums for atomic journal integrity
40+
sha2 = "0.10"
41+
# Signal handling for graceful interrupt during atomic commits
42+
signal-hook = "0.3"
43+
# Cross-platform advisory file locking for concurrent atomic execution prevention
44+
fd-lock = "4"
45+
46+
[target.'cfg(target_os = "macos")'.dependencies]
47+
# macOS F_FULLFSYNC support for durable fsync
48+
libc = "0.2"
3949

4050
[package.metadata.cargo-semver-checks.lints]
4151
constructible_struct_adds_field = "allow"

src/cli.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,23 @@ pub struct Cli {
9393
/// Path to .toggleConfig TOML file
9494
#[arg(long = "config")]
9595
pub config: Option<PathBuf>,
96+
97+
/// Enable atomic multi-file mode: all files succeed or none are modified.
98+
/// Implies --backup unless --no-backup is explicitly passed.
99+
#[arg(long = "atomic")]
100+
pub atomic: bool,
101+
102+
/// Disable backup creation in atomic mode. Only valid with --atomic.
103+
/// WARNING: Without backups, rollback is not possible if the rename phase fails.
104+
#[arg(long = "no-backup")]
105+
pub no_backup: bool,
106+
107+
/// Recover from an interrupted atomic operation. Default: rollback.
108+
#[arg(long = "recover")]
109+
pub recover: bool,
110+
111+
/// Complete an interrupted atomic commit instead of rolling back.
112+
/// Must be combined with --recover.
113+
#[arg(long = "recover-forward")]
114+
pub recover_forward: bool,
96115
}

0 commit comments

Comments
 (0)