Skip to content

VLC double-increment on event receive inflates vector clock #22

Description

@ABresting

Bug

ConsensusEngine::add_event() and receive_event_from_network() both call vlc.merge() followed by vlc.tick() when processing a received event. However, VLC::merge() internally calls VLCSnapshot::receive(), which already performs the increment (merge vector clocks → set logical_time = max + 1 → increment local node's vector clock entry).

The subsequent vlc.tick() calls VLCSnapshot::increment() again, resulting in two increments per receive — the local node's vector clock entry advances by 2 and logical_time advances by 2 for every single received event.

Impact

  • Vector clock entries grow at 2x the correct rate on receive paths
  • logical_time inflates faster than it should, causing should_fold() (VLC delta threshold) to trigger prematurely
  • Causal ordering comparisons (happens_before) are still correct (monotonicity is preserved), but the clock values are unnecessarily inflated

Root cause

// consensus/src/engine.rs — add_event() and receive_event_from_network()
{
    let mut vlc = self.vlc.write().await;
    vlc.merge(&event.vlc_snapshot);  // calls receive() which already increments
    vlc.tick();                       // redundant second increment
}

VLC::merge()VLCSnapshot::receive():

pub fn receive(&mut self, other: &VLCSnapshot, local_node_id: &str) {
    self.vector_clock.merge(&other.vector_clock);
    self.logical_time = self.logical_time.max(other.logical_time) + 1;  // +1
    self.vector_clock.increment(local_node_id);                          // increment
    self.physical_time = Self::current_physical_time();
}

Fix

Remove the redundant vlc.tick() after vlc.merge() in both add_event() and receive_event_from_network(). The create_event() and tick_and_get_vlc() paths are correct (they only call tick() once for local event creation).

Reference

VLC update rules per Chrono: Building a Verifiable Logical Clock for P2P NetworksUPDATE(id, c, [c_received]) performs merge + single increment. The receive path should not double-increment.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions