Search code examples
scalaslick

Data sharing between Akka actors


Based on this question Data sharing in Akka I've implemented a simple akka hierarchy to share data between two actors:

package playground

import akka.actor.{Actor, ActorRef, ActorSystem, Props}

case object GetFromDb
case object GetList
case object ProcessList

class ParentActor extends Actor {
  var fromDb: List[Int] = List()

  def receive = {
    case GetFromDb =>
      // query the database and replace fromDb with a new list
      fromDb = List(1,2,3)

    case GetList =>
      sender() ! fromDb
  }
}

class ChildActor(parent: ActorRef) extends Actor {
  def receive = {
    case ProcessList =>
      // get the list from the parent
      parent ! GetList
    case fromDb: List[Int] =>
      fromDb.foreach(println)
  }
}

object AkkaDataSharingDriver extends App {

  val system: ActorSystem = ActorSystem("as")
  implicit val ec = system.dispatcher

  val parentActorRef: ActorRef = system.actorOf(Props(new ParentActor()))
  val childActorRef: ActorRef = system.actorOf(Props(new ChildActor(parentActorRef)))

  parentActorRef ! GetFromDb
  parentActorRef ! GetList

  childActorRef ! ProcessList

}

The List values 1,2,3 are printed so it appears the message fromDb is sent to ChildActor causing this foreach to be invoked :

case fromDb: List[Int] =>
      fromDb.foreach(println)

But as the message with fromDb is never explicitly sent what is causing

fromDb.foreach(println)

to be invoked ? Is it somehow sent implicitly ?


Solution

  • The child/parent naming is rather confusing since there is no parent/child relationship in this code. Better to call them something like DbProvider and DbClient.

    The sequence is this:

    1. The app sends ProcessList to the child actor (childActorRef ! ProcessList)
    2. The child actor sends GetList to the parent actor (parent ! GetList)
    3. The parent actor replies to the child actor with the db (sender() ! fromDb)
    4. The child actor receives the db and prints it out

    Note that there is a race condition in this code and theoretically the db could be List() rather than List(1,2,3).


    The only delivery guarantee in Akka is that two messages sent from one actor to the same actor will be processed in the order they were sent. Otherwise messages can be processed in any order and, in particular, messages sent via a third actor can overtake messages that are sent directly.

    In this case the ProcessList message could be processed by the ChildActor and the resulting GetList message could be processed by the ParentActor before the GetFromDb message from the app is processed by the ParentActor. If this happens, the reply to GetList would contain the default DB value.