head_thrash
head_thrash

Reputation: 1711

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.

Upvotes: 13

Views: 19971

Answers (2)

Patrik Nordwall
Patrik Nordwall

Reputation: 2426

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.

Upvotes: 5

Arnaud Gourlay
Arnaud Gourlay

Reputation: 4666

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)
      }
    }
...

Upvotes: 11

Related Questions