Advance and Retreat Mechanism
Statement
The eg-walker processes events in topological order but must interpret each event's operation in the context of its parents — not the current state. The advance/retreat mechanism solves this by maintaining two simultaneous views of the document:
- Prepare version : can move forward (advance) and backward (retreat)
- Effect version : only moves forward (via apply)
Before applying event , the algorithm adjusts to match :
The Three Operations
Apply
Precondition: and .
Effect: Updates both versions: , .
Output: The transformed operation — how the document at changes.
Retreat
Precondition: .
Effect: . The effect version is unchanged.
Makes the prepare version behave as if had not yet happened.
Advance
Precondition: but .
Effect: . The effect version is unchanged.
Restores a previously retreated event to the prepare version without re-applying it.
Visualization
Processing events in topological order with the causal structure shown:
graph LR
e1 --> e2
e2 --> e3
e3 --> e4
e2 --> e5
e5 --> e6
e6 --> e7
e4 --> e8
e7 --> e8
The algorithm proceeds:
| Step | Action | after | after |
|---|---|---|---|
| 1 | apply() | ||
| 2 | apply() | ||
| 3 | apply() | ||
| 4 | apply() | ||
| 5 | retreat(), retreat() | ||
| 6 | apply() | ||
| 7 | apply() | ||
| 8 | apply() | ||
| 9 | advance(), advance() | ||
| 10 | apply() |
Note step 9: we advance without retreating — concurrent events coexist in the prepare version when the target event () has both branches as parents.
Computing the Retreat/Advance Sets
Given old prepare version and target :
Efficient computation uses a priority queue traversal:
- Insert frontier events of both and into a max-priority-queue (ordered by topological position)
- Pop the maximum element; tag it as "retreat" (from only) or "advance" (from only)
- If popped from both — it's a common ancestor; enqueue its parents
- Stop when all remaining elements are common ancestors
Index Transformation
The two-version state enables index transformation:
- Locate: Use the prepare version ( count) to find the -th visible character — this is where the operation's author saw the character
- Map: Translate that record's position to the effect version ( count) — this gives the correct index in the current document
graph TD
subgraph "Prepare Version (author's view)"
P["Index i → find i-th record with s_p = Ins"]
end
subgraph "Effect Version (current doc)"
E["Count records with s_e = Ins before target → output index j"]
end
P --> |"position mapping"| E
If the target record has , the deletion is a no-op (the character was already deleted by a concurrent operation).
Connections
The retreat/advance mechanism maintains the Internal State MachineInternal State MachineThe per-character state machine that tracks insertion/deletion across prepare and effect versions in eg-walker.Read more → invariants. Correctness of the overall algorithm (the Event Graph Convergence ProofEvent Graph Convergence ProofFormal proof that eg-walker produces identical document states regardless of topological sort order — strong eventual consistency.Read more →) depends on retreat and advance being exact inverses for the prepare version. The Eg-walker: Event Graph WalkerEg-walker: Event Graph WalkerA collaboration algorithm for text editing that combines CRDT convergence with OT-competitive memory and merge performance.Read more → overview provides the broader algorithmic context.
Referenced by
- Eg-walker: Event Graph WalkerDistributed Systems
- Internal State MachineDistributed Systems
- Event Graph Convergence ProofDistributed Systems