0
votes

I have been using Akka for a while and also, in simpler project, just using Scala 2.10 futures. However, the other day I had to mix a library returning Futures with Akka Actors and I wasn't sure how to integrate the Actor dispatching system and the runaway futures.

The choice of using Akka actors in the project was to be able to fine tune the actor queue and control the parallelization of the code, and maybe, eventually scale out (even if right now it's not the priority). However, if I have a code like this (simplified on purpose):

package externallib

object DoSomething {
    def foo(): Future[Something] = ....
}


package myapp

class MyActor extends Actor {
    def receive = {
        case "message" => externallib.DoSomething.foo() pipeTo sender
    }
}

.../
val actorRef = system.actorOf(Props[MyActor].withRouter(
                   RoundRobinRouter(nrOfInstances = 5)))
(0 to 20000) foreach {

    val futureSomething = actorRef ? "message"

}

then MyActor doesn't actually do anything, except spawning a thread in externallib and directly returning. The externallib future will eventually pipe a result to the caller of the actor.

However, this way, the well controlled router that manages the actors is not really controlling the threads that get spawned, as they are spawned outside the actor system, even if in the same ExecutionContext. In the example of the big loop, this means that instead of queueing the messages to the actor, these ones will be quickly consumed and spawn 20000 threads outside of any tightly controlled queue.

I was thinking that I could do something like this:

class MyActor extends Actor {
    def receive = {
        case "message" => 
            val res = Await.result(externallib.DoSomething.foo(), someDuration)
            sender ! res
    }
}

This will make sure that no new "message" will be sent to the actor until the externallib is done (or timesout). However, this will actually potentially require two threads (the actor's one and the one in DoSomething) to wait on a single computation.

Is there a better way to keep control on these futures that are produced outside the actor system?

1
What ExecutionContext is DoSomething using?Viktor Klang
Let's assume that the lib is implemented in a way that we can specify the execution context, and that the actor provides the dispatchers execution context. Unless there is a better way of doing it ;)Mortimer
You could create an ExecutionContext whose execute method simply sends the runnable to the Actor (reportFailure delegates to the actors' dispatchers' reportFailure) and then the actor can receive Runnables and execute them.Viktor Klang

1 Answers

0
votes

That MyActor looks dangerous, blocking inside of actors isn't really a good idea, indeed, if there is only one thread in the ExecutionContext that the actor and the future use this will deadlock. A better way to write this would be to put an onComplete on the future, and have the actor do a become to a behavior that makes it not dispatch any new futures (possibly by stashing all incoming messages). The onComplete on the future can both message the sender and message the actor to tell it to become the original behavior.