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 ?
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:
ProcessList
to the child actor (childActorRef ! ProcessList
)GetList
to the parent actor (parent ! GetList
)sender() ! fromDb
)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.