Skip to main content

atomic_publish

Function atomic_publish 

pub fn atomic_publish<F, E>(dest: &Path, write: F) -> Result<(), E>
where F: FnOnce(&Path) -> Result<(), E>, E: From<Error>,
Expand description

Publish a file atomically: write the payload to a process-unique temp sibling of dest, then rename it over dest only after write succeeds. An interrupted or failing write leaves any previous dest untouched — the half-written bytes live only in the temp file, which is removed on error. This is the single output-write choke point every emitter routes through so a ^C (or a mid-write failure) can never truncate a good previous output.

Mirrors the fact-cache publish idiom (facts::FactsDb cache write): temp in the destination’s own directory so the rename is a same-filesystem metadata operation (a cross-device rename would fail), a pid suffix to isolate concurrent writers to the same destination, and an fsync before the rename.

write receives the temp path and must create and fully write the file at it. The path it receives does not exist when write is called (any stale temp from this pid’s prior aborted run is cleared first), which is exactly what a DuckDB ATTACH ... (TYPE SQLITE) requires; a streaming writer (File::create) or a COPY ... TO names the path directly.

The error type is the caller’s: write returns Result<(), E> and the helper’s own I/O failures surface through the same E via E: From<std::io::Error> (satisfied by both CodeLoreError and anyhow::Error).

§Errors

Propagates write’s error, or an I/O error from the rename step. The fsync is best-effort (advisory durability): atomicity — the actual guarantee — comes from write-to-temp + rename, so a filesystem that refuses the re-open for fsync must not fail an otherwise-complete write.