skip to content

questions

5

After an unexpected server crash, a relational database keeps the effects of some transactions and throws others away. How does it decide which is which, and what does that mean for an application that already received a successful commit acknowledgement?

level: juniorimportance: must knowfreq 52%

answer

  1. log decides, not data files
  2. commit record durable equals committed
  3. winners redone, losers undone
  4. dirty pages of losers can already be on disk
  5. unknown-outcome commits need idempotent retry

basics

~20 s

The transaction log decides. A transaction whose commit record reached durable storage before the crash is kept, and reapplied if needed. Every transaction without a durable commit record is rolled back completely. So an acknowledged commit survives; in-flight work disappears atomically, never half-applied.

solid answer

~50 s

Recovery is driven by the transaction log, not by whatever happens to be sitting in the data files. On restart the engine scans the log and classifies transactions as **winners** (a commit record is present and durable) and **losers** (no commit record: they were in flight, or already aborting, when the crash hit). - **Winners** are guaranteed durable, but some of their changes may not have reached the data files yet because the modified pages were still cached in memory. Recovery reapplies those changes from the log. - **Losers** must be erased, and some of their changes *may* already sit in the data files, because a dirty page can be written to disk before its transaction commits. Recovery actively undoes them. The application contract is exactly the ACID one: if COMMIT returned success, those changes are present after restart. If the client never got the acknowledgement, the outcome is unknown to the client but still atomic in the database: all or nothing, never partial.

code

text · 8 lines
text
LSN 100  T1  UPDATE page 7  old=A new=B
LSN 110  T2  UPDATE page 9  old=X new=Y
LSN 120  T1  COMMIT
LSN 130  T2  UPDATE page 7  old=B new=C
<< crash >>

recovery: keep T1 (commit record present)
          undo all of T2 (no commit record)

go deeper

for a junior

State the rule cleanly: committed transactions survive, uncommitted ones are rolled back entirely, and the transaction log is what decides.

for a middle

Add why both directions are needed: committed changes may be missing from the data files, and uncommitted changes may already be in them, because page writes are lazy and independent of commit.

for a senior

Bring in the client-facing consequence: the ambiguous commit, idempotent retry design, and the availability gap while recovery runs.

for a principal

Frame it as the durability contract you are buying and its cost: what commit acknowledgement means to callers, how recovery time interacts with the availability objective, and where non-database side effects fall outside the guarantee.

## Why a crash is not a clean state A database keeps recently used data pages in a memory cache (the buffer pool) and writes them back to the data files lazily, in the background. That means at any instant the files on disk are an arbitrary mixture: some committed changes have not been written yet, and some uncommitted changes already have been. A crash freezes that mixture. Recovery's job is to turn it back into a state that satisfies atomicity (a transaction is all or nothing) and durability (an acknowledged commit is permanent). ## The log is the authority, not the data files Before any change is allowed to reach the data file, the log record describing that change must already be durable on disk. This ordering rule is what makes the log the authoritative history: whatever state the data files ended up in, the log knows every change that was made and by whom, and the log knows which transactions committed. Commit is defined by the log, not by the data files. A transaction is committed exactly when its commit record is safely on durable storage. Nothing else is required at commit time: the transaction's data pages may still be dirty in memory and are allowed to be lost by the crash, because they can be reconstructed from the log. ## Winners and losers During recovery the engine reads the log and sorts transactions into two buckets. **Winners** are transactions with a durable commit (or end) record. Their effects must exist after recovery. The engine reapplies any of their logged changes that are missing from the data files. This is what makes durability real: the commit acknowledgement was sent as soon as the commit record hit the disk, so recovery has to honour it. **Losers** are transactions with no commit record. Either they were still running, or they had begun rolling back, or the client had already issued a rollback. Whatever fraction of their work reached the data files must be removed. Recovery walks their log records backwards and reverses each change using the before-images recorded in the log. Crucially, a loser is not merely ignored. Ignoring it would be wrong, because a dirty page holding its changes may well have been flushed to the data file minutes before the crash by ordinary background writing or by cache pressure. ## What the application sees Three cases matter to a client. 1. **COMMIT returned success.** The changes are guaranteed present after restart. This is the promise the database sells, and it is the reason commits cost a durable write. 2. **The transaction was still open** when the crash happened (nothing committed). It is gone entirely, as if it never ran. No partial rows, no half-applied multi-statement work. 3. **The connection died at the moment of commit** and the client never saw the answer. The database's own state is still atomic, but the client cannot tell which way it went. This is why retry logic needs idempotency (a natural key, a client-supplied request identifier, or a follow-up read) rather than blind re-execution. A related consequence: any work the application did outside the database in the same logical operation, such as sending an email or calling a payment API, is not covered by recovery. Database recovery restores database state only, which is why outbox patterns exist. ## Recovery time and availability Recovery is not instantaneous. The engine must read a stretch of the log and reapply and reverse changes before it can safely accept queries, so the service is unavailable during that window. The size of that window depends mainly on how much work happened since the last checkpoint, that is, how far behind the data files had fallen. This is why a crash in a heavy write workload can take noticeably longer to come back than a crash in an idle system, and why operators care about bounding it when they have a recovery-time objective. ## The mental model to carry The data files after a crash are untrustworthy and incomplete. The log is trustworthy and complete. Recovery replays the log against the files until the files agree with the log's version of history, then removes everything the log says was never committed. Anything a client was told about a commit is preserved; anything it was not told about is undone as a unit.

  • A client sent COMMIT, the server crashed, and the client saw a connection reset. Is the transaction committed?
    The database's answer is definite but the client cannot know it without checking. If the commit record reached durable storage before the crash, recovery keeps the transaction; otherwise it is undone entirely. The client must resolve the ambiguity by reading back a unique marker (an idempotency key, a natural unique constraint, or the row itself) rather than blindly retrying, because a blind retry can double-apply the work.
  • Why does recovery have to undo uncommitted transactions at all, rather than just ignoring them?
    Because their changes can already be in the data files. Pages modified by a still-open transaction are dirty in the buffer pool and may be evicted or flushed by background writing at any time, well before commit. Once such a page is on disk, doing nothing would leave uncommitted data permanently visible, which breaks atomicity.

