Add support for durable storage of distributed data, #21645

* using lmdbjava libarary
This commit is contained in:
Patrik Nordwall 2016-10-10 20:00:24 +02:00
parent 446c0545ec
commit d6d50a08d0
18 changed files with 1892 additions and 124 deletions

View file

@ -46,6 +46,7 @@ class LocalConcurrencySpec(_system: ActorSystem) extends TestKit(_system)
ConfigFactory.parseString("""
akka.actor.provider = "cluster"
akka.remote.netty.tcp.port=0
akka.remote.artery.canonical.port = 0
""")))
}

View file

@ -20,12 +20,12 @@ object WriteAggregatorSpec {
val key = GSetKey[String]("a")
def writeAggregatorProps(data: GSet[String], consistency: Replicator.WriteConsistency,
probes: Map[Address, ActorRef], nodes: Set[Address], replyTo: ActorRef): Props =
Props(new TestWriteAggregator(data, consistency, probes, nodes, replyTo))
probes: Map[Address, ActorRef], nodes: Set[Address], replyTo: ActorRef, durable: Boolean): Props =
Props(new TestWriteAggregator(data, consistency, probes, nodes, replyTo, durable))
class TestWriteAggregator(data: GSet[String], consistency: Replicator.WriteConsistency,
probes: Map[Address, ActorRef], nodes: Set[Address], replyTo: ActorRef)
extends WriteAggregator(key, DataEnvelope(data), consistency, None, nodes, replyTo) {
probes: Map[Address, ActorRef], nodes: Set[Address], replyTo: ActorRef, durable: Boolean)
extends WriteAggregator(key, DataEnvelope(data), consistency, None, nodes, replyTo, durable) {
override def replica(address: Address): ActorSelection =
context.actorSelection(probes(address).path)
@ -43,6 +43,8 @@ object WriteAggregatorSpec {
def receive = {
case WriteAck
replicator.foreach(_ ! WriteAck)
case WriteNack
replicator.foreach(_ ! WriteNack)
case msg
replicator = Some(sender())
replica ! msg
@ -50,9 +52,14 @@ object WriteAggregatorSpec {
}
}
class WriteAggregatorSpec extends AkkaSpec("""
class WriteAggregatorSpec extends AkkaSpec(s"""
akka.actor.provider = "cluster"
akka.remote.netty.tcp.port=0
akka.remote.netty.tcp.port = 0
akka.remote.artery.canonical.port = 0
akka.cluster.distributed-data.durable.lmdb {
dir = target/WriteAggregatorSpec-${System.currentTimeMillis}-ddata
map-size = 10 MiB
}
""")
with ImplicitSender {
@ -69,7 +76,7 @@ class WriteAggregatorSpec extends AkkaSpec("""
val data = GSet.empty + "A" + "B"
val timeout = 3.seconds.dilated
val writeTwo = WriteTo(2, timeout)
val writeThree = WriteTo(3, timeout)
val writeMajority = WriteMajority(timeout)
def probes(probe: ActorRef): Map[Address, ActorRef] =
@ -79,7 +86,7 @@ class WriteAggregatorSpec extends AkkaSpec("""
"send to at least N/2+1 replicas when WriteMajority" in {
val probe = TestProbe()
val aggr = system.actorOf(WriteAggregatorSpec.writeAggregatorProps(
data, writeMajority, probes(probe.ref), nodes, testActor))
data, writeMajority, probes(probe.ref), nodes, testActor, durable = false))
probe.expectMsgType[Write]
probe.lastSender ! WriteAck
@ -93,7 +100,7 @@ class WriteAggregatorSpec extends AkkaSpec("""
"send to more when no immediate reply" in {
val probe = TestProbe()
val aggr = system.actorOf(WriteAggregatorSpec.writeAggregatorProps(
data, writeMajority, probes(probe.ref), nodes, testActor))
data, writeMajority, probes(probe.ref), nodes, testActor, durable = false))
probe.expectMsgType[Write]
// no reply
@ -112,7 +119,7 @@ class WriteAggregatorSpec extends AkkaSpec("""
"timeout when less than required acks" in {
val probe = TestProbe()
val aggr = system.actorOf(WriteAggregatorSpec.writeAggregatorProps(
data, writeMajority, probes(probe.ref), nodes, testActor))
data, writeMajority, probes(probe.ref), nodes, testActor, durable = false))
probe.expectMsgType[Write]
// no reply
@ -126,6 +133,84 @@ class WriteAggregatorSpec extends AkkaSpec("""
watch(aggr)
expectTerminated(aggr)
}
}
"Durable WriteAggregator" must {
"not reply before local confirmation" in {
val probe = TestProbe()
val aggr = system.actorOf(WriteAggregatorSpec.writeAggregatorProps(
data, writeThree, probes(probe.ref), nodes, testActor, durable = true))
probe.expectMsgType[Write]
probe.lastSender ! WriteAck
probe.expectMsgType[Write]
probe.lastSender ! WriteAck
expectNoMsg(200.millis)
// the local write
aggr ! UpdateSuccess(WriteAggregatorSpec.key, None)
expectMsg(UpdateSuccess(WriteAggregatorSpec.key, None))
watch(aggr)
expectTerminated(aggr)
}
"tolerate WriteNack if enough WriteAck" in {
val probe = TestProbe()
val aggr = system.actorOf(WriteAggregatorSpec.writeAggregatorProps(
data, writeThree, probes(probe.ref), nodes, testActor, durable = true))
aggr ! UpdateSuccess(WriteAggregatorSpec.key, None) // the local write
probe.expectMsgType[Write]
probe.lastSender ! WriteAck
probe.expectMsgType[Write]
probe.lastSender ! WriteNack
probe.expectMsgType[Write]
probe.lastSender ! WriteAck
expectMsg(UpdateSuccess(WriteAggregatorSpec.key, None))
watch(aggr)
expectTerminated(aggr)
}
"reply with StoreFailure when too many nacks" in {
val probe = TestProbe()
val aggr = system.actorOf(WriteAggregatorSpec.writeAggregatorProps(
data, writeMajority, probes(probe.ref), nodes, testActor, durable = true))
probe.expectMsgType[Write]
probe.lastSender ! WriteNack
aggr ! UpdateSuccess(WriteAggregatorSpec.key, None) // the local write
probe.expectMsgType[Write]
probe.lastSender ! WriteAck
probe.expectMsgType[Write]
probe.lastSender ! WriteNack
probe.expectMsgType[Write]
probe.lastSender ! WriteNack
expectMsg(StoreFailure(WriteAggregatorSpec.key, None))
watch(aggr)
expectTerminated(aggr)
}
"timeout when less than required acks" in {
val probe = TestProbe()
val aggr = system.actorOf(WriteAggregatorSpec.writeAggregatorProps(
data, writeMajority, probes(probe.ref), nodes, testActor, durable = true))
probe.expectMsgType[Write]
// no reply
probe.expectMsgType[Write]
probe.lastSender ! WriteAck
probe.expectMsgType[Write]
probe.lastSender ! WriteNack
probe.expectMsgType[Write]
probe.lastSender ! WriteNack
expectMsg(UpdateTimeout(WriteAggregatorSpec.key, None))
watch(aggr)
expectTerminated(aggr)
}
}
}

View file

@ -31,15 +31,16 @@ class ReplicatedDataSerializerSpec extends TestKit(ActorSystem(
ConfigFactory.parseString("""
akka.actor.provider=cluster
akka.remote.netty.tcp.port=0
akka.remote.artery.canonical.port = 0
"""))) with WordSpecLike with Matchers with BeforeAndAfterAll {
val serializer = new ReplicatedDataSerializer(system.asInstanceOf[ExtendedActorSystem])
val Protocol = if (RARP(system).provider.remoteSettings.Artery.Enabled) "akka" else "akka.tcp"
val address1 = UniqueAddress(Address(Protocol, system.name, "some.host.org", 4711), 1)
val address2 = UniqueAddress(Address(Protocol, system.name, "other.host.org", 4711), 2)
val address3 = UniqueAddress(Address(Protocol, system.name, "some.host.org", 4712), 3)
val address1 = UniqueAddress(Address(Protocol, system.name, "some.host.org", 4711), 1L)
val address2 = UniqueAddress(Address(Protocol, system.name, "other.host.org", 4711), 2L)
val address3 = UniqueAddress(Address(Protocol, system.name, "some.host.org", 4712), 3L)
override def afterAll {
shutdown()

View file

@ -23,21 +23,23 @@ import akka.util.ByteString
import akka.cluster.UniqueAddress
import akka.remote.RARP
import com.typesafe.config.ConfigFactory
import akka.cluster.ddata.DurableStore.DurableDataEnvelope
class ReplicatorMessageSerializerSpec extends TestKit(ActorSystem(
"ReplicatorMessageSerializerSpec",
ConfigFactory.parseString("""
akka.actor.provider=cluster
akka.remote.netty.tcp.port=0
akka.remote.artery.canonical.port = 0
"""))) with WordSpecLike with Matchers with BeforeAndAfterAll {
val serializer = new ReplicatorMessageSerializer(system.asInstanceOf[ExtendedActorSystem])
val Protocol = if (RARP(system).provider.remoteSettings.Artery.Enabled) "akka" else "akka.tcp"
val address1 = UniqueAddress(Address(Protocol, system.name, "some.host.org", 4711), 1)
val address2 = UniqueAddress(Address(Protocol, system.name, "other.host.org", 4711), 2)
val address3 = UniqueAddress(Address(Protocol, system.name, "some.host.org", 4712), 3)
val address1 = UniqueAddress(Address(Protocol, system.name, "some.host.org", 4711), 1L)
val address2 = UniqueAddress(Address(Protocol, system.name, "other.host.org", 4711), 2L)
val address3 = UniqueAddress(Address(Protocol, system.name, "some.host.org", 4712), 3L)
val keyA = GSetKey[String]("A")
@ -72,6 +74,7 @@ class ReplicatorMessageSerializerSpec extends TestKit(ActorSystem(
address3 PruningState(address2, PruningInitialized(Set(address1.address))))))
checkSerialization(Write("A", DataEnvelope(data1)))
checkSerialization(WriteAck)
checkSerialization(WriteNack)
checkSerialization(Read("A"))
checkSerialization(ReadResult(Some(DataEnvelope(data1))))
checkSerialization(ReadResult(None))
@ -81,6 +84,7 @@ class ReplicatorMessageSerializerSpec extends TestKit(ActorSystem(
checkSerialization(Gossip(Map(
"A" DataEnvelope(data1),
"B" DataEnvelope(GSet() + "b" + "c")), sendBack = true))
checkSerialization(new DurableDataEnvelope(data1))
}
}