A directory contains notes.db and a larger neighbor called notes.db-wal. The obvious temptation is to treat the first file as the real database and the second as something temporary that can be ignored. The names encourage that intuition. In an active write-ahead-log database, the intuition can be wrong.

The interesting question is where the latest committed state lives. A database is a logical promise about records and transactions. The files are one implementation of that promise. When the implementation spreads current state across a database file and a log, looking at only the most familiar filename can hide work that has already committed.

Commit and checkpoint do different jobs

In SQLite's WAL documentation, changed pages are appended to a write-ahead log, and a commit record makes a transaction committed. A checkpoint subsequently transfers appropriate log content into the main database file. These are separate events. The log is consequently part of the database's persistent state while it contains committed content that has not been incorporated into the main file.

This arrangement makes a useful trade. A writer can append changes while a reader continues with an earlier view. Moving every change immediately into the main file would interfere with that reader's use of the old pages. The log allows the system to separate the newest committed view from the view a particular reader still needs.

Think of a hypothetical shared notebook containing an inventory of boxes. A reader begins a report when the inventory says 120 boxes. Another connection records 125 and commits. The report can consistently describe its earlier view rather than seeing half of one inventory and half of another. That consistency is more valuable than simply displaying the latest number at every instant.

SQLite's isolation documentation describes this WAL behavior as snapshot isolation and explains that writes are serialized. Multiple connections do not become multiple simultaneous writers to the same database just because readers and a writer can overlap. A design that confuses those two forms of concurrency may appear successful in a reading-heavy demonstration and then disappoint under a different workload.

The reader can affect the housekeeping

Checkpointing has to respect active readers. A reader holding an older view can prevent a checkpoint from progressing past content still needed for that view. SQLite's WAL guide discusses this interaction and the possibility of a growing log when checkpoints cannot complete. An unexpectedly large neighbor file can therefore be a clue about transaction lifetimes, not merely a demand to reduce a file-size setting.

This suggests a diagnostic story before it suggests a tuning knob. In the invented inventory application, perhaps a reporting connection starts a read transaction, streams a few rows, and then waits while unrelated work happens. The application regards the report as idle. The database still has a reader whose snapshot matters. The relevant time is the lifetime of the transaction, which may differ sharply from the time spent executing SQL.

That example is intentionally qualitative. It predicts no throughput or log size. Hardware, page sizes, workload, transaction boundaries, and the surrounding runtime all matter. A benchmark that changes journal mode while also changing the length of transactions has changed more than one variable. A useful account of the result would say so.

Durability needs a named failure

The sentence “the transaction is safe” is incomplete until the failure is specified. An application process crashing is different from the operating system crashing or the machine losing power. A database can preserve consistency after a failure yet lose the newest acknowledged transaction. Consistency and durability answer different questions.

The synchronous pragma reference makes a relevant distinction for WAL mode. With NORMAL, recent committed transactions can roll back after a power loss or system crash even though the WAL approach preserves consistency. FULL adds the synchronization behavior intended to make commits durable, subject to the storage stack honoring its promises. This is an explanation of documented modes, not a universal configuration prescription.

For a hypothetical disposable search index, rebuilding the latest changes might be acceptable. For a notebook whose user interprets a successful save as a durable commitment, the same loss could be a serious breach of expectation. The database setting alone cannot decide that product question. The application has to establish what its acknowledgment means and then use a storage arrangement compatible with it.

A backup is a view, not a gesture

Copying a live database file feels satisfyingly concrete. There is a source, a destination, and a reassuring byte count. But a backup needs a coherent database state, not merely successful file-transfer operations. If current committed pages are still in the WAL, copying only the main file does not establish that the current state has been captured.

SQLite provides an online backup API for making database copies while accounting for live activity. Its documentation describes incremental copying and how concurrent source changes can affect progress. This is a stronger foundation for understanding a backup than assuming that files with temporary-looking names are dispensable. The exact backup procedure still belongs to the application's runtime and operational environment.

An instructive recovery rehearsal for the invented notebook would choose a known saved record, create a backup through the supported mechanism, restore it into a separate location, and verify the record there. That is a proposed test of the promise the application makes. A check that the destination file exists tests a much smaller promise.

There is an aesthetic appeal to SQLite's compactness. It can make substantial data handling feel close enough to touch. That closeness is most useful when it encourages curiosity rather than casual handling. The main database file, the log, a reader's snapshot, the checkpoint, and the backup are different parts of one story. Understanding where a committed page lives is how the filenames start telling the truth.

About the author

Writer

I write about computing systems, from the meaning of a saved record to the work that turns a letter into pixels.

A thought worth keeping?