Tomer Tomer - 3 months ago 14
Scala Question

Update state in actor from within a future

Consider the following code sample:

class MyActor (httpClient: HttpClient) {

var canSendMore = true

override def receive: Receive = {
case PayloadA(name: String) => send(urlA)
case PayloadB(name: String) => send(urlB)

def send(url: String){
if (canSendMore)
httpClient.post(url).map(response => canSendMore = response.canSendMore)
else {
Thread.sleep(5000) //this will be done in a more elegant way, it's just for the example.
httpClient.post(url).map(response => canSendMore = response.canSendMore)
}
}
}


Each message handling will result in an async http request. (post return value is a Future[Response])
My problem is that I want to safely update counter ( At the moment there is a race condition)

BTW, I must somehow update counter in the same thread, or at least before any other message is processed by this actor.

Is this possible?

Answer

You can use become + stash combination to keep on stashing messages when the http request future is in process.

object FreeToProcess

case PayloadA(name: String)

class MyActor (httpClient: HttpClient) extends Actor with Stash {

  def canProcessReceive: Receive = {
    case PayloadA(name: String) => {
      // become an actor which just stashes messages
      context.become(canNotProcessReceive, discardOld = false)

      httpClient.post(urlA).onComplete({
        case Success(x) => {
          // Use your result
          self ! FreeToProcess
        }
        case Failure(e) => {
          // Use your failure
          self ! FreeToProcess
        }
      })
    }
  }

  def canNotProcessReceive: Receive = {
    case CanProcess => {
      // replay stash to mailbox
      unstashAll()
      // start processing messages
      context.unbecome()
    }

    case msg => {
      stash()
    }
  }

}
Comments