Merge pull request #464 from akka/wip-verify-balancing-dispatcher-is-not-used-with-any-kind-of-router-jboner

Added verification that a BalancingDispatcher can not be used with any kind of Router.
This commit is contained in:
Jonas Bonér 2012-05-23 04:30:28 -07:00
commit 63e4b6e96e
8 changed files with 106 additions and 42 deletions

View file

@ -0,0 +1,80 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import akka.testkit._
import akka.testkit.DefaultTimeout
import akka.testkit.TestEvent._
import akka.dispatch.Await
import akka.util.duration._
import akka.routing._
import akka.config.ConfigurationException
import com.typesafe.config.{ Config, ConfigFactory }
import org.scalatest.BeforeAndAfterEach
import org.scalatest.junit.JUnitSuite
object ActorConfigurationVerificationSpec {
class TestActor extends Actor {
def receive: Receive = {
case _
}
}
val config = """
balancing-dispatcher {
type = BalancingDispatcher
throughput = 1
}
pinned-dispatcher {
executor = "thread-pool-executor"
type = PinnedDispatcher
}
"""
}
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class ActorConfigurationVerificationSpec extends AkkaSpec(ActorConfigurationVerificationSpec.config) with DefaultTimeout with BeforeAndAfterEach {
import ActorConfigurationVerificationSpec._
override def atStartup {
system.eventStream.publish(Mute(EventFilter[ConfigurationException]("")))
}
"An Actor configured with a BalancingDispatcher" must {
"fail verification with a ConfigurationException if also configured with a RoundRobinRouter" in {
intercept[ConfigurationException] {
system.actorOf(Props[TestActor].withDispatcher("balancing-dispatcher").withRouter(RoundRobinRouter(2)))
}
}
"fail verification with a ConfigurationException if also configured with a BroadcastRouter" in {
intercept[ConfigurationException] {
system.actorOf(Props[TestActor].withDispatcher("balancing-dispatcher").withRouter(BroadcastRouter(2)))
}
}
"fail verification with a ConfigurationException if also configured with a RandomRouter" in {
intercept[ConfigurationException] {
system.actorOf(Props[TestActor].withDispatcher("balancing-dispatcher").withRouter(RandomRouter(2)))
}
}
"fail verification with a ConfigurationException if also configured with a SmallestMailboxRouter" in {
intercept[ConfigurationException] {
system.actorOf(Props[TestActor].withDispatcher("balancing-dispatcher").withRouter(SmallestMailboxRouter(2)))
}
}
"fail verification with a ConfigurationException if also configured with a ScatterGatherFirstCompletedRouter" in {
intercept[ConfigurationException] {
system.actorOf(Props[TestActor].withDispatcher("balancing-dispatcher").withRouter(ScatterGatherFirstCompletedRouter(nrOfInstances = 2, within = 2 seconds)))
}
}
"not fail verification with a ConfigurationException also not configured with a Router" in {
system.actorOf(Props[TestActor].withDispatcher("balancing-dispatcher"))
}
}
"An Actor configured with a non-balancing dispatcher" must {
"not fail verification with a ConfigurationException if also configured with a Router" in {
system.actorOf(Props[TestActor].withDispatcher("pinned-dispatcher").withRouter(RoundRobinRouter(2)))
}
}
}

View file

