From d20166d5b458fc1423ad7c707a2fd1f928a4974f Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 6 May 2015 15:31:14 +0200 Subject: [PATCH] =act #13935 Throw same exception when extension init fails * The problem is that when an extension partly fails the next attempt will typically generate another failure, such as "actor name [snapshot-store] is not unique" * We have seen this problem for both persistence and cluster extensions * Extensions are now only given one chance to initialize and thereafter fail fast with same exception as the the first failure --- .../scala/akka/actor/ActorSystemSpec.scala | 28 +++++++++++++++++++ .../main/scala/akka/actor/ActorSystem.scala | 3 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/akka-actor-tests/src/test/scala/akka/actor/ActorSystemSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ActorSystemSpec.scala index 76624d0ce6..ce49e407f4 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/ActorSystemSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/ActorSystemSpec.scala @@ -18,6 +18,7 @@ import com.typesafe.config.Config import java.util.concurrent.{ LinkedBlockingQueue, BlockingQueue, TimeUnit } import akka.util.Switch import akka.util.Helpers.ConfigOps +import scala.util.control.NoStackTrace class JavaExtensionSpec extends JavaExtension with JUnitSuiteLike @@ -29,6 +30,23 @@ object TestExtension extends ExtensionId[TestExtension] with ExtensionIdProvider // Dont't place inside ActorSystemSpec object, since it will not be garbage collected and reference to system remains class TestExtension(val system: ExtendedActorSystem) extends Extension +object FailingTestExtension extends ExtensionId[FailingTestExtension] with ExtensionIdProvider { + def lookup = this + def createExtension(s: ExtendedActorSystem) = new FailingTestExtension(s) + + class TestException extends IllegalArgumentException("ERR") with NoStackTrace +} + +// Dont't place inside ActorSystemSpec object, since it will not be garbage collected and reference to system remains +class FailingTestExtension(val system: ExtendedActorSystem) extends Extension { + // first time the actor is created + val ref = system.actorOf(Props.empty, "uniqueName") + // but the extension initialization fails + // second time it will throw exception when trying to create actor with same name, + // but we want to see the first exception every time + throw new FailingTestExtension.TestException +} + object ActorSystemSpec { class Waves extends Actor { @@ -168,6 +186,16 @@ class ActorSystemSpec extends AkkaSpec(ActorSystemSpec.config) with ImplicitSend system.extension(TestExtension).system should ===(system) } + "handle extensions that fail to initialize" in { + intercept[FailingTestExtension.TestException] { + FailingTestExtension(system) + } + // same exception should be reported next time + intercept[FailingTestExtension.TestException] { + FailingTestExtension(system) + } + } + "log dead letters" in { val sys = ActorSystem("LogDeadLetters", ConfigFactory.parseString("akka.loglevel=INFO").withFallback(AkkaSpec.testConf)) try { diff --git a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala index 53f5e6c64b..225c9b2f51 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala @@ -737,6 +737,7 @@ private[akka] class ActorSystemImpl( private def findExtension[T <: Extension](ext: ExtensionId[T]): T = extensions.get(ext) match { case c: CountDownLatch ⇒ c.await(); findExtension(ext) //Registration in process, await completion and retry + case t: Throwable ⇒ throw t //Initialization failed, throw same again case other ⇒ other.asInstanceOf[T] //could be a T or null, in which case we return the null as T } @@ -756,7 +757,7 @@ private[akka] class ActorSystemImpl( } } catch { case t: Throwable ⇒ - extensions.remove(ext, inProcessOfRegistration) //In case shit hits the fan, remove the inProcess signal + extensions.replace(ext, inProcessOfRegistration, t) //In case shit hits the fan, remove the inProcess signal throw t //Escalate to caller } finally { inProcessOfRegistration.countDown //Always notify listeners of the inProcess signal