using System; using System.Collections.Generic; using System.Linq; namespace ZB.MOM.WW.CBDDC.Core; /// /// Represents a Vector Clock for tracking causality in a distributed system. /// Maps NodeId -> HlcTimestamp to track the latest known state of each node. /// public class VectorClock { private readonly Dictionary _clock; /// /// Initializes a new empty vector clock. /// public VectorClock() { _clock = new Dictionary(StringComparer.Ordinal); } /// /// Initializes a new vector clock from an existing clock state. /// /// The clock state to copy. public VectorClock(Dictionary clock) { _clock = new Dictionary(clock, StringComparer.Ordinal); } /// /// Gets all node IDs in this vector clock. /// public IEnumerable NodeIds => _clock.Keys; /// /// Gets the timestamp for a specific node, or default if not present. /// /// The node identifier. public HlcTimestamp GetTimestamp(string nodeId) { return _clock.TryGetValue(nodeId, out var ts) ? ts : default; } /// /// Sets or updates the timestamp for a specific node. /// /// The node identifier. /// The timestamp to set. public void SetTimestamp(string nodeId, HlcTimestamp timestamp) { _clock[nodeId] = timestamp; } /// /// Merges another vector clock into this one, taking the maximum timestamp for each node. /// /// The vector clock to merge from. public void Merge(VectorClock other) { foreach (var nodeId in other.NodeIds) { var otherTs = other.GetTimestamp(nodeId); if (!_clock.TryGetValue(nodeId, out var currentTs) || otherTs.CompareTo(currentTs) > 0) { _clock[nodeId] = otherTs; } } } /// /// Compares this vector clock with another to determine causality. /// Returns: /// - Positive: This is strictly ahead (dominates other) /// - Negative: Other is strictly ahead (other dominates this) /// - Zero: Concurrent (neither dominates) /// /// The vector clock to compare with. public CausalityRelation CompareTo(VectorClock other) { bool thisAhead = false; bool otherAhead = false; var allNodes = new HashSet(_clock.Keys.Union(other._clock.Keys), StringComparer.Ordinal); foreach (var nodeId in allNodes) { var thisTs = GetTimestamp(nodeId); var otherTs = other.GetTimestamp(nodeId); int cmp = thisTs.CompareTo(otherTs); if (cmp > 0) { thisAhead = true; } else if (cmp < 0) { otherAhead = true; } // Early exit if concurrent if (thisAhead && otherAhead) { return CausalityRelation.Concurrent; } } if (thisAhead && !otherAhead) return CausalityRelation.StrictlyAhead; if (otherAhead && !thisAhead) return CausalityRelation.StrictlyBehind; return CausalityRelation.Equal; } /// /// Determines which nodes have updates that this vector clock doesn't have. /// Returns node IDs where the other vector clock is ahead. /// /// The vector clock to compare against. public IEnumerable GetNodesWithUpdates(VectorClock other) { var allNodes = new HashSet(_clock.Keys, StringComparer.Ordinal); foreach (var nodeId in other._clock.Keys) { allNodes.Add(nodeId); } foreach (var nodeId in allNodes) { var thisTs = GetTimestamp(nodeId); var otherTs = other.GetTimestamp(nodeId); if (otherTs.CompareTo(thisTs) > 0) { yield return nodeId; } } } /// /// Determines which nodes have updates that the other vector clock doesn't have. /// Returns node IDs where this vector clock is ahead. /// /// The vector clock to compare against. public IEnumerable GetNodesToPush(VectorClock other) { var allNodes = new HashSet(_clock.Keys.Union(other._clock.Keys), StringComparer.Ordinal); foreach (var nodeId in allNodes) { var thisTs = GetTimestamp(nodeId); var otherTs = other.GetTimestamp(nodeId); if (thisTs.CompareTo(otherTs) > 0) { yield return nodeId; } } } /// /// Creates a copy of this vector clock. /// public VectorClock Clone() { return new VectorClock(new Dictionary(_clock, StringComparer.Ordinal)); } /// public override string ToString() { if (_clock.Count == 0) return "{}"; var entries = _clock.Select(kvp => $"{kvp.Key}:{kvp.Value}"); return "{" + string.Join(", ", entries) + "}"; } } /// /// Represents the causality relationship between two vector clocks. /// public enum CausalityRelation { /// Both vector clocks are equal. Equal, /// This vector clock is strictly ahead (dominates). StrictlyAhead, /// This vector clock is strictly behind (dominated). StrictlyBehind, /// Vector clocks are concurrent (neither dominates). Concurrent }