Plain files, CSV, JSON and the cost of DIY storage
Making file storage safe, and what's still missing
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.
| Tool | What it does | What it fixes |
|---|---|---|
| Atomic rename | Write the new version to a temporary file, then rename it over the old one | Readers and crashes only ever see a complete file |
fsync | Ask the operating system to push a file’s data out to the drive before carrying on | A save that “succeeded” but was still in memory when the power went |
| File lock | Writers take an exclusive lock before read-change-write and release it after | Two 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 need | The file store, with all three fixes |
|---|---|
| Find a user in milliseconds at any size | Still reads and parses the whole file |
| Change one user cheaply | Still rewrites the whole file, now twice (temp file plus rename) |
| No two users with the same email | Only if every writer checks, under the lock, every time |
| Every order belongs to a user that exists | Orders and users are two files; nothing connects them |
| Debit one account and credit another, both or neither | Two files mean two separate replacements; a crash can land between them |
| Many writers working at the same time | The lock makes writers queue behind each other |
| A copy from last Tuesday | Whatever 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
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.
A program calls
fsyncafter saving and then the machine loses power. Can the data still be lost on macOS? Why?Show answer
Yes.
fsyncmoves the data from the computer to the drive, but the drive can still be holding it in its own cache.F_FULLFSYNCasks the drive to flush that cache to permanent storage.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.rename(2) manual page, macOSPrimary· DESCRIPTION·
man 2 renamerename() guarantees that an instance of the new name always exists, even if the system crashes in the middle of the operation.
- 2.fsync(2) manual page, macOSPrimary· DESCRIPTION·
man 2 fsyncfsync() 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.flock(2) manual page, macOSPrimary· DESCRIPTION·
man 2 flockflock() locks are advisory; they let cooperating processes coordinate but do not stop processes that ignore them.
- 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.fsync(2) manual page, macOSPrimary·
man 2 fsyncThe macOS page is unusually candid about what fsync does not guarantee. Read the F_FULLFSYNC paragraph.
- 2.Designing Data-Intensive ApplicationsReferenceMartin 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.