io: add platform-aware fsync helpers (dir + durable file sync)
Add an io::fsync module with a per-OS split (posix/windows) mirroring the writeback_file convention, replacing two duplicated dir-fsync copies: - dir(): POSIX directory fsync; a no-op on Windows, where std cannot open a directory as a File and the failed open logged a spurious warning on every mapfile write. - file_durable(): opens the target read+write before sync_all so the flush succeeds on Windows, where FlushFileBuffers rejects a read-only handle with ERROR_ACCESS_DENIED. Point the mapfile writer at the shared dir() helper.
This commit is contained in:
+2
-22
@@ -630,32 +630,12 @@ impl Mapfile {
|
||||
// this window is the wide one. Best-effort: a dir that can't be
|
||||
// opened/synced (some filesystems, Windows) is not a write failure.
|
||||
if let Some(parent) = self.path.parent() {
|
||||
fsync_dir(parent);
|
||||
crate::io::fsync::dir(parent);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// fsync a directory so a prior `rename(2)` into it is durable. After a
|
||||
/// crash a renamed file's dirent can otherwise be lost even though the
|
||||
/// rename returned, because it is still page-cache-only. Best-effort:
|
||||
/// opening the directory for read and `sync_all()`ing it is the POSIX way
|
||||
/// to flush its metadata; failures (unsupported fs, Windows) are logged
|
||||
/// and ignored rather than propagated, since the file bytes are already
|
||||
/// durable and the caller's write succeeded.
|
||||
fn fsync_dir(dir: &Path) {
|
||||
match std::fs::File::open(dir) {
|
||||
Ok(f) => {
|
||||
if let Err(e) = f.sync_all() {
|
||||
tracing::warn!(path = %dir.display(), error = %e, "failed to fsync mapfile directory");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(path = %dir.display(), error = %e, "could not open mapfile directory to fsync");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Mapfile {
|
||||
/// Best-effort flush on drop so a sweep / patch that returns early
|
||||
/// (or unwinds) doesn't lose its in-memory state. Errors here are
|
||||
@@ -863,7 +843,7 @@ mod tests {
|
||||
|
||||
// The directly-called dir fsync helper must be a no-op-on-error,
|
||||
// never a panic, even for a nonexistent directory.
|
||||
fsync_dir(&dir.join("does-not-exist"));
|
||||
crate::io::fsync::dir(&dir.join("does-not-exist"));
|
||||
|
||||
let loaded = Mapfile::load(&p).unwrap();
|
||||
assert_eq!(loaded.entries(), mf.entries());
|
||||
|
||||
Reference in New Issue
Block a user