@ -128,35 +128,6 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
current.routees.size must be(2) current.routees.size must be(2)
} }
"resize when busy" in {
val busy = new TestLatch(1)
val resizer = DefaultResizer(
lowerBound = 1,
upperBound = 3,
pressureThreshold = 0,
messagesPerResize = 1)
val router = system.actorOf(Props[BusyActor].withRouter(RoundRobinRouter(resizer = Some(resizer))).withDispatcher("bal-disp"))
val latch1 = new TestLatch(1)
router ! (latch1, busy)
Await.ready(latch1, 2 seconds)
val latch2 = new TestLatch(1)
router ! (latch2, busy)
Await.ready(latch2, 2 seconds)
val latch3 = new TestLatch(1)
router ! (latch3, busy)
Await.ready(latch3, 2 seconds)
Await.result(router ? CurrentRoutees, 5 seconds).asInstanceOf[RouterRoutees].routees.size must be(3)
busy.countDown()
}
"grow as needed under pressure" in { "grow as needed under pressure" in {
// make sure the pool starts at the expected lower limit and grows to the upper as needed // make sure the pool starts at the expected lower limit and grows to the upper as needed
// as influenced by the backlog of blocking pooled actors // as influenced by the backlog of blocking pooled actors

View file

@ -120,9 +120,9 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
* Throws: IllegalArgumentException if the value of "type" is not valid * Throws: IllegalArgumentException if the value of "type" is not valid
* IllegalArgumentException if it cannot create the MessageDispatcherConfigurator * IllegalArgumentException if it cannot create the MessageDispatcherConfigurator
*/ */
private[akka] def from(cfg: Config): MessageDispatcher = { private[akka] def from(cfg: Config): MessageDispatcher = configuratorFrom(cfg).dispatcher()
configuratorFrom(cfg).dispatcher()
} private[akka] def isBalancingDispatcher(id: String): Boolean = settings.config.hasPath(id) && config(id).getString("type") == "BalancingDispatcher"
/* /*
* Creates a MessageDispatcherConfigurator from a Config. * Creates a MessageDispatcherConfigurator from a Config.

View file

@ -31,11 +31,17 @@ private[akka] class RoutedActorRef(_system: ActorSystemImpl, _props: Props, _sup
_supervisor, _supervisor,
_path) { _path) {
// verify that a BalancingDispatcher is not used with a Router
if (_system.dispatchers.isBalancingDispatcher(_props.dispatcher) && _props.routerConfig != NoRouter)
throw new ConfigurationException(
"Configuration for actor [" + _path.toString +
"] is invalid - you can not use a 'BalancingDispatcher' together with any type of 'Router'")
/* /*
* CAUTION: RoutedActorRef is PROBLEMATIC * CAUTION: RoutedActorRef is PROBLEMATIC
* ====================================== * ======================================
* *
* We are constructing/assembling the children outside of the scope of the * We are constructing/assembling the children outside of the scope of the
* Router actor, inserting them in its childrenRef list, which is not at all * Router actor, inserting them in its childrenRef list, which is not at all
* synchronized. This is done exactly once at start-up, all other accesses * synchronized. This is done exactly once at start-up, all other accesses
* are done from the Router actor. This means that the only thing which is * are done from the Router actor. This means that the only thing which is

View file

@ -70,7 +70,7 @@ There are 4 different types of message dispatchers:
* BalancingDispatcher * BalancingDispatcher
- This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors. - This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors.
- It is assumed that all actors using the same instance of this dispatcher can process all messages that have been sent to one of the actors; i.e. the actors belong to a pool of actors, and to the client there is no guarantee about which actor instance actually processes a given message. - It is assumed that all actors using the same instance of this dispatcher can process all messages that have been sent to one of the actors; i.e. the actors belong to a pool of actors, and to the client there is no guarantee about which actor instance actually processes a given message.
@ -85,9 +85,11 @@ There are 4 different types of message dispatchers:
"thread-pool-executor" or the FQCN of "thread-pool-executor" or the FQCN of
an ``akka.dispatcher.ExecutorServiceConfigurator`` an ``akka.dispatcher.ExecutorServiceConfigurator``
- Note that you can **not** use a ``BalancingDispatcher`` together with any kind of ``Router``, trying to do so will make your actor fail verification.
* CallingThreadDispatcher * CallingThreadDispatcher
- This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads, - This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads,
but it can be used from different threads concurrently for the same actor. See :ref:`TestCallingThreadDispatcherRef` but it can be used from different threads concurrently for the same actor. See :ref:`TestCallingThreadDispatcherRef`
for details and restrictions. for details and restrictions.

View file

@ -375,7 +375,8 @@ The dispatcher for created children of the router will be taken from
makes sense to configure the :class:`BalancingDispatcher` if the precise makes sense to configure the :class:`BalancingDispatcher` if the precise
routing is not so important (i.e. no consistent hashing or round-robin is routing is not so important (i.e. no consistent hashing or round-robin is
required); this enables newly created routees to pick up work immediately by required); this enables newly created routees to pick up work immediately by
stealing it from their siblings. stealing it from their siblings. Note that you can **not** use a ``BalancingDispatcher``
together with any kind of ``Router``, trying to do so will make your actor fail verification.
The “head” router, of course, cannot run on the same balancing dispatcher, The “head” router, of course, cannot run on the same balancing dispatcher,
because it does not process the same messages, hence this special actor does because it does not process the same messages, hence this special actor does

View file

@ -71,7 +71,7 @@ There are 4 different types of message dispatchers:
* BalancingDispatcher * BalancingDispatcher
- This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors. - This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors.
- It is assumed that all actors using the same instance of this dispatcher can process all messages that have been sent to one of the actors; i.e. the actors belong to a pool of actors, and to the client there is no guarantee about which actor instance actually processes a given message. - It is assumed that all actors using the same instance of this dispatcher can process all messages that have been sent to one of the actors; i.e. the actors belong to a pool of actors, and to the client there is no guarantee about which actor instance actually processes a given message.
@ -86,9 +86,11 @@ There are 4 different types of message dispatchers:
"thread-pool-executor" or the FQCN of "thread-pool-executor" or the FQCN of
an ``akka.dispatcher.ExecutorServiceConfigurator`` an ``akka.dispatcher.ExecutorServiceConfigurator``
- Note that you can **not** use a ``BalancingDispatcher`` together with any kind of ``Router``, trying to do so will make your actor fail verification.
* CallingThreadDispatcher * CallingThreadDispatcher
- This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads, - This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads,
but it can be used from different threads concurrently for the same actor. See :ref:`TestCallingThreadDispatcherRef` but it can be used from different threads concurrently for the same actor. See :ref:`TestCallingThreadDispatcherRef`
for details and restrictions. for details and restrictions.
@ -112,8 +114,8 @@ And then using it:
.. includecode:: ../scala/code/akka/docs/dispatcher/DispatcherDocSpec.scala#defining-pinned-dispatcher .. includecode:: ../scala/code/akka/docs/dispatcher/DispatcherDocSpec.scala#defining-pinned-dispatcher
Note that ``thread-pool-executor`` configuration as per the above ``my-thread-pool-dispatcher`` exmaple is Note that ``thread-pool-executor`` configuration as per the above ``my-thread-pool-dispatcher`` exmaple is
NOT applicable. This is because every actor will have its own thread pool when using ``PinnedDispatcher``, NOT applicable. This is because every actor will have its own thread pool when using ``PinnedDispatcher``,
and that pool will have only one thread. and that pool will have only one thread.
Mailboxes Mailboxes

View file

@ -375,7 +375,9 @@ The dispatcher for created children of the router will be taken from
makes sense to configure the :class:`BalancingDispatcher` if the precise makes sense to configure the :class:`BalancingDispatcher` if the precise
routing is not so important (i.e. no consistent hashing or round-robin is routing is not so important (i.e. no consistent hashing or round-robin is
required); this enables newly created routees to pick up work immediately by required); this enables newly created routees to pick up work immediately by
stealing it from their siblings. stealing it from their siblings. Note that you can **not** use a ``BalancingDispatcher``
together with any kind of ``Router``, trying to do so will make your actor fail verification.
.. note:: .. note::