Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Akka : the proper use of `ask` pattern?

I'm trying to grok Futures and ask pattern in akka.

So, I make two actors, and one asking another to send him back a message. Well, according to akka's Futures documentation, actor should ask(?) for message and it shall give him a Future instanse. Then actor should block (using Await) to get Future results.

Well, I never get my future done. Why is that?

Code is:

package head_thrash

import akka.actor._
import akka.util.Timeout
import scala.concurrent.Await
import scala.concurrent.duration._

object Main extends App {

  val system = ActorSystem("actors")

  val actor1 = system.actorOf(Props[MyActor], "node_1")
  val actor2 = system.actorOf(Props[MyActor], "node_2")

  actor2 ! "ping_other"

  system.awaitTermination()

  Console.println("Bye!")
}

class MyActor extends Actor with ActorLogging {
  import akka.pattern.ask

  implicit val timeout = Timeout(100.days)

  def receive = {
    case "ping_other" => {
      val selection = context.actorSelection("../node_1")
      log.info("Sending ping to node_1")
      val result = Await.result(selection ? "ping", Duration.Inf) // <-- Blocks here forever!
      log.info("Got result " + result)
    }
    case "ping" => {
      log.info("Sending back pong!")
      sender ! "pong"
    }
  }
}

If I change Duration.Inf to 5.seconds, then actor waits 5 seconds, tells that my future is Timeouted (by throwing TimeoutException), and then other actor finally replies with needed message. So, no async happens. Why? :-(

How should I properly implement that pattern? Thanks.

like image 261
head_thrash Avatar asked Nov 21 '13 14:11

head_thrash


2 Answers

The official Akka documentation says that Await.result will cause the current thread to block and wait for the Actor to 'complete' the Future with it's reply.

It is strange that your code blocks there forever, do you have only one thread for all your application?

Anyway I guess a more "idiomatic" way to code it would be to use a callback on the future success.

def receive = {
    case "ping_other" => {
      val selection = context.actorSelection("../node_1")
      log.info("Sending ping to node_1")
      val future: Future[String] = ask(selection, "ping").mapTo[String]
      future.onSuccess { 
         case result : String ⇒ log.info("Got result " + result)
      }
    }
...
like image 141
Arnaud Gourlay Avatar answered Oct 14 '22 00:10

Arnaud Gourlay


Two reasons why that doesn't work.

First, "node_1" asks itself and the "ping" will not be processed because it is blocking in waiting for the ask.

Also, there is a shortcoming of actorSelection for relative paths ("../node_1"). It is processed with message passing, and since your actor is blocking it cannot process any other message. This has been improved in upcoming 2.3 version of Akka, but you should avoid blocking anyway.

like image 36
Patrik Nordwall Avatar answered Oct 14 '22 00:10

Patrik Nordwall