You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The current mechanism is not robust in two ways. It can be fixed completely on POSIX and be fixed somewhat on Windows.
This is also documented in your code: The old file is moved to a backup path, then the new file is moved from a temp path to the target path. If the world stops between these steps, there is no file at the target path. At least POSIX guarantees that a file rename(2) is atomic, i.e. if you do not move the old file from the target path, but "over"-os.(*File).Rename() the new file to the target path, then whenever the world stops, there will always be either the old file or the new file. Never no file. This atomic rename guarantee is valid on POSIX, only valid-ish on Windows.
You should fsync(2) the files. When the world stops you do not have any guarantee of what your files look like, except for the backup file. If Go's file methods resemble the POSIX semantics (I assume they do), then os.(*File).Close() is not enough. You need to do os.(*File).Sync() before os.(*File).Close(). Crashing a process and crashing the machine are two different error modes. Writing files, then not syncing the files, then renaming files, then pulling the power cord leads to unpredictable results even with journaling filesystems.
I'd suggest to do the following:
copy from target path to backup path
sync then close backup file
write new file
sync then close new file
rename new file to target path
on rollback rename backup path to target path.
on success hide or delete backup path
If possible do a hard link instead of a copy in step 1 (would need admin privileges on Windows, IIRC).
There were quite extensive and enlightening discussions about filesystem consistency myths back in the day. For some background information refer to Ted Ts'os blog post Don't fear the fsync.
/e fixed web link
The text was updated successfully, but these errors were encountered:
The current mechanism is not robust in two ways. It can be fixed completely on POSIX and be fixed somewhat on Windows.
This is also documented in your code: The old file is moved to a backup path, then the new file is moved from a temp path to the target path. If the world stops between these steps, there is no file at the target path. At least POSIX guarantees that a file
rename(2)
is atomic, i.e. if you do not move the old file from the target path, but "over"-os.(*File).Rename()
the new file to the target path, then whenever the world stops, there will always be either the old file or the new file. Never no file. This atomic rename guarantee is valid on POSIX, only valid-ish on Windows.You should
fsync(2)
the files. When the world stops you do not have any guarantee of what your files look like, except for the backup file. If Go's file methods resemble the POSIX semantics (I assume they do), thenos.(*File).Close()
is not enough. You need to doos.(*File).Sync()
beforeos.(*File).Close()
. Crashing a process and crashing the machine are two different error modes. Writing files, then not syncing the files, then renaming files, then pulling the power cord leads to unpredictable results even with journaling filesystems.I'd suggest to do the following:
If possible do a hard link instead of a copy in step 1 (would need admin privileges on Windows, IIRC).
There were quite extensive and enlightening discussions about filesystem consistency myths back in the day. For some background information refer to Ted Ts'os blog post Don't fear the fsync.
/e fixed web link
The text was updated successfully, but these errors were encountered: