Rollback and undo: where reversing stops
When can work be rolled back, where must rolling back stop, and how does recovery survive its own crash?
Rollback and undo
What it is. Replay moves forward from a trusted past. Rollback moves backward from an untrusted present. For that you need the before-image (the data as it was before the change). This is information the forward path had to deliberately save. So undo exists only where the storage was made to pay for it on the write path. That is why systems that update data in place keep an undo log, and systems that never overwrite do not need one.
Immutability makes rollback a pointer move
If the old version still exists because nothing was overwritten, undo is a metadata operation: go back to a snapshot, reset a reference, roll back to an earlier manifest (a file that lists which data files make up the table). The cost was pre-paid as space and cleaning work: a garbage collector is what eventually deletes old versions, so retention bounds how far back you can roll. Systems with mutable pages pay the same cost as an extra durable write per update instead. Same invoice, different line item.
The hard boundary: undo stops at the first visible effect
Inside the system's own storage, an operation has an inverse and you still hold its arguments. Outside it, there is none. An email that was sent, a card that was charged, a record that other consumers already read: you cannot reverse these. You can only compensate, which is not the same thing. A refund is not "the charge never happened". It is a second event, visible to the customer, with its own failure modes.
The design consequence is structural: arrange the operation so that the single irreversible act comes last. Do all the reversible work first. Then perform the one designated effect that decides whether everything else counts. The write-audit-publish pattern in data pipelines has this shape: write the data to a staging area, audit it, and only then publish. The publish is the designated act, so rollback is simply "do not publish".
Why undo and redo coexist
Undo cost is bounded by the size of the uncommitted work. Redo cost is bounded by the committed work since the recovery point. They are cheap in different directions, so the standard recovery algorithm (ARIES, below) uses both.
Recovery that survives its own crash
The standard recovery algorithm for databases with mutable pages, ARIES, combines both directions: it redoes everything since the oldest dirty page, then undoes the transactions that never committed. How it works is part of Persistence & Durability. What matters here is why it is built the way it is: crashes during recovery are the normal case, not the edge case, so redo is idempotent (a record is applied only if pageLSN < recordLSN) and every undo step is itself logged and never undone. Recovery can crash and restart any number of times and still converge. It is the first strong evidence for the hypothesis that the recovery path should be the normal path.
The version comparison is a trick you will meet again as a cache version token and as sequence-number de-duplication (Making a repeat harmless): a monotonic version on the record, a monotonic version on the target, and a comparison decides whether to apply.
Boundary
ARIES assumes a single node, surviving stable storage and mutable pages. It does not transfer to immutable storage, where undo is a pointer move and "repeating history" has no meaning. Lakehouse recovery (recovery of table formats such as Iceberg or Delta, which store immutable data files plus a small metadata log) is not ARIES. It is write-ahead intent plus a designated atomic commit. Immutability retires most of the algorithm.