-
-
Notifications
You must be signed in to change notification settings - Fork 845
platform: use F_FULLFSYNC on macOS for SyncFile data durability, fixes #9383 #9592
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 1.4-maint
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import fcntl | ||
| import os | ||
|
|
||
| from libc.stdint cimport uint32_t | ||
|
|
@@ -259,3 +260,30 @@ def set_flags(path, bsd_flags, fd=None): | |
| path_bytes = os.fsencode(path) | ||
| if lchflags(path_bytes, c_flags) == -1: | ||
| raise OSError(errno.errno, os.strerror(errno.errno), os.fsdecode(path_bytes)) | ||
|
|
||
|
|
||
| def fdatasync(fd): | ||
| """macOS fdatasync using F_FULLFSYNC for true data durability. | ||
|
|
||
| On macOS, os.fsync() only flushes to the drive's write cache. | ||
| fcntl F_FULLFSYNC flushes to persistent storage. | ||
|
Comment on lines
+268
to
+269
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first sentence is a bit weird. It "only" flushes the drive's write cache. What else is there to flush that FULLSYNC does?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fsync() on macOS fsync() (and Python's os.fsync()) asks the OS kernel to flush dirty data from its page cache to the drive. On macOS, however, fsync() does not guarantee that data has been committed to stable storage on the physical device. It only ensures the data has been handed off to the drive's own write buffer/cache. If the drive has a volatile write cache (most HDDs and many SSDs do), a power loss after fsync() returns can still result in data loss or corruption, because the drive firmware hasn't necessarily flushed its internal cache to the platters/NAND. F_FULLFSYNC (macOS-specific) F_FULLFSYNC is a macOS-specific fcntl command that goes one step further: it issues a hardware flush command (like FLUSH CACHE in ATA or SYNCHRONIZE CACHE in SCSI/NVMe) to the drive, forcing it to commit all buffered writes from the drive's own cache to persistent storage.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so, os.fsync is only a OS-level flush, while fcntl F_FULLSYNC does a hw-level write buffer flush additionally. |
||
| Falls back to os.fsync() if F_FULLFSYNC is not supported (e.g. network fs). | ||
| """ | ||
| try: | ||
| fcntl.fcntl(fd, fcntl.F_FULLFSYNC) | ||
| except OSError: | ||
| os.fsync(fd) | ||
|
|
||
|
|
||
| def sync_dir(path): | ||
| """Sync a directory to persistent storage on macOS using F_FULLFSYNC.""" | ||
| if isinstance(path, str): | ||
| path = os.fsencode(path) | ||
| fd = os.open(path, os.O_RDONLY) | ||
| try: | ||
| fdatasync(fd) | ||
| except OSError as os_error: | ||
| if os_error.errno != errno.EINVAL: | ||
| raise | ||
| finally: | ||
| os.close(fd) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is still a black change.