Last active
August 29, 2015 13:58
-
-
Save IainHull/10129911 to your computer and use it in GitHub Desktop.
Demonstrate intermittent InvalidActorNameException using akka DeathWatch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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