ZetCode

Python os.fsync Function

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.

Basic Definitions

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.

Basic File Synchronization

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

# Open file for writing
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.

Database Transaction Safety

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")

# Example usage
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).

Performance Impact Measurement

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

# Test without fsync
time_no_sync = test_write(1000, False)
print(f"Without fsync: {time_no_sync:.4f} seconds")

# Test with fsync
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.

Directory Synchronization

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.

Atomic File Replacement

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.

Cross-Platform Considerations

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.

Error Handling

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.

Security Considerations

Best Practices

Source References

Author

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.