2015-06-01 19:03:00 +02:00
|
|
|
/**
|
2017-01-04 17:37:10 +01:00
|
|
|
* Copyright (C) 2015-2017 Lightbend Inc. <http://www.lightbend.com>
|
2015-06-01 19:03:00 +02:00
|
|
|
*/
|
|
|
|
|
|
2015-08-12 14:56:28 +02:00
|
|
|
package akka.pattern
|
2015-07-07 16:28:17 +02:00
|
|
|
|
2015-06-01 19:03:00 +02:00
|
|
|
import scala.concurrent.duration._
|
|
|
|
|
import akka.actor._
|
|
|
|
|
import akka.testkit._
|
2015-11-16 17:42:24 +01:00
|
|
|
import scala.util.control.NoStackTrace
|
|
|
|
|
import akka.actor.SupervisorStrategy
|
2015-06-01 19:03:00 +02:00
|
|
|
|
|
|
|
|
object BackoffSupervisorSpec {
|
2015-11-16 17:42:24 +01:00
|
|
|
|
|
|
|
|
class TestException extends RuntimeException with NoStackTrace
|
|
|
|
|
|
2015-06-01 19:03:00 +02:00
|
|
|
object Child {
|
|
|
|
|
def props(probe: ActorRef): Props =
|
|
|
|
|
Props(new Child(probe))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Child(probe: ActorRef) extends Actor {
|
|
|
|
|
def receive = {
|
2015-11-16 17:42:24 +01:00
|
|
|
case "boom" ⇒ throw new TestException
|
|
|
|
|
case msg ⇒ probe ! msg
|
2015-06-01 19:03:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
2015-12-23 20:18:19 +02:00
|
|
|
|
|
|
|
|
object ManualChild {
|
|
|
|
|
def props(probe: ActorRef): Props =
|
|
|
|
|
Props(new ManualChild(probe))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ManualChild(probe: ActorRef) extends Actor {
|
|
|
|
|
def receive = {
|
|
|
|
|
case "boom" ⇒ throw new TestException
|
|
|
|
|
case msg ⇒
|
|
|
|
|
probe ! msg
|
|
|
|
|
context.parent ! BackoffSupervisor.Reset
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-06-01 19:03:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class BackoffSupervisorSpec extends AkkaSpec with ImplicitSender {
|
|
|
|
|
import BackoffSupervisorSpec._
|
|
|
|
|
|
2015-12-23 20:18:19 +02:00
|
|
|
def onStopOptions(props: Props = Child.props(testActor)) = Backoff.onStop(props, "c1", 100.millis, 3.seconds, 0.2)
|
|
|
|
|
def onFailureOptions(props: Props = Child.props(testActor)) = Backoff.onFailure(props, "c1", 100.millis, 3.seconds, 0.2)
|
|
|
|
|
def create(options: BackoffOptions) = system.actorOf(BackoffSupervisor.props(options))
|
|
|
|
|
|
2015-06-01 19:03:00 +02:00
|
|
|
"BackoffSupervisor" must {
|
2015-12-23 20:18:19 +02:00
|
|
|
"start child again when it stops when using `Backoff.onStop`" in {
|
|
|
|
|
val supervisor = create(onStopOptions())
|
2015-06-01 19:03:00 +02:00
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
|
|
|
|
|
watch(c1)
|
|
|
|
|
c1 ! PoisonPill
|
|
|
|
|
expectTerminated(c1)
|
|
|
|
|
awaitAssert {
|
|
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
// new instance
|
|
|
|
|
expectMsgType[BackoffSupervisor.CurrentChild].ref.get should !==(c1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"forward messages to the child" in {
|
2015-12-23 20:18:19 +02:00
|
|
|
def assertForward(supervisor: ActorRef) = {
|
|
|
|
|
supervisor ! "hello"
|
|
|
|
|
expectMsg("hello")
|
|
|
|
|
}
|
|
|
|
|
assertForward(create(onStopOptions()))
|
|
|
|
|
assertForward(create(onFailureOptions()))
|
2015-06-01 19:03:00 +02:00
|
|
|
}
|
2015-11-16 17:42:24 +01:00
|
|
|
|
2015-12-23 20:18:19 +02:00
|
|
|
"support custom supervision strategy" in {
|
|
|
|
|
def assertCustomStrategy(supervisor: ActorRef) = {
|
2015-11-16 17:42:24 +01:00
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
2015-12-23 20:18:19 +02:00
|
|
|
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
|
|
|
|
|
watch(c1)
|
|
|
|
|
c1 ! "boom"
|
|
|
|
|
expectTerminated(c1)
|
|
|
|
|
awaitAssert {
|
|
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
// new instance
|
|
|
|
|
expectMsgType[BackoffSupervisor.CurrentChild].ref.get should !==(c1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
filterException[TestException] {
|
|
|
|
|
val stoppingStrategy = OneForOneStrategy() {
|
|
|
|
|
case _: TestException ⇒ SupervisorStrategy.Stop
|
|
|
|
|
}
|
|
|
|
|
val restartingStrategy = OneForOneStrategy() {
|
|
|
|
|
case _: TestException ⇒ SupervisorStrategy.Restart
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assertCustomStrategy(
|
|
|
|
|
create(onStopOptions()
|
|
|
|
|
.withSupervisorStrategy(stoppingStrategy)))
|
|
|
|
|
|
|
|
|
|
assertCustomStrategy(
|
|
|
|
|
create(onFailureOptions()
|
|
|
|
|
.withSupervisorStrategy(restartingStrategy)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"support default stopping strategy when using `Backoff.onStop`" in {
|
|
|
|
|
filterException[TestException] {
|
|
|
|
|
val supervisor = create(onStopOptions().withDefaultStoppingStrategy)
|
|
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
|
|
|
|
|
watch(c1)
|
|
|
|
|
supervisor ! BackoffSupervisor.GetRestartCount
|
|
|
|
|
expectMsg(BackoffSupervisor.RestartCount(0))
|
|
|
|
|
|
|
|
|
|
c1 ! "boom"
|
|
|
|
|
expectTerminated(c1)
|
|
|
|
|
awaitAssert {
|
|
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
// new instance
|
|
|
|
|
expectMsgType[BackoffSupervisor.CurrentChild].ref.get should !==(c1)
|
|
|
|
|
}
|
|
|
|
|
supervisor ! BackoffSupervisor.GetRestartCount
|
|
|
|
|
expectMsg(BackoffSupervisor.RestartCount(1))
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"support manual reset" in {
|
|
|
|
|
filterException[TestException] {
|
|
|
|
|
def assertManualReset(supervisor: ActorRef) = {
|
|
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
|
|
|
|
|
watch(c1)
|
|
|
|
|
c1 ! "boom"
|
|
|
|
|
expectTerminated(c1)
|
2016-01-22 14:24:36 +01:00
|
|
|
|
|
|
|
|
awaitAssert {
|
|
|
|
|
supervisor ! BackoffSupervisor.GetRestartCount
|
|
|
|
|
expectMsg(BackoffSupervisor.RestartCount(1))
|
|
|
|
|
}
|
2015-12-23 20:18:19 +02:00
|
|
|
|
|
|
|
|
awaitAssert {
|
|
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
// new instance
|
|
|
|
|
expectMsgType[BackoffSupervisor.CurrentChild].ref.get should !==(c1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
supervisor ! "hello"
|
|
|
|
|
expectMsg("hello")
|
|
|
|
|
|
|
|
|
|
// making sure the Reset is handled by supervisor
|
|
|
|
|
supervisor ! "hello"
|
|
|
|
|
expectMsg("hello")
|
|
|
|
|
|
|
|
|
|
supervisor ! BackoffSupervisor.GetRestartCount
|
|
|
|
|
expectMsg(BackoffSupervisor.RestartCount(0))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val stoppingStrategy = OneForOneStrategy() {
|
|
|
|
|
case _: TestException ⇒ SupervisorStrategy.Stop
|
|
|
|
|
}
|
|
|
|
|
val restartingStrategy = OneForOneStrategy() {
|
|
|
|
|
case _: TestException ⇒ SupervisorStrategy.Restart
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assertManualReset(
|
|
|
|
|
create(onStopOptions(ManualChild.props(testActor))
|
|
|
|
|
.withManualReset
|
|
|
|
|
.withSupervisorStrategy(stoppingStrategy)))
|
|
|
|
|
|
|
|
|
|
assertManualReset(
|
|
|
|
|
create(onFailureOptions(ManualChild.props(testActor))
|
|
|
|
|
.withManualReset
|
|
|
|
|
.withSupervisorStrategy(restartingStrategy)))
|
2015-11-16 17:42:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
2016-09-05 23:47:58 -04:00
|
|
|
|
2016-12-02 03:07:33 +09:00
|
|
|
"reply to sender if replyWhileStopped is specified" in {
|
|
|
|
|
filterException[TestException] {
|
|
|
|
|
val supervisor = create(Backoff.onFailure(Child.props(testActor), "c1", 100.seconds, 300.seconds, 0.2).withReplyWhileStopped("child was stopped"))
|
|
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
|
|
|
|
|
watch(c1)
|
|
|
|
|
supervisor ! BackoffSupervisor.GetRestartCount
|
|
|
|
|
expectMsg(BackoffSupervisor.RestartCount(0))
|
|
|
|
|
|
|
|
|
|
c1 ! "boom"
|
|
|
|
|
expectTerminated(c1)
|
|
|
|
|
|
|
|
|
|
awaitAssert {
|
|
|
|
|
supervisor ! BackoffSupervisor.GetRestartCount
|
|
|
|
|
expectMsg(BackoffSupervisor.RestartCount(1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
supervisor ! "boom"
|
|
|
|
|
expectMsg("child was stopped")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"not reply to sender if replyWhileStopped is NOT specified" in {
|
|
|
|
|
filterException[TestException] {
|
|
|
|
|
val supervisor = create(Backoff.onFailure(Child.props(testActor), "c1", 100.seconds, 300.seconds, 0.2))
|
|
|
|
|
supervisor ! BackoffSupervisor.GetCurrentChild
|
|
|
|
|
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
|
|
|
|
|
watch(c1)
|
|
|
|
|
supervisor ! BackoffSupervisor.GetRestartCount
|
|
|
|
|
expectMsg(BackoffSupervisor.RestartCount(0))
|
|
|
|
|
|
|
|
|
|
c1 ! "boom"
|
|
|
|
|
expectTerminated(c1)
|
|
|
|
|
|
|
|
|
|
awaitAssert {
|
|
|
|
|
supervisor ! BackoffSupervisor.GetRestartCount
|
|
|
|
|
expectMsg(BackoffSupervisor.RestartCount(1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
supervisor ! "boom" //this will be sent to deadLetters
|
|
|
|
|
expectNoMsg(500.milliseconds)
|
|
|
|
|
}
|
2016-09-05 23:47:58 -04:00
|
|
|
}
|
2015-06-01 19:03:00 +02:00
|
|
|
}
|
|
|
|
|
}
|