diff --git a/src/benchmark/Akka.Benchmarks/Cluster/VectorClockBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Cluster/VectorClockBenchmarks.cs
index c97d3263f2c..65c2a3b29f6 100644
--- a/src/benchmark/Akka.Benchmarks/Cluster/VectorClockBenchmarks.cs
+++ b/src/benchmark/Akka.Benchmarks/Cluster/VectorClockBenchmarks.cs
@@ -14,7 +14,7 @@ namespace Akka.Benchmarks.Cluster
[Config(typeof(MicroBenchmarkConfig))]
public class VectorClockBenchmarks
{
- [Params(100, 500, 1000)]
+ [Params(100)]
public int ClockSize;
[Params(1000)]
diff --git a/src/core/Akka.Cluster/VectorClock.cs b/src/core/Akka.Cluster/VectorClock.cs
index 7d208883f25..4c5b7c6387c 100644
--- a/src/core/Akka.Cluster/VectorClock.cs
+++ b/src/core/Akka.Cluster/VectorClock.cs
@@ -40,7 +40,7 @@ public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
- return obj is VectorClock && Equals((VectorClock) obj);
+ return obj is VectorClock vc && Equals(vc);
}
public override int GetHashCode()
@@ -119,22 +119,26 @@ public static Node FromHash(string hash)
{
return new Node(hash);
}
+
+
private static Node Hash(string name)
{
- var md5 = System.Security.Cryptography.MD5.Create();
- var inputBytes = Encoding.UTF8.GetBytes(name);
- var hash = md5.ComputeHash(inputBytes);
+ // TODO: replace with Murmur3 or SHA512, some other consistent hash algorithm for FIPS complaince and no collisions in Akka.NET v1.5 or 2.0
+ using(var md5 = System.Security.Cryptography.MD5.Create()){
+ var inputBytes = Encoding.UTF8.GetBytes(name);
+ var hash = md5.ComputeHash(inputBytes);
- var sb = new StringBuilder();
+ var sb = new StringBuilder();
- foreach (var t in hash)
- {
- sb.Append(t.ToString("X2"));
- }
+ foreach (var t in hash)
+ {
+ sb.Append(t.ToString("X2"));
+ }
- return new Node(sb.ToString());
+ return new Node(sb.ToString());
+ }
}
///
@@ -167,7 +171,7 @@ public int CompareTo(Node other)
///
/// Timestamp used by the .
///
- internal class Timestamp
+ internal static class Timestamp
{
///
/// TBD
@@ -338,7 +342,7 @@ public bool IsSameAs(VectorClock that)
return left.IsConcurrentWith(right);
}
- private static readonly KeyValuePair CmpEndMarker = new KeyValuePair(Node.Create("endmarker"), long.MinValue);
+ private static readonly (Node, long) CmpEndMarker = (Node.Create("endmarker"), Timestamp.EndMarker);
///
///
@@ -364,58 +368,55 @@ public bool IsSameAs(VectorClock that)
/// The true ordering based on the contents of the vectorclock.
internal Ordering CompareOnlyTo(VectorClock that, Ordering order)
{
- if (ReferenceEquals(this, that) || Versions.Equals(that.Versions)) return Ordering.Same;
+ if (ReferenceEquals(this, that) || ReferenceEquals(this.Versions, that.Versions)) return Ordering.Same;
- return Compare(_versions.GetEnumerator(), that._versions.GetEnumerator(), order == Ordering.Concurrent ? Ordering.FullOrder : order);
+ return Compare(_versions.Select(x => (x.Key, x.Value)).GetEnumerator(), that._versions.Select(y => (y.Key, y.Value)).GetEnumerator(), order == Ordering.Concurrent ? Ordering.FullOrder : order);
}
- private static Ordering Compare(IEnumerator> i1, IEnumerator> i2, Ordering requestedOrder)
+ private static Ordering Compare(IEnumerator<(Node, long)> i1, IEnumerator<(Node, long)> i2, Ordering requestedOrder)
{
//TODO: Tail recursion issues?
- Func, KeyValuePair, Ordering, Ordering> compareNext = null;
- compareNext =
- (nt1, nt2, currentOrder) =>
+ Ordering CompareNext((Node, long) nt1, (Node, long) nt2, Ordering currentOrder)
+ {
+ if (requestedOrder != Ordering.FullOrder && currentOrder != Ordering.Same && currentOrder != requestedOrder) return currentOrder;
+ if (nt1.Equals(CmpEndMarker) && nt2.Equals(CmpEndMarker)) return currentOrder;
+ // i1 is empty but i2 is not, so i1 can only be Before
+ if (nt1.Equals(CmpEndMarker)) return currentOrder == Ordering.After ? Ordering.Concurrent : Ordering.Before;
+ // i2 is empty but i1 is not, so i1 can only be After
+ if (nt2.Equals(CmpEndMarker)) return currentOrder == Ordering.Before ? Ordering.Concurrent : Ordering.After;
+ // compare the nodes
+ var nc = nt1.Item1.CompareTo(nt2.Item1);
+ if (nc == 0)
{
- if (requestedOrder != Ordering.FullOrder && currentOrder != Ordering.Same &&
- currentOrder != requestedOrder)
- return currentOrder;
- if (nt1.Equals(CmpEndMarker) && nt2.Equals(CmpEndMarker)) return currentOrder;
- // i1 is empty but i2 is not, so i1 can only be Before
- if (nt1.Equals(CmpEndMarker))
- return currentOrder == Ordering.After ? Ordering.Concurrent : Ordering.Before;
- // i2 is empty but i1 is not, so i1 can only be After
- if (nt2.Equals(CmpEndMarker))
- return currentOrder == Ordering.Before ? Ordering.Concurrent : Ordering.After;
- // compare the nodes
- var nc = nt1.Key.CompareTo(nt2.Key);
- if (nc == 0)
- {
- // both nodes exist compare the timestamps
- // same timestamp so just continue with the next nodes
- if (nt1.Value == nt2.Value)
- return compareNext(NextOrElse(i1, CmpEndMarker), NextOrElse(i2, CmpEndMarker), currentOrder);
- if (nt1.Value < nt2.Value)
- {
- // t1 is less than t2, so i1 can only be Before
- if (currentOrder == Ordering.After) return Ordering.Concurrent;
- return compareNext(NextOrElse(i1, CmpEndMarker), NextOrElse(i2, CmpEndMarker),
- Ordering.Before);
- }
- if (currentOrder == Ordering.Before) return Ordering.Concurrent;
- return compareNext(NextOrElse(i1, CmpEndMarker), NextOrElse(i2, CmpEndMarker), Ordering.After);
- }
- if (nc < 0)
+ // both nodes exist compare the timestamps
+ // same timestamp so just continue with the next nodes
+ if (nt1.Item2 == nt2.Item2) return CompareNext(NextOrElse(i1, CmpEndMarker), NextOrElse(i2, CmpEndMarker), currentOrder);
+ if (nt1.Item2 < nt2.Item2)
{
- // this node only exists in i1 so i1 can only be After
- if (currentOrder == Ordering.Before) return Ordering.Concurrent;
- return compareNext(NextOrElse(i1, CmpEndMarker), nt2, Ordering.After);
+ // t1 is less than t2, so i1 can only be Before
+ if (currentOrder == Ordering.After) return Ordering.Concurrent;
+ return CompareNext(NextOrElse(i1, CmpEndMarker), NextOrElse(i2, CmpEndMarker), Ordering.Before);
}
- // this node only exists in i2 so i1 can only be Before
- if (currentOrder == Ordering.After) return Ordering.Concurrent;
- return compareNext(nt1, NextOrElse(i2, CmpEndMarker), Ordering.Before);
- };
- return compareNext(NextOrElse(i1, CmpEndMarker), NextOrElse(i2, CmpEndMarker), Ordering.Same);
+ if (currentOrder == Ordering.Before) return Ordering.Concurrent;
+ return CompareNext(NextOrElse(i1, CmpEndMarker), NextOrElse(i2, CmpEndMarker), Ordering.After);
+ }
+
+ if (nc < 0)
+ {
+ // this node only exists in i1 so i1 can only be After
+ if (currentOrder == Ordering.Before) return Ordering.Concurrent;
+ return CompareNext(NextOrElse(i1, CmpEndMarker), nt2, Ordering.After);
+ }
+
+ // this node only exists in i2 so i1 can only be Before
+ if (currentOrder == Ordering.After) return Ordering.Concurrent;
+ return CompareNext(nt1, NextOrElse(i2, CmpEndMarker), Ordering.Before);
+ }
+
+ using(i1)
+ using(i2)
+ return CompareNext(NextOrElse(i1, CmpEndMarker), NextOrElse(i2, CmpEndMarker), Ordering.Same);
}
private static T NextOrElse(IEnumerator iter, T @default)