pekko/akka-distributed-data/src/test/scala/akka/cluster/ddata/ORSetSpec.scala

634 lines
23 KiB
Scala
Raw Normal View History

/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.cluster.ddata
import scala.collection.immutable.TreeMap
import akka.actor.Address
import akka.cluster.UniqueAddress
import akka.cluster.ddata.Replicator.Changed
import org.scalatest.Matchers
import org.scalatest.WordSpec
class ORSetSpec extends WordSpec with Matchers {
val node1 = UniqueAddress(Address("akka", "Sys", "localhost", 2551), 1L)
val node2 = UniqueAddress(node1.address.copy(port = Some(2552)), 2L)
val node3 = UniqueAddress(node1.address.copy(port = Some(2553)), 3L)
val nodeA = UniqueAddress(Address("akka", "Sys", "a", 2552), 1L)
val nodeB = UniqueAddress(nodeA.address.copy(host = Some("b")), 2L)
val nodeC = UniqueAddress(nodeA.address.copy(host = Some("c")), 3L)
val nodeD = UniqueAddress(nodeA.address.copy(host = Some("d")), 4L)
val nodeE = UniqueAddress(nodeA.address.copy(host = Some("e")), 5L)
val nodeF = UniqueAddress(nodeA.address.copy(host = Some("f")), 6L)
val nodeG = UniqueAddress(nodeA.address.copy(host = Some("g")), 7L)
val nodeH = UniqueAddress(nodeA.address.copy(host = Some("h")), 8L)
val user1 = """{"username":"john","password":"coltrane"}"""
val user2 = """{"username":"sonny","password":"rollins"}"""
val user3 = """{"username":"charlie","password":"parker"}"""
val user4 = """{"username":"charles","password":"mingus"}"""
"A ORSet" must {
"be able to add user" in {
val c1 = ORSet()
val c2 = c1.add(node1, user1)
val c3 = c2.add(node1, user2)
val c4 = c3.add(node1, user4)
val c5 = c4.add(node1, user3)
c5.elements should contain(user1)
c5.elements should contain(user2)
c5.elements should contain(user3)
c5.elements should contain(user4)
}
"be able to remove added user" in {
val c1 = ORSet()
val c2 = c1.add(node1, user1)
val c3 = c2.add(node1, user2)
val c4 = c3.remove(node1, user2)
val c5 = c4.remove(node1, user1)
c5.elements should not contain (user1)
c5.elements should not contain (user2)
=cdd #18328 use ancestor field, for fast forward merge AFTER: [info] Benchmark (set1Size) Mode Cnt Score Error Units [info] ORSetMergeBenchmark.mergeAddFromBothNodes 1 thrpt 10 717.362 ± 15.770 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 10 thrpt 10 144.862 ± 8.313 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 20 thrpt 10 96.004 ± 0.972 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 100 thrpt 10 18.735 ± 0.368 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 1 thrpt 10 1261.825 ± 51.717 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 10 thrpt 10 162.367 ± 21.443 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 20 thrpt 10 103.423 ± 1.569 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 100 thrpt 10 18.690 ± 0.642 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 1 thrpt 10 3666.086 ± 330.087 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 10 thrpt 10 2404.863 ± 136.244 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 20 thrpt 10 2423.596 ± 142.533 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 100 thrpt 10 2094.802 ± 161.307 ops/ms [info] ORSetMergeBenchmark.mergeComplex 1 thrpt 10 326.784 ± 6.665 ops/ms [info] ORSetMergeBenchmark.mergeComplex 10 thrpt 10 133.394 ± 4.749 ops/ms [info] ORSetMergeBenchmark.mergeComplex 20 thrpt 10 88.241 ± 1.733 ops/ms [info] ORSetMergeBenchmark.mergeComplex 100 thrpt 10 18.117 ± 0.543 ops/ms BEFORE: [info] Benchmark (set1Size) Mode Cnt Score Error Units [info] ORSetMergeBenchmark.mergeAddFromBothNodes 1 thrpt 10 737.646 ± 10.289 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 10 thrpt 10 146.706 ± 6.331 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 20 thrpt 10 95.553 ± 1.801 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 100 thrpt 10 18.321 ± 0.586 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 1 thrpt 10 1274.526 ± 23.732 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 10 thrpt 10 162.426 ± 20.490 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 20 thrpt 10 102.436 ± 2.435 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 100 thrpt 10 18.911 ± 0.659 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 1 thrpt 10 653.358 ± 71.232 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 10 thrpt 10 147.385 ± 2.750 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 20 thrpt 10 94.280 ± 0.894 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 100 thrpt 10 17.922 ± 1.522 ops/ms [info] ORSetMergeBenchmark.mergeComplex 1 thrpt 10 335.060 ± 8.385 ops/ms [info] ORSetMergeBenchmark.mergeComplex 10 thrpt 10 134.438 ± 3.044 ops/ms [info] ORSetMergeBenchmark.mergeComplex 20 thrpt 10 86.015 ± 2.145 ops/ms [info] ORSetMergeBenchmark.mergeComplex 100 thrpt 10 17.611 ± 0.136 ops/ms
2015-09-20 14:30:00 +02:00
val c6 = c3.merge(c5)
c6.elements should not contain (user1)
c6.elements should not contain (user2)
val c7 = c5.merge(c3)
c7.elements should not contain (user1)
c7.elements should not contain (user2)
}
"be able to add removed" in {
val c1 = ORSet()
val c2 = c1.remove(node1, user1)
val c3 = c2.add(node1, user1)
c3.elements should contain(user1)
val c4 = c3.remove(node1, user1)
c4.elements should not contain (user1)
val c5 = c4.add(node1, user1)
c5.elements should contain(user1)
}
"be able to remove and add several times" in {
val c1 = ORSet()
val c2 = c1.add(node1, user1)
val c3 = c2.add(node1, user2)
val c4 = c3.remove(node1, user1)
c4.elements should not contain (user1)
c4.elements should contain(user2)
val c5 = c4.add(node1, user1)
val c6 = c5.add(node1, user2)
c6.elements should contain(user1)
c6.elements should contain(user2)
val c7 = c6.remove(node1, user1)
val c8 = c7.add(node1, user2)
val c9 = c8.remove(node1, user1)
c9.elements should not contain (user1)
c9.elements should contain(user2)
}
"be able to have its user set correctly merged with another ORSet with unique user sets" in {
// set 1
val c1 = ORSet().add(node1, user1).add(node1, user2)
c1.elements should contain(user1)
c1.elements should contain(user2)
// set 2
val c2 = ORSet().add(node2, user3).add(node2, user4).remove(node2, user3)
c2.elements should not contain (user3)
c2.elements should contain(user4)
// merge both ways
2019-03-11 10:38:24 +01:00
val merged1 = c1.merge(c2)
merged1.elements should contain(user1)
merged1.elements should contain(user2)
merged1.elements should not contain (user3)
merged1.elements should contain(user4)
2019-03-11 10:38:24 +01:00
val merged2 = c2.merge(c1)
merged2.elements should contain(user1)
merged2.elements should contain(user2)
merged2.elements should not contain (user3)
merged2.elements should contain(user4)
}
"be able to have its user set correctly merged with another ORSet with overlapping user sets" in {
// set 1
val c1 = ORSet().add(node1, user1).add(node1, user2).add(node1, user3).remove(node1, user1).remove(node1, user3)
c1.elements should not contain (user1)
c1.elements should contain(user2)
c1.elements should not contain (user3)
// set 2
val c2 = ORSet().add(node2, user1).add(node2, user2).add(node2, user3).add(node2, user4).remove(node2, user3)
c2.elements should contain(user1)
c2.elements should contain(user2)
c2.elements should not contain (user3)
c2.elements should contain(user4)
// merge both ways
2019-03-11 10:38:24 +01:00
val merged1 = c1.merge(c2)
merged1.elements should contain(user1)
merged1.elements should contain(user2)
merged1.elements should not contain (user3)
merged1.elements should contain(user4)
2019-03-11 10:38:24 +01:00
val merged2 = c2.merge(c1)
merged2.elements should contain(user1)
merged2.elements should contain(user2)
merged2.elements should not contain (user3)
merged2.elements should contain(user4)
}
"be able to have its user set correctly merged for concurrent updates" in {
val c1 = ORSet().add(node1, user1).add(node1, user2).add(node1, user3)
c1.elements should contain(user1)
c1.elements should contain(user2)
c1.elements should contain(user3)
val c2 = c1.add(node2, user1).remove(node2, user2).remove(node2, user3)
c2.elements should contain(user1)
c2.elements should not contain (user2)
c2.elements should not contain (user3)
// merge both ways
2019-03-11 10:38:24 +01:00
val merged1 = c1.merge(c2)
merged1.elements should contain(user1)
merged1.elements should not contain (user2)
merged1.elements should not contain (user3)
2019-03-11 10:38:24 +01:00
val merged2 = c2.merge(c1)
merged2.elements should contain(user1)
merged2.elements should not contain (user2)
merged2.elements should not contain (user3)
val c3 = c1.add(node1, user4).remove(node1, user3).add(node1, user2)
// merge both ways
2019-03-11 10:38:24 +01:00
val merged3 = c2.merge(c3)
merged3.elements should contain(user1)
merged3.elements should contain(user2)
merged3.elements should not contain (user3)
merged3.elements should contain(user4)
2019-03-11 10:38:24 +01:00
val merged4 = c3.merge(c2)
merged4.elements should contain(user1)
merged4.elements should contain(user2)
merged4.elements should not contain (user3)
merged4.elements should contain(user4)
}
"be able to have its user set correctly merged after remove" in {
val c1 = ORSet().add(node1, user1).add(node1, user2)
val c2 = c1.remove(node2, user2)
// merge both ways
2019-03-11 10:38:24 +01:00
val merged1 = c1.merge(c2)
merged1.elements should contain(user1)
merged1.elements should not contain (user2)
2019-03-11 10:38:24 +01:00
val merged2 = c2.merge(c1)
merged2.elements should contain(user1)
merged2.elements should not contain (user2)
val c3 = c1.add(node1, user3)
// merge both ways
2019-03-11 10:38:24 +01:00
val merged3 = c3.merge(c2)
merged3.elements should contain(user1)
merged3.elements should not contain (user2)
merged3.elements should contain(user3)
2019-03-11 10:38:24 +01:00
val merged4 = c2.merge(c3)
merged4.elements should contain(user1)
merged4.elements should not contain (user2)
merged4.elements should contain(user3)
}
}
"ORSet deltas" must {
def addDeltaOp(s: ORSet[String]): ORSet.AddDeltaOp[String] =
asAddDeltaOp(s.delta.get)
def asAddDeltaOp(delta: Any): ORSet.AddDeltaOp[String] =
delta match {
case d: ORSet.AddDeltaOp[String] @unchecked => d
case _ => throw new IllegalArgumentException("Expected AddDeltaOp")
}
"work for additions" in {
val s1 = ORSet.empty[String]
val s2 = s1.add(node1, "a")
addDeltaOp(s2).underlying.elements should ===(Set("a"))
s1.mergeDelta(s2.delta.get) should ===(s2)
val s3 = s2.resetDelta.add(node1, "b").add(node1, "c")
addDeltaOp(s3).underlying.elements should ===(Set("b", "c"))
s2.mergeDelta(s3.delta.get) should ===(s3)
// another node adds "d"
val s4 = s3.resetDelta.add(node2, "d")
addDeltaOp(s4).underlying.elements should ===(Set("d"))
s3.mergeDelta(s4.delta.get) should ===(s4)
// concurrent update
val s5 = s3.resetDelta.add(node1, "e")
val s6 = s5.merge(s4)
s5.mergeDelta(s4.delta.get) should ===(s6)
// concurrent add of same element
val s7 = s3.resetDelta.add(node1, "d")
val s8 = s7.merge(s4)
// the dot contains both nodes
s8.elementsMap("d").contains(node1)
s8.elementsMap("d").contains(node2)
// and same result when merging the deltas
s7.mergeDelta(s4.delta.get) should ===(s8)
s4.mergeDelta(s7.delta.get) should ===(s8)
}
"handle another concurrent add scenario" in {
val s1 = ORSet.empty[String]
val s2 = s1.add(node1, "a")
val s3 = s2.add(node1, "b")
val s4 = s2.add(node2, "c")
// full state merge for reference
val s5 = s4.merge(s3)
s5.elements should ===(Set("a", "b", "c"))
val s6 = s4.mergeDelta(s3.delta.get)
s6.elements should ===(Set("a", "b", "c"))
}
"merge deltas into delta groups" in {
val s1 = ORSet.empty[String]
val s2 = s1.add(node1, "a")
val d2 = s2.delta.get
val s3 = s2.resetDelta.add(node1, "b")
val d3 = s3.delta.get
2019-03-11 10:38:24 +01:00
val d4 = d2.merge(d3)
asAddDeltaOp(d4).underlying.elements should ===(Set("a", "b"))
s1.mergeDelta(d4) should ===(s3)
s2.mergeDelta(d4) should ===(s3)
val s5 = s3.resetDelta.remove(node1, "b")
val d5 = s5.delta.get
2019-03-11 10:38:24 +01:00
val d6 = d4.merge(d5).asInstanceOf[ORSet.DeltaGroup[String]]
d6.ops.last.getClass should ===(classOf[ORSet.RemoveDeltaOp[String]])
d6.ops.size should ===(2)
s3.mergeDelta(d6) should ===(s5)
val s7 = s5.resetDelta.add(node1, "c")
val s8 = s7.resetDelta.add(node1, "d")
2019-03-11 10:38:24 +01:00
val d9 = d6.merge(s7.delta.get).merge(s8.delta.get).asInstanceOf[ORSet.DeltaGroup[String]]
// the add "c" and add "d" are merged into one AddDeltaOp
asAddDeltaOp(d9.ops.last).underlying.elements should ===(Set("c", "d"))
d9.ops.size should ===(3)
s5.mergeDelta(d9) should ===(s8)
s5.mergeDelta(s7.delta.get).mergeDelta(s8.delta.get) should ===(s8)
}
"work for removals" in {
val s1 = ORSet.empty[String]
val s2 = s1.add(node1, "a").add(node1, "b").resetDelta
val s3 = s2.remove(node1, "b")
s2.merge(s3) should ===(s3)
s2.mergeDelta(s3.delta.get) should ===(s3)
s2.mergeDelta(s3.delta.get).elements should ===(Set("a"))
// concurrent update
val s4 = s2.add(node2, "c").resetDelta
val s5 = s4.merge(s3)
s5.elements should ===(Set("a", "c"))
s4.mergeDelta(s3.delta.get) should ===(s5)
// add "b" again
val s6 = s5.add(node2, "b")
// merging the old delta should not remove it
s6.mergeDelta(s3.delta.get) should ===(s6)
s6.mergeDelta(s3.delta.get).elements should ===(Set("a", "b", "c"))
}
"work for clear" in {
val s1 = ORSet.empty[String]
val s2 = s1.add(node1, "a").add(node1, "b")
val s3 = s2.resetDelta.clear()
val s4 = s3.resetDelta.add(node1, "c")
s2.merge(s3) should ===(s3)
s2.mergeDelta(s3.delta.get) should ===(s3)
val s5 = s2.mergeDelta(s3.delta.get).mergeDelta(s4.delta.get)
s5.elements should ===(Set("c"))
s5 should ===(s4)
// concurrent update
val s6 = s2.resetDelta.add(node2, "d")
val s7 = s6.merge(s3)
s7.elements should ===(Set("d"))
s6.mergeDelta(s3.delta.get) should ===(s7)
// add "b" again
val s8 = s7.add(node2, "b")
// merging the old delta should not remove it
s8.mergeDelta(s3.delta.get) should ===(s8)
s8.mergeDelta(s3.delta.get).elements should ===(Set("b", "d"))
}
"handle a mixed add/remove scenario" in {
val s1 = ORSet.empty[String]
val s2 = s1.resetDelta.remove(node1, "e")
val s3 = s2.resetDelta.add(node1, "b")
val s4 = s3.resetDelta.add(node1, "a")
val s5 = s4.resetDelta.remove(node1, "b")
2019-03-11 10:38:24 +01:00
val deltaGroup1 = s3.delta.get.merge(s4.delta.get).merge(s5.delta.get)
2019-03-11 10:38:24 +01:00
val s7 = s2.mergeDelta(deltaGroup1)
s7.elements should ===(Set("a"))
// The above scenario was constructed from failing ReplicatorDeltaSpec,
// some more checks...
val s8 = s2.resetDelta.add(node2, "z") // concurrent update from node2
2019-03-11 10:38:24 +01:00
val s9 = s8.mergeDelta(deltaGroup1)
s9.elements should ===(Set("a", "z"))
}
"handle a mixed add/remove scenario 2" in {
val s1 = ORSet.empty[String]
val s2 = s1.resetDelta.add(node1, "a")
val s3 = s2.resetDelta.add(node1, "b")
val s4 = s3.resetDelta.add(node2, "a")
val s5 = s4.resetDelta.remove(node1, "a")
s5.elements should ===(Set("b"))
2019-03-11 10:38:24 +01:00
val delta1 = s2.delta.get.merge(s3.delta.get)
val delta2 = s4.delta.get
val t1 = ORSet.empty[String]
val t2 = t1.mergeDelta(delta1).mergeDelta(delta2)
t2.elements should ===(Set("a", "b"))
val t3 = t2.resetDelta.add(node3, "z")
val t4 = t3.mergeDelta(s5.delta.get)
t4.elements should ===(Set("b", "z"))
}
"handle a mixed add/remove scenario 3" in {
val s1 = ORSet.empty[String]
val s2 = s1.resetDelta.add(node1, "a")
val s3 = s2.resetDelta.add(node1, "b")
val s4 = s3.resetDelta.add(node2, "a")
val s5 = s4.resetDelta.remove(node1, "a")
s5.elements should ===(Set("b"))
2019-03-11 10:38:24 +01:00
val delta1 = s2.delta.get.merge(s3.delta.get)
val t1 = ORSet.empty[String]
val t2 = t1.mergeDelta(delta1)
t2.elements should ===(Set("a", "b"))
val t3 = t2.resetDelta.add(node3, "a")
val t4 = t3.mergeDelta(s5.delta.get)
t4.elements should ===(Set("b", "a"))
}
"not have anomalies for ORSet in complex but realistic scenario" in {
val node1_1 = ORSet.empty[String].add(node1, "q").remove(node1, "q")
val delta1_1 = node1_1.delta.get
val node1_2 = node1_1.resetDelta.resetDelta.add(node1, "z").remove(node1, "z")
val delta1_2 = node1_2.delta.get
// we finished doing stuff on node1 - there are two separate deltas that will be propagated
// node2 is created, then gets first delta from node1 and then adds an element "x"
val node2_1 = ORSet.empty[String].mergeDelta(delta1_1).resetDelta.add(node2, "x")
val delta2_1 = node2_1.delta.get
// node2 continues its existence adding and later removing the element
// it still didn't get the second update from node1 (that is fully legit :) )
val node2_2 = node2_1.resetDelta.add(node2, "a").remove(node2, "a")
val delta2_2 = node2_2.delta.get
// in the meantime there is some node3
// there is not much activity on it, it just gets the first delta from node1 then it gets
// first delta from node2
// then it gets the second delta from node1 (that node2 still didn't get, but, hey!, this is fine)
val node3_1 = ORSet.empty[String].mergeDelta(delta1_1).mergeDelta(delta2_1).mergeDelta(delta1_2)
// and node3_1 receives full update from node2 via gossip
2019-03-11 10:38:24 +01:00
val merged1 = node3_1.merge(node2_2)
merged1.contains("a") should be(false)
// and node3_1 receives delta update from node2 (it just needs to get the second delta,
// as it already got the first delta just a second ago)
2019-03-11 10:38:24 +01:00
val merged2 = node3_1.mergeDelta(delta2_2)
val ORSet(mg2) = merged2
mg2 should be(Set("x")) // !!!
}
"require causal delivery of deltas" in {
// This test illustrates why we need causal delivery of deltas.
// Otherwise the following could happen.
// s0 is the stable state that is initially replicated to all nodes
val s0 = ORSet.empty[String].add(node1, "a")
// add element "b" and "c" at node1
val s11 = s0.resetDelta.add(node1, "b")
val s12 = s11.resetDelta.add(node1, "c")
// at the same time, add element "d" at node2
val s21 = s0.resetDelta.add(node2, "d")
// node3 receives delta for "d" and "c", but the delta for "b" is lost
2019-03-11 10:38:24 +01:00
val s31 = s0.mergeDelta(s21.delta.get).mergeDelta(s12.delta.get)
s31.elements should ===(Set("a", "c", "d"))
// node4 receives all deltas
2019-03-11 10:38:24 +01:00
val s41 = s0.mergeDelta(s11.delta.get).mergeDelta(s12.delta.get).mergeDelta(s21.delta.get)
s41.elements should ===(Set("a", "b", "c", "d"))
// node3 and node4 sync with full state gossip
2019-03-11 10:38:24 +01:00
val s32 = s31.merge(s41)
// one would expect elements "a", "b", "c", "d", but "b" is removed
// because we applied s12.delta without applying s11.delta
s32.elements should ===(Set("a", "c", "d"))
}
}
"ORSet unit test" must {
"verify subtractDots" in {
val dot = VersionVector(TreeMap(nodeA -> 3L, nodeB -> 2L, nodeD -> 14L, nodeG -> 22L))
2019-03-11 10:38:24 +01:00
val vvector =
VersionVector(TreeMap(nodeA -> 4L, nodeB -> 1L, nodeC -> 1L, nodeD -> 14L, nodeE -> 5L, nodeF -> 2L))
val expected = VersionVector(TreeMap(nodeB -> 2L, nodeG -> 22L))
ORSet.subtractDots(dot, vvector) should be(expected)
}
"verify mergeCommonKeys" in {
val commonKeys: Set[String] = Set("K1", "K2")
val thisDot1 = VersionVector(TreeMap(nodeA -> 3L, nodeD -> 7L))
val thisDot2 = VersionVector(TreeMap(nodeB -> 5L, nodeC -> 2L))
val thisVvector = VersionVector(TreeMap(nodeA -> 3L, nodeB -> 5L, nodeC -> 2L, nodeD -> 7L))
2019-03-11 10:38:24 +01:00
val thisSet = new ORSet(elementsMap = Map("K1" -> thisDot1, "K2" -> thisDot2), vvector = thisVvector)
!cdd #18328 optimize VersionVector for size 1 (typical dots) AFTER: [info] Benchmark (set1Size) Mode Cnt Score Error Units [info] ORSetMergeBenchmark.mergeAddFromBothNodes 1 thrpt 10 2007.939 ± 74.673 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 10 thrpt 10 337.110 ± 15.055 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 20 thrpt 10 223.600 ± 8.403 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 100 thrpt 10 46.697 ± 2.136 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 1 thrpt 10 2542.537 ± 120.697 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 10 thrpt 10 365.694 ± 17.571 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 20 thrpt 10 216.323 ± 9.446 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 100 thrpt 10 49.563 ± 2.725 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 1 thrpt 10 9883.186 ± 725.672 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 10 thrpt 10 3266.528 ± 189.993 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 20 thrpt 10 3206.017 ± 124.623 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 100 thrpt 10 2709.031 ± 162.182 ops/ms [info] ORSetMergeBenchmark.mergeComplex 1 thrpt 10 572.704 ± 21.504 ops/ms [info] ORSetMergeBenchmark.mergeComplex 10 thrpt 10 249.226 ± 12.324 ops/ms [info] ORSetMergeBenchmark.mergeComplex 20 thrpt 10 170.560 ± 10.320 ops/ms [info] ORSetMergeBenchmark.mergeComplex 100 thrpt 10 46.373 ± 1.800 ops/ms BEFORE: [info] Benchmark (set1Size) Mode Cnt Score Error Units [info] ORSetMergeBenchmark.mergeAddFromBothNodes 1 thrpt 10 885.664 ± 99.718 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 10 thrpt 10 304.617 ± 4.755 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 20 thrpt 10 200.977 ± 3.708 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 100 thrpt 10 47.879 ± 4.352 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 1 thrpt 10 1586.848 ± 27.476 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 10 thrpt 10 354.408 ± 4.772 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 20 thrpt 10 210.563 ± 32.914 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 100 thrpt 10 52.750 ± 0.698 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 1 thrpt 10 3915.817 ± 420.643 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 10 thrpt 10 2369.476 ± 250.336 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 20 thrpt 10 2378.924 ± 47.160 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 100 thrpt 10 2167.841 ± 20.339 ops/ms [info] ORSetMergeBenchmark.mergeComplex 1 thrpt 10 387.261 ± 8.820 ops/ms [info] ORSetMergeBenchmark.mergeComplex 10 thrpt 10 212.661 ± 4.802 ops/ms [info] ORSetMergeBenchmark.mergeComplex 20 thrpt 10 151.512 ± 2.627 ops/ms [info] ORSetMergeBenchmark.mergeComplex 100 thrpt 10 40.976 ± 2.014 ops/ms * use subtype polymorphism for VersionVector tmp
2015-09-21 13:09:19 +02:00
val thatDot1 = VersionVector(nodeA, 3L)
val thatDot2 = VersionVector(nodeB, 6L)
val thatVvector = VersionVector(TreeMap(nodeA -> 3L, nodeB -> 6L, nodeC -> 1L, nodeD -> 8L))
2019-03-11 10:38:24 +01:00
val thatSet = new ORSet(elementsMap = Map("K1" -> thatDot1, "K2" -> thatDot2), vvector = thatVvector)
2019-03-11 10:38:24 +01:00
val expectedDots = Map("K1" -> VersionVector(nodeA, 3L), "K2" -> VersionVector(TreeMap(nodeB -> 6L, nodeC -> 2L)))
ORSet.mergeCommonKeys(commonKeys, thisSet, thatSet) should be(expectedDots)
}
"verify mergeDisjointKeys" in {
val keys: Set[Any] = Set("K3", "K4", "K5")
2019-03-13 10:56:20 +01:00
val elements: Map[Any, VersionVector] = Map(
"K3" -> VersionVector(nodeA, 4L),
"K4" -> VersionVector(TreeMap(nodeA -> 3L, nodeD -> 8L)),
"K5" -> VersionVector(nodeA, 2L))
val vvector = VersionVector(TreeMap(nodeA -> 3L, nodeD -> 7L))
val acc: Map[Any, VersionVector] = Map("K1" -> VersionVector(nodeA, 3L))
2019-03-11 10:38:24 +01:00
val expectedDots = acc ++ Map("K3" -> VersionVector(nodeA, 4L), "K4" -> VersionVector(nodeD, 8L)) // "a" -> 3 removed, optimized to include only those unseen
ORSet.mergeDisjointKeys(keys, elements, vvector, acc) should be(expectedDots)
}
"verify disjoint merge" in {
val a1 = ORSet().add(node1, "bar")
val b1 = ORSet().add(node2, "baz")
val c = a1.merge(b1)
val a2 = a1.remove(node1, "bar")
val d = a2.merge(c)
d.elements should be(Set("baz"))
}
"verify removed after merge" in {
// Add Z at node1 replica
val a = ORSet().add(node1, "Z")
// Replicate it to some node3, i.e. it has dot 'Z'->{node1 -> 1}
val c = a
// Remove Z at node1 replica
val a2 = a.remove(node1, "Z")
// Add Z at node2, a new replica
val b = ORSet().add(node2, "Z")
// Replicate b to node1, so now node1 has a Z, the one with a Dot of
// {node2 -> 1} and version vector of [{node1 -> 1}, {node2 -> 1}]
val a3 = b.merge(a2)
a3.elements should be(Set("Z"))
// Remove the 'Z' at node2 replica
val b2 = b.remove(node2, "Z")
// Both node3 (c) and node1 (a3) have a 'Z', but when they merge, there should be
// no 'Z' as node3 (c)'s has been removed by node1 and node1 (a3)'s has been removed by
// node2
c.elements should be(Set("Z"))
a3.elements should be(Set("Z"))
b2.elements should be(Set())
a3.merge(c).merge(b2).elements should be(Set.empty)
a3.merge(b2).merge(c).elements should be(Set.empty)
c.merge(b2).merge(a3).elements should be(Set.empty)
c.merge(a3).merge(b2).elements should be(Set.empty)
b2.merge(c).merge(a3).elements should be(Set.empty)
b2.merge(a3).merge(c).elements should be(Set.empty)
}
"verify removed after merge 2" in {
val a = ORSet().add(node1, "Z")
val b = ORSet().add(node2, "Z")
// replicate node3
val c = a
val a2 = a.remove(node1, "Z")
// replicate b to node1, now node1 has node2's 'Z'
val a3 = a2.merge(b)
a3.elements should be(Set("Z"))
// Remove node2's 'Z'
val b2 = b.remove(node2, "Z")
// Replicate c to node2, now node2 has node1's old 'Z'
val b3 = b2.merge(c)
b3.elements should be(Set("Z"))
// Merge everytyhing
a3.merge(c).merge(b3).elements should be(Set.empty)
a3.merge(b3).merge(c).elements should be(Set.empty)
c.merge(b3).merge(a3).elements should be(Set.empty)
c.merge(a3).merge(b3).elements should be(Set.empty)
b3.merge(c).merge(a3).elements should be(Set.empty)
b3.merge(a3).merge(c).elements should be(Set.empty)
}
"not pollute the vvector of result during mergeRemoveDelta" in {
val a = ORSet().add(nodeA, "a")
val a1 = a.add(nodeB, "b").remove(nodeB, "b").resetDelta.remove(nodeA, "a") // a1 now contains dot from nodeB
a.vvector.contains(nodeA) should be(true)
a.vvector.contains(nodeB) should be(false)
a1.vvector.contains(nodeA) should be(true)
a1.vvector.contains(nodeB) should be(true)
val a2 = ORSet().mergeDelta(a.delta.get).mergeDelta(a1.delta.get)
a2.elements should be(Set.empty)
a2.vvector.contains(nodeB) should be(false) // a2 should not be polluted by the nodeB dot, as all operations on it pertained only to elements from nodeA
}
"have unapply extractor" in {
val s1 = ORSet.empty.add(node1, "a").add(node2, "b")
val _: ORSet[String] = s1
val ORSet(elements1) = s1 // `unapply[A](s: ORSet[A])` is used here
val elements2: Set[String] = elements1
elements2 should be(Set("a", "b"))
Changed(ORSetKey[String]("key"))(s1) match {
case c @ Changed(ORSetKey("key")) =>
val _: ORSet[String] = c.dataValue
val ORSet(elements3) = c.dataValue
val elements4: Set[String] = elements3
elements4 should be(Set("a", "b"))
case changed =>
fail(s"Failed to match [$changed]")
}
val msg: Any = Changed(ORSetKey[String]("key"))(s1)
msg match {
case c @ Changed(ORSetKey("key")) =>
val ORSet(elements3) = c.dataValue // `unapply(a: ReplicatedData)` is used here
// if `unapply(a: ReplicatedData)` isn't defined the next line doesn't compile:
// type mismatch; found : scala.collection.immutable.Set[A] where type A required: Set[Any] Note: A <: Any,
// but trait Set is invariant in type A. You may wish to investigate a wildcard type such as _ <: Any. (SLS 3.2.10)
val elements4: Set[Any] = elements3
elements4 should be(Set("a", "b"))
case changed =>
fail(s"Failed to match [$changed]")
}
}
}
}