Skip to main content
cd ../lexicon
sys/docs/lexicon/version-vector-reconciliation-with-sibling-explosion.md
Lexicon

Version Vector Reconciliation with Sibling Explosion

Difficulty: Advanced
2 min read

In plain English

Plain definition

The uncontrolled accumulation of unmerged concurrent write versions for a single key in a vector-clock-based leaderless datastore, caused by clients failing to reconcile or propagate causal context.

In systems like Riak or early Dynamo-style stores, causality between writes to the same key is tracked with a vector clock or its refinement, the dotted version vector (DVV). When two writes are causally concurrent (neither vector clock dominates the other), the system cannot safely pick a winner, so it stores both versions as siblings and defers resolution to the client (semantic reconciliation, e.g. CRDT merge or application-level merge functions) or to background read-repair. Sibling explosion is the pathological case where the rate of concurrent writes, client crashes mid-reconciliation, or misconfigured actors that never merge causes the sibling set for a key to grow unbounded across dozens or hundreds of versions.

The mechanical trigger is almost always a client that reads a value, fails to correctly propagate the full causal context (the vector clock/DVV it observed) on the subsequent write, or writes without ever reading first. Every such write is seen by the coordinator as causally concurrent with all existing siblings rather than a descendant of one, so nothing gets garbage collected. This is compounded in multi-datacenter deployments where W/DW quorum writes land on disjoint replica sets before anti-entropy has a chance to converge them, and by application bugs that treat the datastore as last-write-wins when the underlying engine is actually siblings-on-conflict.

Operationally, sibling explosion manifests as a slow creep in per-key object size, GET latency spikes correlated with specific hot keys, and eventually node-level memory pressure or compaction stalls as the storage engine (e.g. Bitcask or LevelDB backends) repeatedly serializes and deserializes bloated sibling lists. Mitigations include: enforcing causal-context-carrying writes at the client SDK level (never allow a blind PUT), bounding sibling counts with a hard cap that forces server-side LWW fallback past a threshold, using CRDTs (counters, sets, maps) instead of opaque blobs so merge is commutative and associative by construction, and running aggressive read-repair or active anti-entropy to collapse siblings proactively rather than waiting for client-driven reconciliation.

The broader architectural lesson is that leaderless, multi-master systems that expose causality tracking to the application are trading write availability for a reconciliation obligation that must be honored continuously; deferring or dropping that obligation does not eliminate the conflict, it accumulates it as unbounded state on the hot path of every subsequent read.