-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Internal dispatcher to protect against starvation (akka#26816)
* Allow for dispatcher aliases and define a internal dispatcher * Test checking dispatcher name * MiMa for Dispatchers * Migration guide entry * No need to have custom dispatcher lookup logic in streams anymore * Default dispatcher size and migration note about that * Test checking exact config values... * Typed receptionist on internal dispatcher * All internal usages of system.dispatcher gone through
- Loading branch information
1 parent
e34a711
commit 81b1e2e
Showing
57 changed files
with
524 additions
and
329 deletions.
There are no files selected for viewing
167 changes: 167 additions & 0 deletions
167
akka-actor-tests/src/test/scala/akka/actor/ActorSystemDispatcherSpec.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/* | ||
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com> | ||
*/ | ||
|
||
package akka.actor | ||
|
||
import akka.ConfigurationException | ||
import akka.actor.setup.ActorSystemSetup | ||
import akka.dispatch.{ Dispatchers, ExecutionContexts } | ||
import akka.testkit.{ AkkaSpec, ImplicitSender, TestActors, TestProbe } | ||
import com.typesafe.config.ConfigFactory | ||
|
||
import scala.concurrent.ExecutionContext | ||
import scala.concurrent.duration._ | ||
|
||
object ActorSystemDispatchersSpec { | ||
|
||
class SnitchingExecutionContext(testActor: ActorRef, underlying: ExecutionContext) extends ExecutionContext { | ||
|
||
def execute(runnable: Runnable): Unit = { | ||
testActor ! "called" | ||
underlying.execute(runnable) | ||
} | ||
|
||
def reportFailure(t: Throwable): Unit = { | ||
testActor ! "failed" | ||
underlying.reportFailure(t) | ||
} | ||
} | ||
|
||
} | ||
|
||
class ActorSystemDispatchersSpec extends AkkaSpec(ConfigFactory.parseString(""" | ||
dispatcher-loop-1 = "dispatcher-loop-2" | ||
dispatcher-loop-2 = "dispatcher-loop-1" | ||
""")) with ImplicitSender { | ||
|
||
import ActorSystemDispatchersSpec._ | ||
|
||
"The ActorSystem" must { | ||
|
||
"work with a passed in ExecutionContext" in { | ||
val ecProbe = TestProbe() | ||
val ec = new SnitchingExecutionContext(ecProbe.ref, ExecutionContexts.global()) | ||
|
||
val system2 = ActorSystem(name = "ActorSystemDispatchersSpec-passed-in-ec", defaultExecutionContext = Some(ec)) | ||
|
||
try { | ||
val ref = system2.actorOf(Props(new Actor { | ||
def receive = { | ||
case "ping" => sender() ! "pong" | ||
} | ||
})) | ||
|
||
val probe = TestProbe() | ||
|
||
ref.tell("ping", probe.ref) | ||
|
||
ecProbe.expectMsg(1.second, "called") | ||
probe.expectMsg(1.second, "pong") | ||
} finally { | ||
shutdown(system2) | ||
} | ||
} | ||
|
||
"not use passed in ExecutionContext if executor is configured" in { | ||
val ecProbe = TestProbe() | ||
val ec = new SnitchingExecutionContext(ecProbe.ref, ExecutionContexts.global()) | ||
|
||
val config = ConfigFactory.parseString("akka.actor.default-dispatcher.executor = \"fork-join-executor\"") | ||
val system2 = ActorSystem( | ||
name = "ActorSystemDispatchersSpec-ec-configured", | ||
config = Some(config), | ||
defaultExecutionContext = Some(ec)) | ||
|
||
try { | ||
val ref = system2.actorOf(TestActors.echoActorProps) | ||
val probe = TestProbe() | ||
|
||
ref.tell("ping", probe.ref) | ||
|
||
ecProbe.expectNoMessage(200.millis) | ||
probe.expectMsg(1.second, "ping") | ||
} finally { | ||
shutdown(system2) | ||
} | ||
} | ||
|
||
def userGuardianDispatcher(system: ActorSystem): String = { | ||
val impl = system.asInstanceOf[ActorSystemImpl] | ||
impl.guardian.asInstanceOf[ActorRefWithCell].underlying.asInstanceOf[ActorCell].dispatcher.id | ||
} | ||
|
||
"provide a single place to override the internal dispatcher" in { | ||
val sys = ActorSystem( | ||
"ActorSystemDispatchersSpec-override-internal-disp", | ||
ConfigFactory.parseString(""" | ||
akka.actor.internal-dispatcher = akka.actor.default-dispatcher | ||
""")) | ||
try { | ||
// that the user guardian runs on the overriden dispatcher instead of internal | ||
// isn't really a guarantee any internal actor has been made running on the right one | ||
// but it's better than no test coverage at all | ||
userGuardianDispatcher(sys) should ===("akka.actor.default-dispatcher") | ||
} finally { | ||
shutdown(sys) | ||
} | ||
} | ||
|
||
"provide internal execution context instance through BootstrapSetup" in { | ||
val ecProbe = TestProbe() | ||
val ec = new SnitchingExecutionContext(ecProbe.ref, ExecutionContexts.global()) | ||
|
||
// using the default for internal dispatcher and passing a pre-existing execution context | ||
val system2 = | ||
ActorSystem( | ||
name = "ActorSystemDispatchersSpec-passed-in-ec-for-internal", | ||
config = Some(ConfigFactory.parseString(""" | ||
akka.actor.internal-dispatcher = akka.actor.default-dispatcher | ||
""")), | ||
defaultExecutionContext = Some(ec)) | ||
|
||
try { | ||
val ref = system2.actorOf(Props(new Actor { | ||
def receive = { | ||
case "ping" => sender() ! "pong" | ||
} | ||
}).withDispatcher(Dispatchers.InternalDispatcherId)) | ||
|
||
val probe = TestProbe() | ||
|
||
ref.tell("ping", probe.ref) | ||
|
||
ecProbe.expectMsg(1.second, "called") | ||
probe.expectMsg(1.second, "pong") | ||
} finally { | ||
shutdown(system2) | ||
} | ||
} | ||
|
||
"use an internal dispatcher for the guardian by default" in { | ||
userGuardianDispatcher(system) should ===("akka.actor.internal-dispatcher") | ||
} | ||
|
||
"use the default dispatcher by a user provided user guardian" in { | ||
val sys = new ActorSystemImpl( | ||
"ActorSystemDispatchersSpec-custom-user-guardian", | ||
ConfigFactory.defaultReference(), | ||
getClass.getClassLoader, | ||
None, | ||
Some(Props.empty), | ||
ActorSystemSetup.empty) | ||
sys.start() | ||
try { | ||
userGuardianDispatcher(sys) should ===("akka.actor.default-dispatcher") | ||
} finally shutdown(sys) | ||
} | ||
|
||
"provide a good error on an dispatcher alias loop in the config" in { | ||
intercept[ConfigurationException] { | ||
system.dispatchers.lookup("dispatcher-loop-1") | ||
} | ||
} | ||
|
||
} | ||
|
||
} |
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
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
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
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
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
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
Oops, something went wrong.