pekko/akka-cluster-tools/src/test/scala/akka/cluster/singleton/ClusterSingletonLeaseSpec.scala
Patrik Nordwall c45e6ef39b
Add Lightbend's SBR to Akka Cluster, #29085 (#29099)
* change package name to akka.cluster.sbr
* reference.conf has same config paths
* akka.cluster.sbr.SplitBrainResolverProvider instead of com.lightbend.akka.sbr.SplitBrainResolverProvider
* dependency from akka-cluster to akka-coordination, for lease strategy
* move TestLease to akka-coordination and use that in SBR tests
* remove keep-referee strategy
* use keep-majority by default
* review and adjust reference documentation

Co-authored-by: Johan Andrén <johan@markatta.com>
Co-authored-by: Johannes Rudolph <johannes.rudolph@gmail.com>
Co-authored-by: Christopher Batey <christopher.batey@gmail.com>
Co-authored-by: Arnout Engelen <github@bzzt.net>
2020-05-25 12:21:13 +02:00

206 lines
7.3 KiB
Scala

/*
* Copyright (C) 2019-2020 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.cluster.singleton
import java.util.concurrent.atomic.AtomicInteger
import scala.concurrent.Promise
import scala.concurrent.duration._
import scala.util.Success
import com.typesafe.config.ConfigFactory
import akka.actor.Actor
import akka.actor.ActorLogging
import akka.actor.ActorRef
import akka.actor.ExtendedActorSystem
import akka.actor.PoisonPill
import akka.actor.Props
import akka.cluster.Cluster
import akka.cluster.MemberStatus
import akka.coordination.lease.TestLease
import akka.coordination.lease.TestLeaseExt
import akka.testkit.AkkaSpec
import akka.testkit.TestException
import akka.testkit.TestProbe
class ImportantSingleton(lifeCycleProbe: ActorRef) extends Actor with ActorLogging {
override def preStart(): Unit = {
log.info("Important Singleton Starting")
lifeCycleProbe ! "preStart"
}
override def postStop(): Unit = {
log.info("Important Singleton Stopping")
lifeCycleProbe ! "postStop"
}
override def receive: Receive = {
case msg =>
sender() ! msg
}
}
class ClusterSingletonLeaseSpec extends AkkaSpec(ConfigFactory.parseString("""
akka.loglevel = INFO
akka.actor.provider = cluster
akka.cluster.singleton {
use-lease = "test-lease"
lease-retry-interval = 2000ms
}
""").withFallback(TestLease.config)) {
import TestLease.{ AcquireReq, ReleaseReq }
val cluster = Cluster(system)
val testLeaseExt = TestLeaseExt(system)
override protected def atStartup(): Unit = {
cluster.join(cluster.selfAddress)
awaitAssert {
cluster.selfMember.status shouldEqual MemberStatus.Up
}
}
def extSystem: ExtendedActorSystem = system.asInstanceOf[ExtendedActorSystem]
val counter = new AtomicInteger()
def nextName() = s"important-${counter.getAndIncrement()}"
val shortDuration = 50.millis
val leaseOwner = cluster.selfMember.address.hostPort
def nextSettings() = ClusterSingletonManagerSettings(system).withSingletonName(nextName())
def leaseNameFor(settings: ClusterSingletonManagerSettings): String =
s"ClusterSingletonLeaseSpec-singleton-akka://ClusterSingletonLeaseSpec/user/${settings.singletonName}"
"A singleton with lease" should {
"not start until lease is available" in {
val probe = TestProbe()
val settings = nextSettings()
system.actorOf(
ClusterSingletonManager.props(Props(new ImportantSingleton(probe.ref)), PoisonPill, settings),
settings.singletonName)
val testLease = awaitAssert {
testLeaseExt.getTestLease(leaseNameFor(settings))
} // allow singleton manager to create the lease
probe.expectNoMessage(shortDuration)
testLease.initialPromise.complete(Success(true))
probe.expectMsg("preStart")
}
"do not start if lease acquire returns false" in {
val probe = TestProbe()
val settings = nextSettings()
system.actorOf(
ClusterSingletonManager.props(Props(new ImportantSingleton(probe.ref)), PoisonPill, settings),
settings.singletonName)
val testLease = awaitAssert {
testLeaseExt.getTestLease(leaseNameFor(settings))
} // allow singleton manager to create the lease
probe.expectNoMessage(shortDuration)
testLease.initialPromise.complete(Success(false))
probe.expectNoMessage(shortDuration)
}
"retry trying to get lease if acquire returns false" in {
val singletonProbe = TestProbe()
val settings = nextSettings()
system.actorOf(
ClusterSingletonManager.props(Props(new ImportantSingleton(singletonProbe.ref)), PoisonPill, settings),
settings.singletonName)
val testLease = awaitAssert {
testLeaseExt.getTestLease(leaseNameFor(settings))
} // allow singleton manager to create the lease
testLease.probe.expectMsg(AcquireReq(leaseOwner))
singletonProbe.expectNoMessage(shortDuration)
val nextResponse = Promise[Boolean]()
testLease.setNextAcquireResult(nextResponse.future)
testLease.initialPromise.complete(Success(false))
testLease.probe.expectMsg(AcquireReq(leaseOwner))
singletonProbe.expectNoMessage(shortDuration)
nextResponse.complete(Success(true))
singletonProbe.expectMsg("preStart")
}
"do not start if lease acquire fails" in {
val probe = TestProbe()
val settings = nextSettings()
system.actorOf(
ClusterSingletonManager.props(Props(new ImportantSingleton(probe.ref)), PoisonPill, settings),
settings.singletonName)
val testLease = awaitAssert {
testLeaseExt.getTestLease(leaseNameFor(settings))
} // allow singleton manager to create the lease
probe.expectNoMessage(shortDuration)
testLease.initialPromise.failure(TestException("no lease for you"))
probe.expectNoMessage(shortDuration)
}
"retry trying to get lease if acquire returns fails" in {
val singletonProbe = TestProbe()
val settings = nextSettings()
system.actorOf(
ClusterSingletonManager.props(Props(new ImportantSingleton(singletonProbe.ref)), PoisonPill, settings),
settings.singletonName)
val testLease = awaitAssert {
testLeaseExt.getTestLease(leaseNameFor(settings))
} // allow singleton manager to create the lease
testLease.probe.expectMsg(AcquireReq(leaseOwner))
singletonProbe.expectNoMessage(shortDuration)
val nextResponse = Promise[Boolean]()
testLease.setNextAcquireResult(nextResponse.future)
testLease.initialPromise.failure(TestException("no lease for you"))
testLease.probe.expectMsg(AcquireReq(leaseOwner))
singletonProbe.expectNoMessage(shortDuration)
nextResponse.complete(Success(true))
singletonProbe.expectMsg("preStart")
}
"stop singleton if the lease fails periodic check" in {
val lifecycleProbe = TestProbe()
val settings = nextSettings()
system.actorOf(
ClusterSingletonManager.props(Props(new ImportantSingleton(lifecycleProbe.ref)), PoisonPill, settings),
settings.singletonName)
val testLease = awaitAssert {
testLeaseExt.getTestLease(leaseNameFor(settings))
}
testLease.probe.expectMsg(AcquireReq(leaseOwner))
testLease.initialPromise.complete(Success(true))
lifecycleProbe.expectMsg("preStart")
val callback = testLease.getCurrentCallback()
callback(None)
lifecycleProbe.expectMsg("postStop")
testLease.probe.expectMsg(ReleaseReq(leaseOwner))
// should try and reacquire lease
testLease.probe.expectMsg(AcquireReq(leaseOwner))
lifecycleProbe.expectMsg("preStart")
}
"release lease when leaving oldest" in {
val singletonProbe = TestProbe()
val settings = nextSettings()
system.actorOf(
ClusterSingletonManager.props(Props(new ImportantSingleton(singletonProbe.ref)), PoisonPill, settings),
settings.singletonName)
val testLease = awaitAssert {
testLeaseExt.getTestLease(leaseNameFor(settings))
} // allow singleton manager to create the lease
singletonProbe.expectNoMessage(shortDuration)
testLease.probe.expectMsg(AcquireReq(leaseOwner))
testLease.initialPromise.complete(Success(true))
singletonProbe.expectMsg("preStart")
cluster.leave(cluster.selfAddress)
testLease.probe.expectMsg(ReleaseReq(leaseOwner))
}
}
}