George Pligoropoulos
George Pligoropoulos

Reputation: 2939

How to get a Promise (or Future) by asking an Akka Actor considering that the result is NOT available as a response from the same message

I have followed this tutorial: http://doc.akka.io/docs/akka/2.0/intro/getting-started-first-scala.html

Basically there is a Master actor which responds to these two messages:

def receive = {
    case Calculate => {
      //....
    }
    case Result(value) => {
      //....
    }
}

We send the message "Calculate" and after several "Result"s (from the slave actors) we have the correct calculation

Now I am inside an action of a play2 controller and I am using this:

val promise = (master ? Calculate)(10.seconds).mapTo[String].asPromise

Unfortunately (obviously) I get nothing because Calculate message does reply with a message to the sender.

I would like to somehow make the Akka Actor wait.... and when the computation is complete to send a message back to the sender.

But how?... Unless I am modelling it in the wrong way!

Upvotes: 3

Views: 4419

Answers (2)

sourcedelica
sourcedelica

Reputation: 24040

You should either forward the message to the slaves (which keeps the same sender), or include the sender in the message to the slaves. For example:

def receive = {
  case Calculate => slave ! CalculateWithSender(sender)

  case res @ Result(value, originalSender) =>        
    val result = resultMap(originalSender) + res   // assuming results are just added
    resultMap += (originalSender -> result) 
    if (resultsAreFinished(originalSender))    
      originalSender ! result
}

or

def receive = {
  case Calculate => slave.forward(Calculate)

  case res @ Result(value, originalSender) => 
    val result = resultMap(originalSender) + res   // assuming results are just added 
    resultMap += (originalSender -> result) 
    if (resultsAreFinished(originalSender))    
      originalSender ! result
}

or

def receive = {
  case Calculate => slave.tell(Calculate, sender)

  case res @ Result(value, originalSender) => 
    val result = resultMap(originalSender) + res   // assuming results are just added 
    resultMap += (originalSender -> result) 
    if (resultsAreFinished(originalSender))    
      originalSender ! result
}

I'm not familiar with Play promises, but ? (ask) returns a Akka Future. If .asPromise converts an Akka Future to a Play Promise then you're set.

Upvotes: 4

stew
stew

Reputation: 11366

Spin up a new actor for each calculation. forward the Calculate message to the new actor. The new actor stores the original sender. When the result is ready, the result is sent to the original sender, and the ephemeral actor dies:

class CalculateActor extends Actor {
    var origSender : ActorRef = _
    def receive {
        case Calculate => {
            origSender = sender
            slaveActors ! doStuff
            //....
    }
    case Result(value) => {
        if(results are ready) {
            origSender ! results
            self ! PoisonPill  // I'm done, time to die
        }
    }
}

class MasterActor extends Actor {
    def receive {
        case msg @ Calculate => {
            // forward sends without changing the sender
            context.actorOf(Props(new CalculateActor)).forward(msg)
        }
    }
}

Upvotes: 1

Related Questions