Skip to content

Consistency & Vector Clocks

When multiple nodes can read and write the same cache key, you need a way to track which value is the most recent. The framework uses vector clocks for this.


The Problem

Without coordination, concurrent writes on different nodes can produce conflicting values:

Node A writes config:theme = "dark"
Node B writes config:theme = "light"   (at the same time)
Which one wins?

Vector clocks solve this by tracking causality.


How Vector Clocks Work

Each node maintains a logical clock (map<node_id, counter>). When a node writes to the cache:

  1. It increments its own counter in the vector clock.
  2. The updated clock is stored alongside the value.
  3. When another node receives the value, it can determine if the update is newer, older, or concurrent.
cpp
// Access the vector clock
auto& clock = app.get_state()->get_clock();

// The clock is a map of node_id → uint64_t counter

Conflict Resolution Rules

When a node receives a cache update, it compares the incoming vector clock against the local clock for that key:

ScenarioResolution
One vector dominates (all counters >=)Newer value wins
Vectors conflict (A > B in some, B > A in others)Value with the later timestamp wins

vector_clock::compare(a, b) — Manual comparison

cpp
using namespace framework::support;

int result = vector_clock::compare(clock_a, clock_b);
// result < 0: a is older than b
// result > 0: a is newer than b
// result = 0: concurrent (conflicting)
// result = 2: equal

Helper methods on vector_clock:

MethodDescription
increment(node_id)Increment this node's counter.
update(other)Merge in another vector clock.
is_concurrent(other)Returns true if the clocks conflict.
operator<=(other)Returns true if this is dominated by other.
get_data()Returns the raw map<uuid, uint64_t>.

What This Means for You

You do not need to manage vector clocks manually in most cases. The framework handles:

  • Automatic clock propagation — When state->cache_set() is called, the current clock is included.
  • Automatic conflict resolution — When receiving cache updates from other nodes, the framework compares clocks and applies the correct value.
  • Anti-entropy sync — Background synchronization (see Anti-Entropy Sync) ensures all nodes converge.