Complete guide to Python's os.fsync function covering file synchronization, disk writes, and practical examples.
Last modified April 11, 2025
This comprehensive guide explores Python’s os.fsync function, which forces write operations to disk. We’ll cover file synchronization, performance implications, and practical usage examples.
The os.fsync function forces write of file buffers to disk. It ensures all modified data and attributes are written to permanent storage.
Key parameter: fd (file descriptor). No return value. Raises OSError if the operation fails. Works on both Unix and Windows systems.
This example demonstrates the basic usage of os.fsync to ensure data is written to disk after file operations. This is crucial for critical data integrity.
basic_sync.py
import os
file_path = “important_data.txt” with open(file_path, “w”) as f: # Write data to file f.write(“Critical transaction data\n”) f.write(“Must not be lost on system crash\n”)
# Force write to disk
f.flush()
os.fsync(f.fileno())
print(“Data safely written to disk”)
The example writes critical data to a file and ensures it’s physically written to disk. Without fsync, data might remain in system buffers during a crash.
Note we first call flush() to ensure Python’s buffers are emptied, then fsync to force the OS to write to disk.
This example shows how os.fsync can be used to ensure database transaction durability. It mimics a simple transaction log implementation.
db_transaction.py
import os
def log_transaction(transaction_data): with open(“transaction.log”, “a”) as log_file: log_file.write(f"Transaction: {transaction_data}\n") log_file.flush() os.fsync(log_file.fileno())
print("Transaction safely logged")
log_transaction(“UPDATE accounts SET balance=1000 WHERE id=1”) log_transaction(“INSERT INTO logs VALUES (‘account_update’, NOW())”)
Each transaction is immediately written to disk before continuing. This prevents data loss if the system crashes after the transaction.
In real databases, this technique ensures ACID properties, particularly durability (the “D” in ACID).
This example demonstrates the performance impact of using os.fsync by comparing write operations with and without synchronization.
performance_test.py
import os import time
def test_write(count, use_fsync): start = time.time() with open(“testfile.txt”, “w”) as f: for i in range(count): f.write(f"Line {i}\n") if use_fsync: f.flush() os.fsync(f.fileno()) return time.time() - start
time_no_sync = test_write(1000, False) print(f"Without fsync: {time_no_sync:.4f} seconds")
time_with_sync = test_write(1000, True) print(f"With fsync: {time_with_sync:.4f} seconds")
ratio = time_with_sync / time_no_sync print(f"fsync is {ratio:.1f}x slower")
The example writes 1000 lines to a file with and without fsync after each write. It measures and compares the execution times.
Results will show fsync is significantly slower due to disk I/O overhead. Use it judiciously where data integrity is critical.
This example shows how to synchronize both file and directory metadata using os.fsync on the directory file descriptor.
dir_sync.py
import os
def create_file_with_metadata_sync(filename, content): # Get directory path and open directory dir_path = os.path.dirname(filename) if dir_path == “”: dir_path = “.”
dir_fd = os.open(dir_path, os.O_RDONLY)
# Create and write file
with open(filename, "w") as f:
f.write(content)
f.flush()
os.fsync(f.fileno()) # Sync file data
# Sync directory metadata
os.fsync(dir_fd)
os.close(dir_fd)
print(f"File {filename} created with full metadata sync")
create_file_with_metadata_sync(“new_config.cfg”, “[settings]\noption=value\n”)
The example ensures both file data and directory metadata are synchronized to disk. This is important when file existence is critical.
Directory synchronization ensures the new file’s metadata is durable, not just the file contents.
This example demonstrates an atomic file replacement pattern using os.fsync to ensure data integrity during the operation.
atomic_replace.py
import os
def atomic_replace(filename, content): # Write to temporary file tempname = filename + “.tmp” with open(tempname, “w”) as f: f.write(content) f.flush() os.fsync(f.fileno())
# Replace original file
os.replace(tempname, filename)
# Sync directory
dir_fd = os.open(os.path.dirname(filename) or ".", os.O_RDONLY)
os.fsync(dir_fd)
os.close(dir_fd)
print(f"Atomically replaced {filename}")
atomic_replace(“config.json”, ‘{“version”: 2, “settings”: {“debug”: false}}’)
The example writes to a temporary file first, syncs it to disk, then performs an atomic rename operation. Finally, it syncs the directory metadata.
This pattern ensures the original file is either completely preserved or completely replaced, never in a partially updated state.
This example demonstrates handling os.fsync differences between Unix and Windows systems, including error handling.
cross_platform.py
import os import sys
def safe_sync(file_obj): try: file_obj.flush() os.fsync(file_obj.fileno()) except AttributeError: # On Windows, sometimes need to call _commit if sys.platform == “win32”: import msvcrt msvcrt._commit(file_obj.fileno()) else: raise
def write_with_sync(filename, data): with open(filename, “w”) as f: f.write(data) safe_sync(f) print(f"Data safely written to {filename}")
write_with_sync(“data.bin”, b"\x00\x01\x02\x03\x04")
The example provides a cross-platform synchronization function that handles Windows-specific requirements through the msvcrt module.
On Windows, _commit may be needed in some cases to ensure data is written to disk, similar to fsync on Unix systems.
This example demonstrates proper error handling when using os.fsync, including cases where synchronization might fail.
error_handling.py
import os import errno
def write_with_guarantee(filename, data): try: with open(filename, “w”) as f: f.write(data) f.flush() try: os.fsync(f.fileno()) print(“Data successfully synchronized to disk”) except OSError as e: if e.errno == errno.EINVAL: print(“Warning: fsync not supported on this filesystem”) else: raise except IOError as e: print(f"Failed to write file: {e}")
write_with_guarantee(“critical.log”, “System shutdown initiated\n”)
The example handles various error conditions that might occur during file writing and synchronization, including unsupported operations.
EINVAL error specifically handles cases where the filesystem doesn’t support synchronization, common with some network filesystems.
Data integrity: fsync ensures data survives system crashes
Performance cost: Frequent fsync calls can significantly slow operations
Filesystem support: Not all filesystems honor fsync requests
Battery impact: On mobile devices, fsync can reduce battery life
Partial writes: Always write complete records before fsync
Use sparingly: Only for critical data that must survive crashes
Batch operations: Group changes and sync once at the end
Handle errors: Always check for and handle fsync failures
Test thoroughly: Verify behavior on your target filesystems
Consider alternatives: For some cases, O_DIRECT or O_SYNC may be better
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Python tutorials.