Skip to content
EL
Contents

Plain files, CSV, JSON and the cost of DIY storage

Making file storage safe, and what's still missing

ConceptFoundation4 min read#fundamentals#storage#durability#concurrency

The idea in 30 seconds

Three operating-system tools make a file store much safer: an atomic rename so readers never see a half-written file, fsync so saved data reaches the drive, and a lock so writers take turns. Even with all three, you still have no uniqueness, no links between files, no fast lookups and no way to change two files together. Adding those is building a database.

Helps to know first: What data is, and why we need a database

On this page

Three tools from the operating system

The lab showed three ways a JSON file fails: it is slow to search, two writers corrupt it, and a crash destroys it. The operating system gives you a tool for each of the last two.

ToolWhat it doesWhat it fixes
Atomic renameWrite the new version to a temporary file, then rename it over the old oneReaders and crashes only ever see a complete file
fsyncAsk the operating system to push a file’s data out to the drive before carrying onA save that “succeeded” but was still in memory when the power went
File lockWriters take an exclusive lock before read-change-write and release it afterTwo writers overwriting each other

Atomic rename

Writing a file in place passes through a moment where the file is empty or half-written. Renaming does not. The operating system guarantees that the new name always refers to a complete file: the old version until the rename, the new one after, even if the system crashes during the rename. In the lab that took the crash result from 0 of 20 readable to 20 of 20.

fsync, and how far “saved” really goes

When a program writes to a file, the data usually goes into the operating system’s memory first and reaches the drive later. A crash of your program does not lose it; a power cut can. fsync asks the operating system to push the data to the drive now.

Even that is not the end of the story. On macOS, fsync moves data from the computer to the drive, but the drive may keep it in its own cache for a while. The manual page says a power loss can still lose some or all of it, and offers a stronger call, F_FULLFSYNC, that asks the drive itself to flush to permanent storage. Databases have to deal with exactly this, on every platform they run on.

File locks

An exclusive lock makes writers take turns, which fixed the two-writer test: 2,000 users every time. But flock locks are advisory. They coordinate the processes that ask for them and do nothing about a process that just opens the file and writes. Every program that touches the file, including the one somebody writes next year, has to remember to take the lock.

What is still missing

Suppose you apply all three. Your store no longer corrupts itself. Compare it with what you need from real application data:

You needThe file store, with all three fixes
Find a user in milliseconds at any sizeStill reads and parses the whole file
Change one user cheaplyStill rewrites the whole file, now twice (temp file plus rename)
No two users with the same emailOnly if every writer checks, under the lock, every time
Every order belongs to a user that existsOrders and users are two files; nothing connects them
Debit one account and credit another, both or neitherTwo files mean two separate replacements; a crash can land between them
Many writers working at the same timeThe lock makes writers queue behind each other
A copy from last TuesdayWhatever backups you remembered to set up

Each row is a problem databases solved long ago, and each is a topic later in this field: indexes for fast lookups, constraints for uniqueness and links between tables, transactions for all-or-nothing changes, multi-version concurrency so readers do not wait, and a write-ahead log so a crash never leaves half a change behind.

Check yourself

  1. What does an atomic rename protect against, and what does it not?

    Show answer

    It guarantees the file name always points at a complete version, so readers and crashes never see a half-written file. It does nothing about two writers overwriting each other’s changes; that needs a lock.

  2. A program calls fsync after saving and then the machine loses power. Can the data still be lost on macOS? Why?

    Show answer

    Yes. fsync moves the data from the computer to the drive, but the drive can still be holding it in its own cache. F_FULLFSYNC asks the drive to flush that cache to permanent storage.

  3. Why can’t a file store easily guarantee “debit one account and credit another, both or neither”?

    Show answer

    Replacing two files is two separate operations, each atomic on its own. A crash between them leaves one balance changed and the other not, and nothing records that the pair was meant to change together.

Finished reading?

Saved in this browser only.

Sources

Where each claim on this page comes from.

  1. 1.
    rename(2) manual page, macOS
    Primary· DESCRIPTION· man 2 rename

    rename() guarantees that an instance of the new name always exists, even if the system crashes in the middle of the operation.

  2. 2.
    fsync(2) manual page, macOS
    Primary· DESCRIPTION· man 2 fsync

    fsync() flushes data from the host to the drive, but the drive may still hold it in its own cache; on macOS, the F_FULLFSYNC fcntl asks the drive to flush everything to permanent storage.

  3. 3.
    flock(2) manual page, macOS
    Primary· DESCRIPTION· man 2 flock

    flock() locks are advisory; they let cooperating processes coordinate but do not stop processes that ignore them.

  4. 4.
    PostgreSQL documentation: 28.3 Write-Ahead Logging (WAL)
    Primary· §28.3 Write-Ahead Logging (WAL)

    PostgreSQL flushes a log of each change to permanent storage before changing data files, and replays it after a crash.

Go further

What to read next, and why it is worth the time.

  1. 1.
    fsync(2) manual page, macOS
    Primary· man 2 fsync

    The macOS page is unusually candid about what fsync does not guarantee. Read the F_FULLFSYNC paragraph.

  2. 2.
    Designing Data-Intensive Applications
    ReferenceMartin Kleppmann· Chapter 7: Transactions· location not yet verified

    The guarantees this page shows you cannot easily build with files, explained carefully from the database side.

Applies to macOS or Linux, Python 3. Draft: not yet fact-checked end to end.