Plain POSIX files: remove commit on close (fs.rs)
Staging every write until close broke tempfile, git, and SQLite, which expect normal files. Actors that need an atomic publish write a temporary file and rename it; the SDK has write_atomic for that.
This commit is contained in:
@@ -77,6 +77,15 @@ pub fn reply(message: &Message, body: Value) -> String {
|
||||
format!("{}/{id}", me())
|
||||
}
|
||||
|
||||
/// Write a file so that readers see either the old or the new content,
|
||||
/// never half of it: write a temporary file next to `path`, then rename it.
|
||||
pub fn write_atomic(path: impl AsRef<std::path::Path>, contents: impl AsRef<[u8]>) -> std::io::Result<()> {
|
||||
let path = path.as_ref();
|
||||
let tmp = path.with_file_name(format!(".{}.tmp-{}", path.file_name().unwrap_or_default().to_string_lossy(), next_id()));
|
||||
std::fs::write(&tmp, contents)?;
|
||||
std::fs::rename(&tmp, path)
|
||||
}
|
||||
|
||||
/// Publish a message to this actor's subscribers and return its full ID.
|
||||
pub fn publish(body: Value) -> String {
|
||||
let id = next_id();
|
||||
@@ -201,6 +210,18 @@ mod tests {
|
||||
assert_eq!(message.kind(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_atomic_replaces_the_file() {
|
||||
let dir = std::env::temp_dir().join(format!("sausage-actor-{}", next_id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("state.json");
|
||||
write_atomic(&path, "old").unwrap();
|
||||
write_atomic(&path, "new").unwrap();
|
||||
assert_eq!(std::fs::read_to_string(&path).unwrap(), "new");
|
||||
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 1);
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_ids_differ() {
|
||||
assert_ne!(next_id(), next_id());
|
||||
|
||||
Reference in New Issue
Block a user