Search code examples
scalastateactormutable

cannot increment a 'total' variable inside receive() in Akka Actor


I am new to Scala (Akka Actors. I am aware there are benefits of avoiding mutable state in actors, but a found a solution to incrementing a var total similar to that given by the SO question:

Alternatives to using "var" for state with actors?

My code is below.

The solution would appear simple: my Calculator actor maintains a local var total, and the appropriate match on a case class in receive() => increments this total with an amount extracted from the matching case class.

My case statements are working. A println shows that each time my input matches the case statement, the amount to be added to the total (amt) is yielded by the match. I.e. this works as expected:

case MyCaseClass(_, amt) => println(amt)

But this line:

case MyCaseClass(_, amt) => total += amt

fails. The local var total is never incremented. I tried using a list, and adding the new amt as a new member to a var List, but this fails also. In each case increment of the local var, be it type Double or List[Double] fails. Why is this? and what can I do to get the increment to work inside receive()?

Code:

import akka.actor.ActorSystem
import akka.actor.Props

class CostingActor extends Actor {
  var bedrooms:Double = 0.0
  var bathrooms: Double = 0.0

  def receive = {
    //THIS IS THE OFFENDING LINE OF CODE--PRINTS OUT P FINE, EACH TIME A MATCH IS MADE
    case RoomPojo("Bedroom", p) => bedrooms += p    //println(p)
    case RoomPojo("Bathroom", p) => bathrooms += p  //println(p)
    case "total" => println(bedrooms + bathrooms)
  }
}

object Main extends App {
  val system = ActorSystem("CostingSystem")
  var costingActor = system.actorOf(Props[CostingActor], name =     "costingactor")
  var roomActor = system.actorOf(Props[RoomActor], name = "roomactor")
  for (i <- 0 until args.length)
    roomActor ! args(i)
  //the following line of code may be wrong; (I also tried a future) but should n't the variables bedrooms
  // and bathrooms still show at least some incrementation?
    costingActor ! "total"

    system.shutdown()
}



object RoomActor {
  case class RoomPojo(name:String, price:Double) {}

}

class RoomActor extends Actor {

  val checkout = context.actorOf(Props[CostingActor])
  def receive: Receive = {
    case "Bedroom"  => checkout ! new RoomPojo("Bedroom", 45.0)
    case "Bathroom" => checkout ! new RoomPojo("Bathroom", 90.0)
    case _ => println("missed in room")
  }
}

Solution

  • The system.actorOf(Props[CostingActor], name = "costingactor") from your Main is a different actor than the context.actorOf(Props[CostingActor]) from the RoomActor. Each actorOf creates a new instance of this actor, each with its own state. You send the RoomPojo messages to one actor instance and ask a completely different instance for the state, which hasn't seen any RoomPojo messages at all.

    You can modify the RoomActor to take an actorRef as a parameter and pass the costingActor to the Props, this way you're using the same actor instance for the state.

    var costingActor = system.actorOf(Props[CostingActor], name = "costingactor")
    var roomActor = system.actorOf(Props(classOf[RoomActor], costingActor), name = "roomactor")
    
    // ...
    
    class RoomActor(checkout: ActorRef) extends Actor
    

    Also, since everything is asynchronous, the 'total' message might be lost if you shut down the system immediately after sending the message.