Version vectors: distinguish later from concurrent
Compare componentwise causal histories and preserve incomparable updates instead of inventing an ordering from clock time.
On this page
The question for today
What you’ll understand
- Compare fixed-membership version vectors componentwise.
- Calculate a merge context and a subsequent local update.
- Explain the metadata and actor-identity assumptions behind the comparison.
A newer-looking timestamp cannot tell you whether one update incorporated another. Version vectors instead summarize per-actor progress in a defined history. One vector may dominate another, or the two may be incomparable because each contains work the other has not observed. This is the distinction between causally later and concurrent. We will use fixed actor identities and contiguous per-actor counters so the small examples expose the mechanism without silently assuming arbitrary missing events are represented.
Componentwise order represents observed progress
Give each participating writer a stable identity and an increasing local event counter. A vector entry records the prefix of that actor’s updates incorporated into the relevant history. Before creating a new version, combine the causal context actually read or received, then increment the origin actor’s component. The scope matters: an object-version vector, a process-wide vector clock, and a replication stream watermark can have different meanings. Comparing integers is easy; defining which events the integers summarize is the important design work.
Vector v is no later than w when every component of v is at most the matching component of w. Strict domination also requires at least one smaller component. If neither dominates, the versions are concurrent under this model. They need not have occurred at the same physical instant. A disconnected update from yesterday can be concurrent with today’s update when neither observed the other. A total timestamp ordering would hide that causal distinction by selecting a winner even when the histories are incomparable.
Merge context without pretending values merged themselves
The componentwise maximum summarizes both represented prefixes. It does not by itself decide what two conflicting application values mean. A multi-value register can retain both incomparable versions for explicit resolution; a CRDT may combine them using type-specific rules. If an application resolves two siblings, its replacement must carry the context of both so subsequent readers know the new version supersedes both histories. Omitting one sibling’s context can make that supposedly resolved sibling reappear on the next synchronization.
A compact vector is valid only under its identity and prefix assumptions. Reusing an actor identity after losing its counter can make an old event indistinguishable from a new one. Pruning entries or compressing sparse histories also needs a justified rule. Dotted version vectors separate a specific new event from causal context and can address practical sibling-tracking issues, but their correctness comes from the actual protocol. Do not implement arbitrary vector truncation merely because a drawing becomes crowded.
Budget metadata and preserve client context
With r actors and fixed-width counters, the counter payload alone grows proportionally to r, before actor identifiers and encoding overhead. A mobile system with unbounded transient writers therefore needs an explicit membership and compaction design. Clients must return causal context when the API requires it; a write without the context it read can look concurrent even if a human intended a replacement. Test interleavings with delayed delivery, repeated messages, restarted actors, and clients that accidentally drop their version token.
The geometry lab can show why two-coordinate comparison is not a single numeric ranking. Reset, set angle to 0 degrees and magnitude to 2, predict projections (2,0), then Step. Change angle to 90 degrees with magnitude still 2 and predict (0,2). Neither coordinate pair dominates the other componentwise. The lab draws a continuous 2D vector, not a causal clock; rounding near zero and its angular interpolation are irrelevant to exact event counters. Write the discrete history alongside it.
Work it through
Resolve two incomparable histories
Actors A and B supply versions v=(3,1) and w=(2,2). A resolves both and creates exactly one further update. Compute comparisons, merged context, and the new version.
Compare A’s components: 3 exceeds 2. Compare B’s components: 1 is less than 2. Neither vector dominates, so the two versions are concurrent under the stated history model.
Take componentwise maxima to obtain context (3,2). It includes both known prefixes: three A events and two B events, five distinct actor-event identities in this toy accounting.
A creates a new event by incrementing its own component, yielding (4,2). That vector dominates both inputs. This records that the resolution observed both histories; it does not establish that the chosen replacement value satisfies the product’s business rules.
The new vector is (4,2), representing six actor events. Causal metadata records observation, while resolution still defines the value.
The living notebook
Explore the model
Interactive lab · vectors
Two coordinates can be incomparable
Reset, set angle 0° and magnitude 2, predict (2,0), then Step. Set angle 90° while retaining magnitude 2 and predict (0,2). These geometric projections illustrate componentwise incomparability; the lab does not implement version vectors or events.
- x component
- 1.393
- y component
- 0.975
- Magnitude
- 1.7
Playback traces a fixed vector and its projections. Angle and magnitude set the geometry; the moving marker does not change them. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forA total ranking would discard information that a componentwise partial order preserves.
Pause & reason
Check your understanding
Your turn
Try it yourself
Join v=(1,4,2) and w=(3,2,2), then let actor B create one event after observing the join. How many actor-event identities does the resulting prefix vector represent?
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Take maxima first; then increment the second component, not every component.
Compare with the solution
The joined context is (3,4,2). B’s new event gives (3,5,2), representing 3+5+2=10 distinct actor-event identities under the contiguous-prefix model. Summing is useful for this count, but equal sums would not imply equal or causally ordered histories.
Keep the idea
Take it with you
- Concurrency means neither history includes the other, not simultaneous physical time.
- A vector join combines context, while a value merge needs explicit semantics.
- Stable actor identities and faithful counter prefixes are correctness requirements.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.