fixes to ORSet mergeRemoveDelta and ORMap deltaMerge (#22648)

This commit is contained in:
gosubpl 2017-03-31 13:29:27 +02:00
parent d3de9d40cd
commit 3a8eef4506
11 changed files with 421 additions and 150 deletions

View file

@ -6,8 +6,7 @@ package akka.cluster.ddata
import java.util.concurrent.ThreadLocalRandom
import scala.concurrent.duration._
import akka.cluster.Cluster
import akka.cluster.{ Cluster, ddata }
import akka.cluster.ddata.Replicator._
import akka.remote.testconductor.RoleName
import akka.remote.testkit.MultiNodeConfig
@ -38,21 +37,38 @@ object ReplicatorMapDeltaSpec extends MultiNodeConfig {
final case class Delay(n: Int) extends Op
final case class Incr(ki: (PNCounterMapKey[String], String), n: Int, consistency: WriteConsistency) extends Op
final case class Decr(ki: (PNCounterMapKey[String], String), n: Int, consistency: WriteConsistency) extends Op
final case class Add(ki: (ORMultiMapKey[String, String], String), elem: String, consistency: WriteConsistency) extends Op
final case class Remove(ki: (ORMultiMapKey[String, String], String), elem: String, consistency: WriteConsistency) extends Op
// AddVD and RemoveVD for variant of ORMultiMap with Value Deltas, NoVD - for the vanilla ORMultiMap
final case class AddVD(ki: (ORMultiMapKey[String, String], String), elem: String, consistency: WriteConsistency) extends Op
final case class RemoveVD(ki: (ORMultiMapKey[String, String], String), elem: String, consistency: WriteConsistency) extends Op
final case class AddNoVD(ki: (ORMultiMapKey[String, String], String), elem: String, consistency: WriteConsistency) extends Op
final case class RemoveNoVD(ki: (ORMultiMapKey[String, String], String), elem: String, consistency: WriteConsistency) extends Op
// AddOM and RemoveOM for Vanilla ORMap holding ORSet inside
final case class AddOM(ki: (ORMapKey[String, ORSet[String]], String), elem: String, consistency: WriteConsistency) extends Op
final case class RemoveOM(ki: (ORMapKey[String, ORSet[String]], String), elem: String, consistency: WriteConsistency) extends Op
val timeout = 5.seconds
val writeTwo = WriteTo(2, timeout)
val writeMajority = WriteMajority(timeout)
val KeyPN = PNCounterMapKey[String]("A")
val KeyMM = ORMultiMapKey[String, String]("D")
// VD and NoVD as above
val KeyMMVD = ORMultiMapKey[String, String]("D")
val KeyMMNoVD = ORMultiMapKey[String, String]("G")
// OM as above
val KeyOM = ORMapKey[String, ORSet[String]]("J")
val KeyA: (PNCounterMapKey[String], String) = (KeyPN, "a")
val KeyB: (PNCounterMapKey[String], String) = (KeyPN, "b")
val KeyC: (PNCounterMapKey[String], String) = (KeyPN, "c")
val KeyD: (ORMultiMapKey[String, String], String) = (KeyMM, "d")
val KeyE: (ORMultiMapKey[String, String], String) = (KeyMM, "e")
val KeyF: (ORMultiMapKey[String, String], String) = (KeyMM, "f")
val KeyD: (ORMultiMapKey[String, String], String) = (KeyMMVD, "d")
val KeyE: (ORMultiMapKey[String, String], String) = (KeyMMVD, "e")
val KeyF: (ORMultiMapKey[String, String], String) = (KeyMMVD, "f")
val KeyG: (ORMultiMapKey[String, String], String) = (KeyMMNoVD, "g")
val KeyH: (ORMultiMapKey[String, String], String) = (KeyMMNoVD, "h")
val KeyI: (ORMultiMapKey[String, String], String) = (KeyMMNoVD, "i")
val KeyJ: (ORMapKey[String, ORSet[String]], String) = (KeyOM, "j")
val KeyK: (ORMapKey[String, ORSet[String]], String) = (KeyOM, "k")
val KeyL: (ORMapKey[String, ORSet[String]], String) = (KeyOM, "l")
def generateOperations(onNode: RoleName): Vector[Op] = {
val rnd = ThreadLocalRandom.current()
@ -73,7 +89,7 @@ object ReplicatorMapDeltaSpec extends MultiNodeConfig {
}
}
def rndOrSetkey(): (ORMultiMapKey[String, String], String) = {
def rndOrSetkeyVD(): (ORMultiMapKey[String, String], String) = {
rnd.nextInt(3) match {
case 0 KeyD
case 1 KeyE
@ -81,6 +97,22 @@ object ReplicatorMapDeltaSpec extends MultiNodeConfig {
}
}
def rndOrSetkeyNoVD(): (ORMultiMapKey[String, String], String) = {
rnd.nextInt(3) match {
case 0 KeyG
case 1 KeyH
case 2 KeyI
}
}
def rndOrSetkeyOM(): (ORMapKey[String, ORSet[String]], String) = {
rnd.nextInt(3) match {
case 0 KeyJ
case 1 KeyK
case 2 KeyL
}
}
var availableForRemove = Set.empty[String]
def rndAddElement(): String = {
@ -97,24 +129,44 @@ object ReplicatorMapDeltaSpec extends MultiNodeConfig {
availableForRemove.toVector(rnd.nextInt(availableForRemove.size))
}
(0 to (30 + rnd.nextInt(10))).map { _
rnd.nextInt(4) match {
(0 to (50 + rnd.nextInt(10))).map { _
rnd.nextInt(6) match {
case 0 Delay(rnd.nextInt(500))
case 1 Incr(rndPnCounterkey(), rnd.nextInt(100), consistency())
case 2 Decr(rndPnCounterkey(), rnd.nextInt(10), consistency())
case 3
// ORSet
val key = rndOrSetkey()
// FIXME use full state for removals, until issue #22648 is fixed
// // only removals for KeyF on node first
// if (key == KeyF && onNode == first && rnd.nextBoolean())
// Remove(key, rndRemoveElement(), consistency())
// else
Add(key, rndAddElement(), consistency())
// ORMultiMap.withValueDeltas
val key = rndOrSetkeyVD()
// only removals for KeyF on node first
if (key == KeyF && onNode == first && rnd.nextBoolean())
RemoveVD(key, rndRemoveElement(), consistency())
else
AddVD(key, rndAddElement(), consistency())
case 4
// ORMultiMap - vanilla variant - without Value Deltas
val key = rndOrSetkeyNoVD()
// only removals for KeyI on node first
if (key == KeyI && onNode == first && rnd.nextBoolean())
RemoveNoVD(key, rndRemoveElement(), consistency())
else
AddNoVD(key, rndAddElement(), consistency())
case 5
// Vanilla ORMap - with ORSet inside
val key = rndOrSetkeyOM()
// only removals for KeyL on node first
if (key == KeyL && onNode == first && rnd.nextBoolean())
RemoveOM(key, rndRemoveElement(), consistency())
else
AddOM(key, rndAddElement(), consistency())
}
}.toVector
}
def addElementToORMap(om: ORMap[String, ORSet[String]], key: String, element: String)(implicit node: Cluster) =
om.updated(node, key, ORSet.empty[String])(_.add(node, element))
def removeElementFromORMap(om: ORMap[String, ORSet[String]], key: String, element: String)(implicit node: Cluster) =
om.updated(node, key, ORSet.empty[String])(_.remove(node, element))
}
class ReplicatorMapDeltaSpecMultiJvmNode1 extends ReplicatorMapDeltaSpec
@ -191,6 +243,14 @@ class ReplicatorMapDeltaSpec extends MultiNodeSpec(ReplicatorMapDeltaSpec) with
fullStateReplicator ! Update(key._1, ORMultiMap.emptyWithValueDeltas[String, String], WriteLocal)(_ + (key._2 Set("a")))
deltaReplicator ! Update(key._1, ORMultiMap.emptyWithValueDeltas[String, String], WriteLocal)(_ + (key._2 Set("a")))
}
List(KeyG, KeyH, KeyI).foreach { key
fullStateReplicator ! Update(key._1, ORMultiMap.empty[String, String], WriteLocal)(_ + (key._2 Set("a")))
deltaReplicator ! Update(key._1, ORMultiMap.empty[String, String], WriteLocal)(_ + (key._2 Set("a")))
}
List(KeyJ, KeyK, KeyL).foreach { key
fullStateReplicator ! Update(key._1, ORMap.empty[String, ORSet[String]], WriteLocal)(_ + (key._2 (ORSet.empty + "a")))
deltaReplicator ! Update(key._1, ORMap.empty[String, ORSet[String]], WriteLocal)(_ + (key._2 (ORSet.empty + "a")))
}
}
enterBarrier("updated-1")
@ -205,10 +265,25 @@ class ReplicatorMapDeltaSpec extends MultiNodeSpec(ReplicatorMapDeltaSpec) with
awaitAssert {
val p = TestProbe()
List(KeyD, KeyE, KeyF).foreach { key
fullStateReplicator.tell(Get(key._1, ReadLocal), p.ref)
val res = p.expectMsgType[GetSuccess[ORMultiMap[String, String]]].dataValue.get(key._2) should ===(Some(Set("a")))
}
}
awaitAssert {
val p = TestProbe()
List(KeyG, KeyH, KeyI).foreach { key
fullStateReplicator.tell(Get(key._1, ReadLocal), p.ref)
p.expectMsgType[GetSuccess[ORMultiMap[String, String]]].dataValue.get(key._2) should ===(Some(Set("a")))
}
}
awaitAssert {
val p = TestProbe()
List(KeyJ, KeyK, KeyL).foreach { key
fullStateReplicator.tell(Get(key._1, ReadLocal), p.ref)
val res = p.expectMsgType[GetSuccess[ORMap[String, ORSet[String]]]].dataValue.get(key._2)
res.map(_.elements) should ===(Some(Set("a")))
}
}
}
enterBarrierAfterTestStep()
@ -231,18 +306,42 @@ class ReplicatorMapDeltaSpec extends MultiNodeSpec(ReplicatorMapDeltaSpec) with
case Decr(key, n, consistency)
fullStateReplicator ! Update(key._1, PNCounterMap.empty[String], WriteLocal)(_ decrement (key._2, n))
deltaReplicator ! Update(key._1, PNCounterMap.empty[String], WriteLocal)(_ decrement (key._2, n))
case Add(key, elem, consistency)
case AddVD(key, elem, consistency)
// to have an deterministic result when mixing add/remove we can only perform
// the ORSet operations from one node
runOn((if (key == KeyF) List(first) else List(first, second, third)): _*) {
fullStateReplicator ! Update(key._1, ORMultiMap.emptyWithValueDeltas[String, String], WriteLocal)(_ addBinding (key._2, elem))
deltaReplicator ! Update(key._1, ORMultiMap.emptyWithValueDeltas[String, String], WriteLocal)(_ addBinding (key._2, elem))
}
case Remove(key, elem, consistency)
case RemoveVD(key, elem, consistency)
runOn(first) {
fullStateReplicator ! Update(key._1, ORMultiMap.emptyWithValueDeltas[String, String], WriteLocal)(_ removeBinding (key._2, elem))
deltaReplicator ! Update(key._1, ORMultiMap.emptyWithValueDeltas[String, String], WriteLocal)(_ removeBinding (key._2, elem))
}
case AddNoVD(key, elem, consistency)
// to have an deterministic result when mixing add/remove we can only perform
// the ORSet operations from one node
runOn((if (key == KeyI) List(first) else List(first, second, third)): _*) {
fullStateReplicator ! Update(key._1, ORMultiMap.empty[String, String], WriteLocal)(_ addBinding (key._2, elem))
deltaReplicator ! Update(key._1, ORMultiMap.empty[String, String], WriteLocal)(_ addBinding (key._2, elem))
}
case RemoveNoVD(key, elem, consistency)
runOn(first) {
fullStateReplicator ! Update(key._1, ORMultiMap.empty[String, String], WriteLocal)(_ removeBinding (key._2, elem))
deltaReplicator ! Update(key._1, ORMultiMap.empty[String, String], WriteLocal)(_ removeBinding (key._2, elem))
}
case AddOM(key, elem, consistency)
// to have an deterministic result when mixing add/remove we can only perform
// the ORSet operations from one node
runOn((if (key == KeyL) List(first) else List(first, second, third)): _*) {
fullStateReplicator ! Update(key._1, ORMap.empty[String, ORSet[String]], WriteLocal)(om addElementToORMap(om, key._2, elem))
deltaReplicator ! Update(key._1, ORMap.empty[String, ORSet[String]], WriteLocal)(om addElementToORMap(om, key._2, elem))
}
case RemoveOM(key, elem, consistency)
runOn(first) {
fullStateReplicator ! Update(key._1, ORMap.empty[String, ORSet[String]], WriteLocal)(om removeElementFromORMap(om, key._2, elem))
deltaReplicator ! Update(key._1, ORMap.empty[String, ORSet[String]], WriteLocal)(om removeElementFromORMap(om, key._2, elem))
}
}
}
@ -274,6 +373,32 @@ class ReplicatorMapDeltaSpec extends MultiNodeSpec(ReplicatorMapDeltaSpec) with
}
}
List(KeyG, KeyH, KeyI).foreach { key
within(5.seconds) {
awaitAssert {
val p = TestProbe()
fullStateReplicator.tell(Get(key._1, ReadLocal), p.ref)
val fullStateValue = p.expectMsgType[GetSuccess[ORMultiMap[String, String]]].dataValue.get(key._2)
deltaReplicator.tell(Get(key._1, ReadLocal), p.ref)
val deltaValue = p.expectMsgType[GetSuccess[ORMultiMap[String, String]]].dataValue.get(key._2)
deltaValue should ===(fullStateValue)
}
}
}
List(KeyJ, KeyK, KeyL).foreach { key
within(5.seconds) {
awaitAssert {
val p = TestProbe()
fullStateReplicator.tell(Get(key._1, ReadLocal), p.ref)
val fullStateValue = p.expectMsgType[GetSuccess[ORMap[String, ORSet[String]]]].dataValue.get(key._2)
deltaReplicator.tell(Get(key._1, ReadLocal), p.ref)
val deltaValue = p.expectMsgType[GetSuccess[ORMap[String, ORSet[String]]]].dataValue.get(key._2)
deltaValue.map(_.elements) should ===(fullStateValue.map(_.elements))
}
}
}
enterBarrierAfterTestStep()
} catch {
case e: Throwable