Skip to content

Instantly share code, notes, and snippets.

@IainHull
Last active August 29, 2015 13:58
Show Gist options
  • Save IainHull/10129911 to your computer and use it in GitHub Desktop.
Save IainHull/10129911 to your computer and use it in GitHub Desktop.
Demonstrate intermittent InvalidActorNameException using akka DeathWatch
name := "DeathReportActor"
version := "1.0"
scalaVersion := "2.10.3"
resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.3.1",
"com.typesafe.akka" %% "akka-testkit" % "2.3.1",
"org.scalatest" %% "scalatest" % "2.1.3" % "test",
"junit" % "junit" % "4.11" % "test"
)
import akka.actor.ActorSystem
import akka.testkit.TestKit
import org.scalatest.Suite
import org.scalatest.junit.ShouldMatchersForJUnit
import akka.testkit.ImplicitSender
import akka.actor.Actor
import akka.testkit.TestProbe
import org.junit.Test
import akka.actor.ActorRef
import akka.actor.Props
import akka.actor.Terminated
class DeathReportActorTest extends TestKit(ActorSystem("DeathReportActorTest"))
with Suite
with ShouldMatchersForJUnit
with ImplicitSender {
val pheonixProbe = TestProbe()
class PheonixActor(value: String) extends Actor {
override def preStart: Unit = {
pheonixProbe.ref ! ('Started, value)
}
override def postStop: Unit = {
pheonixProbe.ref ! ('Stopped, value)
}
def receive = {
case m => pheonixProbe.ref ! ('Message, m)
}
}
def pheonixProps(value: String) = Props(new PheonixActor(value))
case class Create(value: String)
case class Recreate(value: String)
class GuardianActor extends Actor {
val deathReporter = context.actorOf(DeathReportActor.props)
var fatedOpt: Option[ActorRef] = None
def receive = {
case Create(value) =>
fatedOpt = Some(context.actorOf(pheonixProps(value), "someName"))
case Recreate(value) =>
fatedOpt foreach { fated =>
deathReporter ! DeathReportActor.Watch(fated, self, Create(value))
context.system.stop(fated)
}
}
}
@Test
def testRestartPattern = {
val guardian = system.actorOf(Props(new GuardianActor))
guardian ! Create("foo0")
pheonixProbe.expectMsg(('Started, "foo0"))
1 to 50000 foreach { i =>
val now = "foo" + (i - 1)
val next = "foo" + i
guardian ! Recreate(next)
pheonixProbe.expectMsg(('Stopped, now))
pheonixProbe.expectMsg(('Started, next))
}
}
}
/**
* Simple actor that reports the death of other actors.
*
* Clients send the DeathReportActor a Watch message, then when the fated
* actor dies the specified message is sent to the mourner.
*
* This is useful for restarting actors with different constructor parameters
*
* {{{
*
* def receive = {
* case Create(value) =>
* fated = context.actorOf(FatedActor.props(value), "someName")
* case Recreate(value) =>
* context.system.stop(fated)
* deathReporter ! Watch(fated, self, Create(value))
* }
*
* }}}
*/
object DeathReportActor {
/**
* Message to get the DeathReportActor to watch for another actor's death.
*
* @param fated the actor that should die
* @param mourner the the actor that cares about the death of the fated actor
*/
case class Watch(fated: ActorRef, mourner: ActorRef, message: AnyRef)
def props: Props = Props[DeathReportActor]
}
class DeathReportActor extends Actor {
import DeathReportActor._
var watches = Map[ActorRef, Watch]()
def receive = {
case w @ Watch(fated, _, _) =>
watches += fated -> w
context.watch(fated)
case Terminated(fated) =>
watches.get(fated) foreach {
case Watch(_, mourner, message) =>
mourner ! message
}
}
}
java.lang.AssertionError: assertion failed: timeout (3 seconds) during expectMsg while waiting for ('Started,foo7493)
at scala.Predef$.assert(Predef.scala:179)
at akka.testkit.TestKitBase$class.expectMsg_internal(TestKit.scala:327)
at akka.testkit.TestKitBase$class.expectMsg(TestKit.scala:314)
at akka.testkit.TestKit.expectMsg(TestKit.scala:707)
at DeathReportActorTest$$anonfun$testRestartPattern$1.apply(TestFoo.scala:66)
at DeathReportActorTest$$anonfun$testRestartPattern$1.apply(TestFoo.scala:61)
at scala.collection.immutable.Range.foreach(Range.scala:141)
at DeathReportActorTest.testRestartPattern(TestFoo.scala:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
[INFO] [04/08/2014 16:08:23.670] [DeathReportActorTest-akka.actor.default-dispatcher-6] [akka://DeathReportActorTest/user/$a/someName] Message [akka.dispatch.sysmsg.Suspend] from Actor[akka://DeathReportActorTest/user/$a/someName#697148684] to Actor[akka://DeathReportActorTest/user/$a/someName#697148684] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
[ERROR] [04/08/2014 16:08:23.671] [DeathReportActorTest-akka.actor.default-dispatcher-8] [akka://DeathReportActorTest/user/$a] actor name [someName] is not unique!
akka.actor.InvalidActorNameException: actor name [someName] is not unique!
at akka.actor.dungeon.ChildrenContainer$NormalChildrenContainer.reserve(ChildrenContainer.scala:130)
at akka.actor.dungeon.Children$class.reserveChild(Children.scala:77)
at akka.actor.ActorCell.reserveChild(ActorCell.scala:369)
at akka.actor.dungeon.Children$class.makeChild(Children.scala:202)
at akka.actor.dungeon.Children$class.actorOf(Children.scala:38)
at akka.actor.ActorCell.actorOf(ActorCell.scala:369)
at DeathReportActorTest$GuardianActor$$anonfun$receive$2.applyOrElse(TestFoo.scala:45)
at akka.actor.Actor$class.aroundReceive(Actor.scala:465)
at DeathReportActorTest$GuardianActor.aroundReceive(TestFoo.scala:39)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:516)
at akka.actor.ActorCell.invoke(ActorCell.scala:487)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:238)
at akka.dispatch.Mailbox.run(Mailbox.scala:220)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:393)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment