Documentation
¶
Overview ¶
Package atomicfile replaces a file's content so that it is never seen truncated or half-written: a reader finds the old content or the new, whole (atomic visibility). Write gives that guarantee or does nothing: where the platform has no atomic rename for the target's volume, it refuses (ErrNoAtomicRename) before touching the target, unless the caller passed AllowNonAtomic, which accepts a replace without the guarantee on such a volume. Surviving a crash (durability) is promised only where the code below asks the filesystem for it: on Unix, not on Windows.
The new content, with its mode, goes to a temp file in the target's directory and is synced; the temp file is then renamed over the target.
On Unix the rename is rename(2), which replaces the target in one step, and Write then fsyncs the directory, which fsync(2) requires for the rename itself to reach stable storage. Once Write returns nil, a crash leaves the new content, on a filesystem that honors fsync of a directory (a filesystem that cannot sync a directory, where fsync reports EINVAL, gets visibility only).
On Windows, Go's os.Rename makes no atomicity promise, so the rename is the one Windows gives POSIX semantics (SetFileInformationByHandle, FileRenameInfoEx with FILE_RENAME_POSIX_SEMANTICS and FILE_RENAME_REPLACE_IF_EXISTS: internal/winfile, which storage/local's writes use too). A volume that refuses it (FAT and exFAT, Windows before 10 1709, and redirector volumes: network shares, mapped drives, \\wsl$ paths) refuses without changing anything, and Write then fails with ErrNoAtomicRename: no rename Windows offers there (MoveFileEx included) is documented as atomic. With AllowNonAtomic, Write uses os.Rename there instead, as Gombit did before this package existed. Nothing here makes the POSIX-semantics rename durable before Write returns, so on Windows a power loss right after a successful Write may still leave the old content.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoAtomicRename = fmt.Errorf("atomicfile: the volume refused an atomic rename (as FAT and exFAT volumes, network shares, \\\\wsl$ paths and Windows before 10 1709 do), so the file was not replaced: %w", errors.ErrUnsupported)
ErrNoAtomicRename: the target's volume refused an atomic (POSIX-semantics) rename, so Write refused before changing the target. It wraps errors.ErrUnsupported. Only Windows returns it (see the package doc for the volumes that do this).
var ErrNotDurable = errors.New("atomicfile: the file was replaced, but the replacement was not synced to disk")
ErrNotDurable: Write put the new content in place (readers see it), but the directory sync that makes the replacement survive a crash failed. A Write error wrapping it is a post-commit error; see Committed.
Functions ¶
func Committed ¶
Committed reports whether, after Write returned err, the target holds the new content: on success, and on an ErrNotDurable failure. Any other error left the target untouched. A caller that undoes its writes must treat a committed write as done, whatever the error.
func SetSyncDirForTest ¶
SetSyncDirForTest makes Write sync directories with f until the returned restore is called, so that callers can test their handling of a post-commit failure (ErrNotDurable). Not for concurrent use with Write.
func Write ¶
Write writes content, with mode, to a temp file in the target's directory, fsyncs and closes it, renames it over path, and (on Unix) fsyncs the directory: see the package doc for what that rename guarantees on each platform. A failure anywhere before the rename, or a refused rename, leaves path untouched and removes the temp file. A failed directory sync comes after the commit: the error wraps ErrNotDurable, and path holds the new content, which may not survive a crash (Committed reports true).
Types ¶
type Option ¶
type Option func(*options)
Option adjusts Write.
func AllowNonAtomic ¶
func AllowNonAtomic() Option
AllowNonAtomic lets Write replace the file without the atomic guarantee on a volume that refuses an atomic rename (Windows only; see the package doc), instead of failing with ErrNoAtomicRename. For a caller that can undo a half-done replace another way, such as a transaction with its own rollback.