How should undo, snapshots, and recovery work together in a deterministic document editor?

0
1
Asked By MellowCedar42 On

I'm designing recovery for a nondestructive document editor and want to establish the right boundaries before the architecture becomes difficult to change. The editor has one authoritative canonical document state plus an optimistic UI that can run ahead and reconcile when an edit is rejected. Original material and asset references should remain preserved separately from the current transformed presentation.

I'm considering a source store containing original assets, references, commands, supporting data, and integrity hashes. A separate history or transform store might contain checkpoints, object-level history, and compact representations of earlier transformed states for inspection, comparison, and selective recovery. I'm not sure whether these should be separate databases or simply separate responsibilities in one persistence layer.

At present, reopening uses checkpoints plus an append-only command journal. I'm considering adding retained source data and snapshots so the editor can reconstruct documents, detect drift, rebuild corrupted state, and recover missing assets without copying the entire document after every edit. I also need to decide how undo should work at the document, page, and object levels. For example, should undo append an inverse command, move an object's active history position backward, or reference an existing snapshot? I'd like to keep event-sourced designs open for future collaboration, but I'm concerned about mixing local undo, event sourcing, snapshots, and collaborative editing.

The goal is for retained records to be immutable and replay to be deterministic under a defined software version. Recovery should happen automatically when the result is unambiguous and offer manual choices otherwise. I also want storage compaction and pruning without weakening the recovery guarantees. What boundaries and design patterns work well for undo, snapshots, event logs, and recovery? Are there useful examples or resources, including approaches that failed?

3 Answers

Answered By SilverMaple88 On

Start with the smallest reliable system: immutable versioned commands, deterministic replay, checksummed snapshots, and a clear schema and software-version identifier. Add snapshots at bounded intervals or when replay cost exceeds a target. Keep at least one validated snapshot before the retained journal range, and only prune commands after proving that the snapshot contains everything needed to reconstruct the promised states.

Storage can be reduced with deduplicated content-addressed blobs, structural sharing, compression, and snapshot retention policies. Those are safe only when they remove duplicate representation rather than historical information you still promise to recover. If an operation is not cleanly invertible, store the original value or a sufficient before-image; otherwise an inverse command cannot reliably restore it. Recovery tests should include truncated journals, corrupted snapshots, missing assets, schema migrations, repeated undo and redo, and replay from every supported checkpoint.

Answered By QuietHarbor7 On

Undo, snapshots, and recovery solve different problems. An event or command log describes how the document changed. Undo is a user-facing operation that creates a new logical change, usually an inverse operation, rather than secretly erasing history. A snapshot is a checkpoint used to reduce replay time and provide a known reconstruction point. Recovery is the process of validating a checkpoint, replaying later commands, and detecting when the result cannot be trusted.

A practical design is an append-only journal plus periodic immutable snapshots. Undo can append an inverse command that references the necessary prior values or an earlier content record. It should not generally move a global history pointer backward, because that makes collaboration, branching, crash recovery, and auditability much harder. If an inverse operation depends on context that has since changed, record that context explicitly and define what happens when the inverse is no longer valid.

The journal and object history can share immutable command records or content-addressed blocks, but the references must preserve the original ordering, version, dependencies, and boundaries. Test that loading a snapshot and replaying its suffix produces exactly the same canonical bytes as replaying from the beginning. Hashes help detect corruption or drift, but they do not prove that the commands or transformation code were correct.

MellowCedar42 -

That distinction helps. I was mainly thinking about an object-level history layer, not deleting commands from the main journal. I’ll treat undo as another recorded transition and let object histories reference shared command blocks or snapshots without replacing the chronological journal.

Answered By OrbitingPine19 On

For collaborative editing, local undo becomes much more complicated because another user may have changed the same object between the original operation and the undo request. An inverse transform may then be applied to a different context than the one in which it was created. You need explicit conflict semantics, such as undoing only the local user’s contribution, rejecting an inverse whose preconditions no longer hold, or producing a compensating edit that is visible to everyone.

CRDTs or operational transformation can help with convergence and optimistic updates, but neither automatically defines good user-facing undo or guarantees that arbitrary transformation code is deterministic. Keep the collaboration protocol, document operations, and recovery format versioned separately. Define canonical serialization, operation preconditions, and behavior for unsupported or obsolete commands.

Related Questions

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.