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

0
1
Asked By MellowCedar42 On

I'm designing recovery for a nondestructive document editor and want to establish sensible boundaries before the architecture becomes difficult to change. The editor has one authoritative canonical document state plus an optimistic UI that may 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 data, asset references, commands, supporting reconstruction data, and integrity hashes. A separate history or transform store might contain checkpoints, object-level history, and reduced representations of earlier transformed states for inspection, comparison, and selective recovery. I'm unsure whether these should be separate databases or simply separate responsibilities in one persistence layer.

Reopening currently uses checkpoints and a command journal. I'm considering expanding that into a source-preservation layer and a snapshot/history layer so the document can be reconstructed, drift can be detected, and corrupted states or missing assets can be recovered. I also need to design undo, including page-level undo and undo for individual objects that may have their own transform history.

At the moment I'm using inverse commands. I'm wondering whether inverse operations should remain in the append-only history and later be compacted at safe checkpoints, or whether undo should move an object's history pointer backward. I'm also considering event sourcing for future collaboration and whether the main journal and per-object history could share immutable command blocks without duplicating records.

The goals are immutable retained records, deterministic replay under a defined software version, automatic recovery when the correct result is unambiguous, and manual choices when it is not. I don't want to assume that adding more hashes or redundant records automatically makes the system trustworthy. How would you separate undo, snapshots, event sourcing, and recovery? How would you control storage growth without weakening the recovery guarantees? Are there useful examples, books, or projects that discuss similar designs or failed approaches?

3 Answers

Answered By QuietFalcon7 On

Undo and inverse commands are closely related, so the distinction needs to be explicit. In general, storage versus replayability is a real tradeoff: compression can remove redundancy, but it cannot preserve arbitrary information without storing that information somewhere. More aggressive compaction also tends to cost more CPU and design complexity. Decide what recovery fidelity and replay time you actually need, then optimize only the redundant parts.

MellowCedar42 -

What I meant was keeping the chronological journal intact while possibly maintaining a per-object history that points to existing command blocks or snapshots. I’m trying to decide whether undo should create a new transition back to earlier values, possibly referencing an existing snapshot, rather than moving a pointer and changing the interpretation of history. Sharing underlying immutable records seems attractive, but I don’t want a failure in the object-history layer to compromise the main journal.

Answered By StoneOrbit5 On

A practical separation is: the command log is the authoritative ordered history, snapshots are checkpoints used to shorten replay, and undo is a new command that applies the inverse of an earlier operation in the current state. Don’t mutate history by moving a pointer if you need an auditable append-only record. A snapshot can reference an earlier state or command range, so undo does not necessarily require duplicating the full document.

Every operation should declare whether it has a safe inverse. If it does not, retain the affected data or create a state snapshot. Periodically validate that loading a checkpoint and replaying its following commands produces the same canonical bytes as the expected state. Storage can be controlled with checkpoint intervals, deduplicated immutable blocks, and retention policies, but pruning should only remove records after a checkpoint or backup has taken over the recovery guarantee.

Answered By BrightMango18 On

For a collaborative editor, undo becomes much harder when other users have changed the same content since your edit. Append-only operations and inverse transforms are a reasonable starting point, but rapid undo and redo during concurrent edits can produce surprising results. You may need to define undo as reversing the local user’s effect in the current document rather than rewinding global time.

Conflict-free replicated data types can help with convergence and optimistic updates, but they add their own modeling complexity and are not automatically the right choice for every document. Full event history is often the simplest first implementation, especially while correctness matters more than disk usage. Add snapshots later to reduce startup replay time, and treat unusual high-conflict cases as an explicit product behavior rather than promising perfectly intuitive results.

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.