The data files are a half-finished whiteboard; the log is the meeting minutes. After the lights go out, you do not trust the whiteboard, you rewrite it from the minutes and erase anything the minutes never approved.

saying these in an interview costs you the question

  • Saying a commit is durable only once the table's data pages have been written to disk
  • Assuming uncommitted changes cannot possibly be in the data files, so undo is unnecessary
  • Claiming the database can be queried immediately at process start, with recovery happening later in the background
  • Treating a lost connection at commit time as a guaranteed rollback and retrying blindly

context

open as a page

During crash recovery, the engine may encounter a log record describing a page change that had already been written to the data file before the crash. How does it avoid applying that change a second time, and why would double application be harmful?

level: middleimportance: must knowfreq 45%

basics

~20 s

Every data page stores the log sequence number of the last change applied to it. Before replaying a record, recovery compares the record's LSN with the page's stored LSN; if the page is already at or beyond it, the change is skipped. That makes replay idempotent, which matters because many logged changes are not repeatable operations.

open as a page

Describe the phases an ARIES-style recovery process runs through when a relational database restarts after a crash, and what each phase accomplishes.

level: middleimportance: must knowfreq 58%

basics

~20 s

Three passes. Analysis reads forward from the last checkpoint to rebuild the list of in-flight transactions and dirty pages and to find where redo must start. Redo replays every logged change, committed or not, until the data files match the log. Undo then rolls back the transactions that never committed.

open as a page

When a relational engine rolls back a transaction, the undo work itself is written to the transaction log as compensation log records. What problem do those records solve, and what happens if the server crashes again while a rollback is still in progress?

level: seniorimportance: should knowfreq 30%

basics

~20 s

A compensation log record (CLR) logs each reversal as it is performed and carries a pointer to the next record still to be undone. That makes rollback restartable and never repeated: after a second crash, recovery reads the CLRs, sees which reversals are done, and resumes from the pointer. CLRs are redo-only and are never themselves undone.

open as a page

In ARIES-style recovery, the redo pass reapplies changes made by transactions that were still uncommitted at crash time, only for the undo pass to roll them back immediately afterwards. Why is that apparently wasteful design chosen over skipping the uncommitted transactions during redo?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Redo repeats history so that after it, the pages match the exact state at the moment of the crash. Undo then operates on a state it understands, using the same rollback path used at runtime. Selective redo would create a state that never existed, where logged before-images and page-internal structures no longer line up.

open as a page