Delay a shutdown when background actors must finish their tasks - scala

I have an Akka application with a Router Group composed by actors executing some jobs. When I detected a shutdown of my application, I want that my actors complete their work before shutting down the application completely. The use case of my question is in case of redeployment : I don't want to authorize it if current jobs are not executed.
To detect shutdown of my application, I'm using following code :
scala.sys.addShutdownHook {
// let actors finished their work
}
To make some tests, I add an infinite loop to see if the shutdown hook is blocked but application ends so this is not the expected behavior for me.
In order to let my actors finished their job, I will implement the idea in following article : http://letitcrash.com/post/30165507578/shutdown-patterns-in-akka-2
So now I'm searching a way to ignore the shutdown hook and closing all resources and application when all jobs have been executed by my workers.
Update after #Edmondo1984 comment
My main app :
val workers = this.createWorkerActors()
val masterOfWorkers = system.actorOf(Master.props(workers), name = "master")
this.monitorActors(supervisor,workers,masterOfWorkers)
this.addShutDownHook(system,masterOfWorkers,supervisor)
def monitorActors(supervisor : ActorRef,workers : List[ActorRef], master : ActorRef) : Unit = {
val actorsToMonitor = master +: workers
supervisor ! MonitorActors(actorsToMonitor)
}
def addShutDownHook
(
system : ActorSystem,
masterOfWorkers : ActorRef, // actor wrapping a ClusterGroup router, brodcasting a PoisonPill to each worker
supervisor : ActorRef
) : Unit = {
scala.sys.addShutdownHook {
implicit val timeout = Timeout(10.hours) // How to block here until actors are terminated ?
system.log.info("Send a Init Shutdown to {}", masterOfWorkers.path.toStringWithoutAddress)
masterOfWorkers ! InitShutDown
system.log.info("Gracefully shutdown all actors of ActorSystem {}", system.name)
Await.result((supervisor ? InitShutDown), Duration.Inf)
system.log.info("Gracefully shutdown actor system")
Await.result(system.terminate(), 1.minutes)
system.log.info("Gracefully shutdown Akka management ...")
Await.result(AkkaManagement(system).stop(), 1.minutes)
System.exit(0)
}
}
Supervisor actor
case class Supervisor() extends Actor with ActorLogging {
var numberOfActorsToWatch = 0L
override def receive: Receive = {
case MonitorActors(actorsToMonitor) =>
log.info("Monitor {} actors, received by {}", actorsToMonitor.length, sender().path)
this.numberOfActorsToWatch = actorsToMonitor.length
actorsToMonitor foreach(context.watch(_))
case Terminated(terminatedActor) if this.numberOfActorsToWatch > 0 =>
log.info("Following actor {} is terminated. Remaining alives actors is {}", terminatedActor.path.toStringWithoutAddress, this.numberOfActorsToWatch)
this.numberOfActorsToWatch -= 1
case Terminated(lastTerminatedActor) if this.numberOfActorsToWatch == 0 =>
log.info("Following actor {} is terminated. All actors has been terminated",lastTerminatedActor.path.toStringWithoutAddress, this.numberOfActorsToWatch)
// what I can do here ?
//context.stop(self)
}
}
application.conf
akka {
actor {
coordinated-shutdown {
default-phase-timeout = 20 s
terminate-actor-system = off
exit-jvm = off
run-by-jvm-shutdown-hook = off
}
}
}
I don't know how to block the main thread, the one killing finally the app.

This is easily achieved by placing a supervisor actor in front of your hierarchy:
When you need shutdown, you send a message to the supervisor and you cache the sender A
The supervisor subscribes to children death through DeadWatch (see https://doc.akka.io/docs/akka/2.5/actors.html)
The supervisor will set a counter variable to the number of children, then sends a message to all the childs telling them to shut down asap. When the childs are done, they will terminate themselves. The supervisor will receive a notification and decrease the counter
When the counter reach 0, the supervisor will send a message to A saying ShutdownTerminated and terminates itself.
Your code will become like so:
class Supervisor extends Actor with ActorLogging {
var shutdownInitiator:ActorRef = _
var numberOfActorsToWatch = 0L
override def receive: Receive = {
case InitShutdown =>
this.numberOfActorsToWatch = context.children.length
context.children.foreach(context.watch(_))
context.children.foreach { s => s ! TerminateSomehow }
shutdownInitiator = sender
case Terminated(terminatedActor) if this.numberOfActorsToWatch > 0 =>
log.info("Following actor {} is terminated. Remaining alives actors is {}", terminatedActor.path.toStringWithoutAddress, this.numberOfActorsToWatch)
this.numberOfActorsToWatch -= 1
case Terminated(lastTerminatedActor) if this.numberOfActorsToWatch == 0 =>
log.info("Following actor {} is terminated. All actors has been terminated",lastTerminatedActor.path.toStringWithoutAddress, this.numberOfActorsToWatch)
// what I can do here ?
shutdownInitiator ! Done
context.stop(self)
}
}
On your shutdown hook, you need a reference to the supervisor and use the ask pattern:
Await.result(supervisor ? InitShutdown, Duration.Inf)
actorSystem.terminate()

Related

how to watch multiple akka actors for termination

I have akka system which is basically two producer actors that send messages to one consumer actor. In a simplified form I have something like this:
class ProducerA extends Actor {
def receive = {
case Produce => Consumer ! generateMessageA()
}
... more code ...
}
class ProducerB extends Actor {
def receive = {
case Produce => Consumer ! generateMessageB()
}
... more code ...
}
class Consumer extends Actor {
def receive = {
case A => handleMessageA(A)
case B => handleMessageB(B)
}
... more code ...
}
And they are all siblings of the same akka system.
I am trying to figure out how to terminate this system gracefully. This means that on shutdown I want ProducerA and ProducerB to stop immediately and then I want Consumer to finish processing whatever messages are left in the message queue and then shutdown.
It seems like what I want is for the Consumer actor to be able to watch for the termination of both ProducerA and ProducerB. Or generally, it seems like what I want is to be able to send a PoisonPill message to the Consumer after both the producers are stopped.
https://alvinalexander.com/scala/how-to-monitor-akka-actor-death-with-watch-method
The above tutorial has a pretty good explanation of how one actor can watch for the termination of one other actor but not sure how an actor could watch for the termination of multiple actors.
An actor can watch multiple actors simply via multiple invocations of context.watch, passing in a different ActorRef with each call. For example, your Consumer actor could watch the termination of the Producer actors in the following way:
case class WatchMe(ref: ActorRef)
class Consumer extends Actor {
var watched = Set[ActorRef]()
def receive = {
case WatchMe(ref) =>
context.watch(ref)
watched = watched + ref
case Terminated(ref) =>
watched = watched - ref
if (watched.isEmpty) self ! PoisonPill
// case ...
}
}
Both Producer actors would send their respective references to Consumer, which would then monitor the Producer actors for termination. When the Producer actors are both terminated, the Consumer sends a PoisonPill to itself. Because a PoisonPill is treated like a normal message in an actor's mailbox, the Consumer will process any messages that are already enqueued before handling the PoisonPill and shutting itself down.
A similar pattern is described in Derek Wyatt's "Shutdown Patterns in Akka 2" blog post, which is mentioned in the Akka documentation.
import akka.actor._
import akka.util.Timeout
import scala.concurrent.duration.DurationInt
class Producer extends Actor {
def receive = {
case _ => println("Producer received a message")
}
}
case object KillConsumer
class Consumer extends Actor {
def receive = {
case KillConsumer =>
println("Stopping Consumer After All Producers")
context.stop(self)
case _ => println("Parent received a message")
}
override def postStop(): Unit = {
println("Post Stop Consumer")
super.postStop()
}
}
class ProducerWatchers(producerListRef: List[ActorRef], consumerRef: ActorRef) extends Actor {
producerListRef.foreach(x => context.watch(x))
context.watch(consumerRef)
var producerActorCount = producerListRef.length
implicit val timeout: Timeout = Timeout(5 seconds)
override def receive: Receive = {
case Terminated(x) if producerActorCount == 1 && producerListRef.contains(x) =>
consumerRef ! KillConsumer
case Terminated(x) if producerListRef.contains(x) =>
producerActorCount -= 1
case Terminated(`consumerRef`) =>
println("Killing ProducerWatchers On Consumer End")
context.stop(self)
case _ => println("Dropping Message")
}
override def postStop(): Unit = {
println("Post Stop ProducerWatchers")
super.postStop()
}
}
object ProducerWatchers {
def apply(producerListRef: List[ActorRef], consumerRef: ActorRef) : Props = Props(new ProducerWatchers(producerListRef, consumerRef))
}
object AkkaActorKill {
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem("AkkaActorKill")
implicit val timeout: Timeout = Timeout(10 seconds)
val consumerRef = actorSystem.actorOf(Props[Consumer], "Consumer")
val producer1 = actorSystem.actorOf(Props[Producer], name = "Producer1")
val producer2 = actorSystem.actorOf(Props[Producer], name = "Producer2")
val producer3 = actorSystem.actorOf(Props[Producer], name = "Producer3")
val producerWatchers = actorSystem.actorOf(ProducerWatchers(List[ActorRef](producer1, producer2, producer3), consumerRef),"ProducerWatchers")
producer1 ! PoisonPill
producer2 ! PoisonPill
producer3 ! PoisonPill
Thread.sleep(5000)
actorSystem.terminate
}
}
It can be implemented using ProducerWatchers actor, which manages producers killed, once all the producers are killed you can kill the Consumer actor, and then the ProducerWatchers actor.
so the solution I ended up going with was inspired by Derek Wyatt's terminator pattern
val shutdownFut = Future.sequence(
Seq(
gracefulStop(producerA, ProducerShutdownWaitSeconds seconds),
gracefulStop(producerB, ProducerShutdownWaitSeconds seconds),
)
).flatMap(_ => gracefulStop(consumer, ConsumerShutdownWaitSeconds seconds))
Await.result(shutdownFut, (ProducerShutdownWaitSeconds seconds) + (ConsumerShutdownWaitSeconds seconds))
This was more or less exactly what I wanted. The consumer shutdown waits for the producers to shutdown based on the fulfillment of futures. Furthermore, the whole shutdown itself results in a future which you can wait on therefore being able to keep the thread up long enough for everything to clean up properly.

Send message to actor after restart from Supervisor

I am using BackoffSupervisor strategy to create a child actor that has to process some message. I want to implement a very simple restart strategy, in which in case of exception:
Child propagates failing message to supervisor
Supervisor restarts child and sends the failing message again.
Supervisor gives up after 3 retries
Akka persistence is not an option
So far what I have is this:
Supervisor definition:
val childProps = Props(new SenderActor())
val supervisor = BackoffSupervisor.props(
Backoff.onFailure(
childProps,
childName = cmd.hashCode.toString,
minBackoff = 1.seconds,
maxBackoff = 2.seconds,
randomFactor = 0.2
)
.withSupervisorStrategy(
OneForOneStrategy(maxNrOfRetries = 3, loggingEnabled = true) {
case msg: MessageException => {
println("caught specific message!")
SupervisorStrategy.Restart
}
case _: Exception => SupervisorStrategy.Restart
case _ ⇒ SupervisorStrategy.Escalate
})
)
val sup = context.actorOf(supervisor)
sup ! cmd
Child actor that is supposed to send the e-mail, but fails (throws some Exception) and propagates Exception back to supervisor:
class SenderActor() extends Actor {
def fakeSendMail():Unit = {
Thread.sleep(1000)
throw new Exception("surprising exception")
}
override def receive: Receive = {
case cmd: NewMail =>
println("new mail received routee")
try {
fakeSendMail()
} catch {
case t => throw MessageException(cmd, t)
}
}
}
In the above code I wrap any exception into custom class MessageException that gets propagated to SupervisorStrategy, but how to propagate it further to the new child to force reprocessing? Is this the right approach?
Edit. I attempted to resent the message to the Actor on preRestart hook, but somehow the hook is not being triggered:
class SenderActor() extends Actor {
def fakeSendMail():Unit = {
Thread.sleep(1000)
// println("mail sent!")
throw new Exception("surprising exception")
}
override def preStart(): Unit = {
println("child starting")
}
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
reason match {
case m: MessageException => {
println("aaaaa")
message.foreach(self ! _)
}
case _ => println("bbbb")
}
}
override def postStop(): Unit = {
println("child stopping")
}
override def receive: Receive = {
case cmd: NewMail =>
println("new mail received routee")
try {
fakeSendMail()
} catch {
case t => throw MessageException(cmd, t)
}
}
}
This gives me something similar to following output:
new mail received routee
caught specific message!
child stopping
[ERROR] [01/26/2018 10:15:35.690]
[example-akka.actor.default-dispatcher-2]
[akka://example/user/persistentActor-4-scala/$a/1962829645] Could not
process message sample.persistence.MessageException:
Could not process message <stacktrace>
child starting
But no logs from preRestart hook
The reason that the child's preRestart hook is not invoked is because Backoff.onFailure uses BackoffOnRestartSupervisor underneath the covers, which replaces the default restart behavior with a stop-and-delayed-start behavior that is consistent with the backoff policy. In other words, when using Backoff.onFailure, when a child is restarted, the child's preRestart method is not called because the underlying supervisor actually stops the child, then starts it again later. (Using Backoff.onStop can trigger the child's preRestart hook, but that's tangential to the present discussion.)
The BackoffSupervisor API doesn't support the automatic resending of a message when the supervisor's child restarts: you have to implement this behavior yourself. An idea for retrying messages is to let the BackoffSupervisor's supervisor handle it. For example:
val supervisor = BackoffSupervisor.props(
Backoff.onFailure(
...
).withReplyWhileStopped(ChildIsStopped)
).withSupervisorStrategy(
OneForOneStrategy(maxNrOfRetries = 3, loggingEnabled = true) {
case msg: MessageException =>
println("caught specific message!")
self ! Error(msg.cmd) // replace cmd with whatever the property name is
SupervisorStrategy.Restart
case ...
})
)
val sup = context.actorOf(supervisor)
def receive = {
case cmd: NewMail =>
sup ! cmd
case Error(cmd) =>
timers.startSingleTimer(cmd.id, Replay(cmd), 10.seconds)
// We assume that NewMail has an id field. Also, adjust the time as needed.
case Replay(cmd) =>
sup ! cmd
case ChildIsStopped =>
println("child is stopped")
}
In the above code, the NewMail message embedded in the MessageException is wrapped in a custom case class (in order to easily distinguish it from a "normal"/new NewMail message) and sent to self. In this context, self is the actor that created the BackoffSupervisor. This enclosing actor then uses a single timer to replay the original message at some point. This point in time should be far enough in the future such that the BackoffSupervisor can potentially exhaust SenderActor's restart attempts, so that the child can have ample opportunity to get in a "good" state before it receives the resent message. Obviously this example involves only one message resend regardless of the number of child restarts.
Another idea is to create a BackoffSupervisor-SenderActor pair for every NewMail message, and have the SenderActor send the NewMail message to itself in the preStart hook. One concern with this approach is the cleaning up of resources; i.e., shutting down the BackoffSupervisors (which will, in turn, shut down their respective SenderActor children) when the processing is successful or when the child restarts are exhausted. A map of NewMail ids to (ActorRef, Int) tuples (in which the ActorRef is a reference to a BackoffSupervisor actor, and the Int is the number of restart attempts) would be helpful in this case:
class Overlord extends Actor {
var state = Map[Long, (ActorRef, Int)]() // assuming the mail id is a Long
def receive = {
case cmd: NewMail =>
val childProps = Props(new SenderActor(cmd, self))
val supervisor = BackoffSupervisor.props(
Backoff.onFailure(
...
).withSupervisorStrategy(
OneForOneStrategy(maxNrOfRetries = 3, loggingEnabled = true) {
case msg: MessageException =>
println("caught specific message!")
self ! Error(msg.cmd)
SupervisorStrategy.Restart
case ...
})
)
val sup = context.actorOf(supervisor)
state += (cmd.id -> (sup, 0))
case ProcessingDone(cmdId) =>
state.get(cmdId) match {
case Some((backoffSup, _)) =>
context.stop(backoffSup)
state -= cmdId
case None =>
println(s"${cmdId} not found")
}
case Error(cmd) =>
val cmdId = cmd.id
state.get(cmdId) match {
case Some((backoffSup, numRetries)) =>
if (numRetries == 3) {
println(s"${cmdId} has already been retried 3 times. Giving up.")
context.stop(backoffSup)
state -= cmdId
} else
state += (cmdId -> (backoffSup, numRetries + 1))
case None =>
println(s"${cmdId} not found")
}
case ...
}
}
Note that SenderActor in the above example takes a NewMail and an ActorRef as constructor arguments. The latter argument allows the SenderActor to send a custom ProcessingDone message to the enclosing actor:
class SenderActor(cmd: NewMail, target: ActorRef) extends Actor {
override def preStart(): Unit = {
println(s"child starting, sending ${cmd} to self")
self ! cmd
}
def fakeSendMail(): Unit = ...
def receive = {
case cmd: NewMail => ...
}
}
Obviously the SenderActor is set up to fail every time with the current implementation of fakeSendMail. I'll leave the additional changes needed in SenderActor to implement the happy path, in which SenderActor sends a ProcessingDone message to target, to you.
In the good solution that #chunjef provides, he alert about the risk of schedule a job resend before the backoff supervisor has started the worker
This enclosing actor then uses a single timer to replay the original message at some point. This point in time should be far enough in the future such that the BackoffSupervisor can potentially exhaust SenderActor's restart attempts, so that the child can have ample opportunity to get in a "good" state before it receives the resent message.
If this happens, the scenario will be jobs going to dead letters and no further progress will be done.
I've made a simplified fiddle with this scenario.
So, the schedule delay should be larger than the maxBackoff, and this could represent an impact in job completion time.
A possible solution to avoid this scenario is making the worker actor to send a message to his father when is ready to work, like here.
The failed child actor is available as the sender in your supervisor strategy. Quoting https://doc.akka.io/docs/akka/current/fault-tolerance.html#creating-a-supervisor-strategy:
If the strategy is declared inside the supervising actor (as opposed
to within a companion object) its decider has access to all internal
state of the actor in a thread-safe fashion, including obtaining a
reference to the currently failed child (available as the sender of
the failure message).
Sending emails is a dangerous operation with some third party software in your case. Why not to apply Circuit Breaker pattern and skip the sender actor entirely? Also, you can still have an actor (with some Backoff Supervisor) and Circuit Breaker inside it (if that makes sense for you).

Akka Stop/Kill actors after processing completion

I would like to know how to efficiently cleanup akka actors that are created on the fly.
To give a bit of background:
Actor Hierarchy created per event.
Supervisor -> child1 -> grandChild1
In my application the supervisor actor dynamically creates other actors(on a periodic event). I wanted to cleanup the actors after the processing steps for that event is complete.
So, I would like to kill all the child actors once the processing is complete.
I am propagating a message (successfulProcessing) after successful completion in the reverse of creation. (Grandchild1 -> child1 -> Supervisor ).
In the Supervisor, I will send a PoisonPill to the child actor.
This is the code for the Supervisor actor.
class Supervisor extends Actor {
def receive={
case onEvent: OnEvent =>
//Create child actor and send message
case successfulProcessing =>
sender() ! PoisonPill
}
override val supervisorStrategy = AllForOneStrategy() {
case e: Exception =>
Stop
}
}
Is this the correct approach to cleanup the dynamically created actors. If there is any disadvantage to this approach or is there a pattern to be followed?
As per Akka Document 2.4.14 ,
Better way to handle PoisonPill/Kill message is to broadcast them.
ActorRef ! Broadcast(PoisonPill)
Note: Do not broadcast messages when using BalancingPool
The pattern I've seen is to have an actor who manages other actors. In the following example from this tutorial, actor1 manages actor2, where actor2 does all the work. actor1 then cleans up.
case class StartCounting(n: Int, actor: ActorRef)
case class CountDown(n: Int)
class CountDownActor extends Actor {
def receive = {
case StartCounting(n, actor) =>
println(n)
actor ! CountDown(n-1)
case CountDown(n) =>
if(n > 0) {
println(n)
sender ! CountDown(n-1)
} else {
context.system.shutdown()
}
}
}
object Main extends App {
val system = ActorSystem("HelloSystem")
// default Actor constructor
val actor1 = system.actorOf(Props[CountDownActor], name = "manager")
val actor2 = system.actorOf(Props[CountDownActor], name = "worker")
actor1 ! StartCounting(10, actor2)
}
You can think of this like recursion: base and inductive cases. You can apply this at depth for all sibling actors being managed their parent.

Handling Faults in Akka actors

I've a very simple example where I've an Actor (SimpleActor) that perform a periodic task by sending a message to itself. The message is scheduled in the constructor for the actor. In the normal case (i.e., without faults) everything works fine.
But what if the Actor has to deal with faults. I've another Actor (SimpleActorWithFault). This actor could have faults. In this case, I'm generating one myself by throwing an exception. When a fault happens (i.e., SimpleActorWithFault throws an exception) it is automatically restarted. However, this restart messes up the scheduler inside the Actor which no longer functions as excepted. And if the faults happens rapidly enough it generates more unexpected behavior.
My question is what's the preferred way to dealing with faults in such cases? I know I can use Try blocks to handle exceptions. But what if I'm extending another actor where I cannot put a Try in the super class or some case when I'm an excepted fault happens in the actor.
import akka.actor.{Props, ActorLogging}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import akka.actor.Actor
case object MessageA
case object MessageToSelf
class SimpleActor extends Actor with ActorLogging {
//schedule a message to self every second
context.system.scheduler.schedule(0 seconds, 1 seconds, self, MessageToSelf)
//keeps track of some internal state
var count: Int = 0
def receive: Receive = {
case MessageA => {
log.info("[SimpleActor] Got MessageA at %d".format(count))
}
case MessageToSelf => {
//update state and tell the world about its current state
count = count + 1
log.info("[SimpleActor] Got scheduled message at %d".format(count))
}
}
}
class SimpleActorWithFault extends Actor with ActorLogging {
//schedule a message to self every second
context.system.scheduler.schedule(0 seconds, 1 seconds, self, MessageToSelf)
var count: Int = 0
def receive: Receive = {
case MessageA => {
log.info("[SimpleActorWithFault] Got MessageA at %d".format(count))
}
case MessageToSelf => {
count = count + 1
log.info("[SimpleActorWithFault] Got scheduled message at %d".format(count))
//at some point generate a fault
if (count > 5) {
log.info("[SimpleActorWithFault] Going to throw an exception now %d".format(count))
throw new Exception("Excepttttttiooooooon")
}
}
}
}
object MainApp extends App {
implicit val akkaSystem = akka.actor.ActorSystem()
//Run the Actor without any faults or exceptions
akkaSystem.actorOf(Props(classOf[SimpleActor]))
//comment the above line and uncomment the following to run the actor with faults
//akkaSystem.actorOf(Props(classOf[SimpleActorWithFault]))
}
The correct way is to push down the risky behavior into it's own actor. This pattern is called the Error Kernel pattern (see Akka Concurrency, Section 8.5):
This pattern describes a very common-sense approach to supervision
that differentiates actors from one another based on any volatile
state that they may hold.
In a nutshell, it means that actors whose state is precious should not
be allowed to fail or restart. Any actor that holds precious data is
protected such that any risky operations are relegated to a slave
actor who, if restarted, only causes good things to happen.
The error kernel pattern implies pushing levels of risk further down
the tree.
See also another tutorial here.
So in your case it would be something like this:
SimpleActor
|- ActorWithFault
Here SimpleActor acts as a supervisor for ActorWithFault. The default supervising strategy of any actor is to restart a child on Exception and escalate on anything else:
http://doc.akka.io/docs/akka/snapshot/scala/fault-tolerance.html
Escalating means that the actor itself may get restarted. Since you really don't want to restart SimpleActor you could make it always restart the ActorWithFault and never escalate by overriding the supervisor strategy:
class SimpleActor {
override def preStart(){
// our faulty actor --- we will supervise it from now on
context.actorOf(Props[ActorWithFault], "FaultyActor")
...
override val supervisorStrategy = OneForOneStrategy () {
case _: ActorKilledException => Escalate
case _: ActorInitializationException => Escalate
case _ => Restart // keep restarting faulty actor
}
}
To avoid messing up the scheduler:
class SimpleActor extends Actor with ActorLogging {
private var cancellable: Option[Cancellable] = None
override def preStart() = {
//schedule a message to self every second
cancellable = Option(context.system.scheduler.schedule(0 seconds, 1 seconds, self, MessageToSelf))
}
override def postStop() = {
cancellable.foreach(_.cancel())
cancellable = None
}
...
To correctly handle exceptions (akka.actor.Status.Failure is for correct answer to an ask in case of Ask pattern usage by sender):
...
def receive: Receive = {
case MessageA => {
try {
log.info("[SimpleActor] Got MessageA at %d".format(count))
} catch {
case e: Exception =>
sender ! akka.actor.Status.Failure(e)
log.error(e.getMessage, e)
}
}
...

Actor that waits for completing all jobs in children before exit

Can't figure out how to resolve following problem: I have a few actors (workers) that execute tasks in some way when they recieve (I mean react) them. A main actor (foreman) controls this process and can recieve task to stop work. In this case main actor must stop creating new tasks and wait when workers will finish all existing tasks and only then main actor should exit.
import actors.Actor
import actors.Actor._
class Foreman extends Actor{
val workerA = new WorkerA
val workerB = new WorkerB
val workerC = new WorkerC
self.link(workerA)
self.link(workerB)
self.link(workerC)
def act{
workerA.start
workerB.start
workerC.start
// adding tasks to workers somehow
//...
loop{
case ResultOfTask(res) => //...
case Stop => //workers mustn't immediately stop but must finish their tasks and then exit
case ProductionAccident => //...
}
}
}
case class Task(activity:String)
case class ResultOfTask(result:String)
trait Worker extends Actor{
def act{
loop{
react{
case Task(activity) => sender ! processTask(activity)
}
}
}
def processTask(activity:String):ResultOfTask
}
To solve this I wrote following code:
def condition = workerA.getState!=State.Suspended && workerB.getState!=State.Suspended && workerC.getState!=State.Suspended && mailboxSize == 0
case Stop => {
if(condition) exit("sweet dreams") else continue
}
to check if main actor should exit. Another variant to have counder in "Worker" trait, increment it when worker recieves message and decrement when it reponses.
trait Worker extends Actor {
private var count = 0
def act {
loop{
react{
case Task(activity) => {
count += 1
sender ! processTask(activity)
count -= 1
}
}
}
}
def hasDoneAllTasks = count == 0
def processTask(activity: String): ResultOfTask
}
And "condition" function in "Foreman" will be
def condition = workerA.hasDoneAllTasks && workerB.hasDoneAllTasks && workerC.hasDoneAllTasks && mailboxSize == 0
I hope there are better solutions and you will propose them.
If the foreman always expects an answer from the workers, the solution is easy: the foreman maintains a counter, and each time it sends a message it increments it and each time it receives one from a worker it decrements it. Any time the counter is zero it is free to stop itself (assuming that no-one else sends messages to the workers).
If the foreman does not always expect an answer from the workers, you can make this the case by having a no-content message
case object Done { }
and having the workers reply with that when they're finished. Then, see above.
If the foreman is not the only one talking to the workers, or you want there to be less chatter in the background, then the foreman and the workers will have to negotiate. The foreman can send
case object RequestStop { }
and the workers will do something graceful and reply with Done when they're done. When the foreman receives as many Done messages as it has sent RequestStops, it is free to exit.
Why not include a reference to the Foreman actor when sending jobs to the Workers? Then, when the workers shut down they can send a notification to the Foreman. Each time the Foreman receives a worker shut down message it logs it and sees if all the workers have completed. If so, it shuts itself down too.
My approach is to do all calculations in the Foreman.
You didn't write how the foreman creates tasks, so I'll assume it is in response to a message
class Foreman extends Actor{
var numTasks: Int = 0
var shouldExit = false
def act = loop {
react {
case t: Task =>
if (!shouldExit) {
numTasks += 1
selectWorker ! t
} else {
// send some kind of error status to sender
}
case ResultOfTask(rest) =>
numTasks -= 1
// ....
if (numTasks == 0 && shouldExit) exit
case Stop() => shoudExit = true
}
An improvement then is to prioritize Stop so that it is handled even if there are Task messages in the queue
def act = loop {
reactWithin(0) {
case Stop() => shouldStop = true
case TIMEOUT => react {
case t: Task =>
if (!shouldExit) {
numTasks += 1
selectWorker ! t
} else {
// send some kind of error status to sender
}
case ResultOfTask(rest) =>
numTasks -= 1
// ....
if (numTasks == 0 && shouldExit) exit
case Stop() => shoudExit = true
}
}
One thing you can take advantage of is the trapExit, case Exit and exit, system. In your main actor, you can set trapExit to true:
// Foreman
def act() {
trapExit = true
link(workerA)
link(workerB)
...
}
This means that your foreman actor will get an Exit message when the worker process terminates, including a reason:
// Foreman
def act() {
....
loop { react {
case Exit (worker: Actor, reason: AnyRef) => {
// decrement counter, or list of workers, and exit if empty
}
...
}}
}
You can pattern match on the reason parameter. For instance, in your worker class, you might exit using a variety of case classes indicating what the foreman should do:
// Worker:
exit(WorkComplete)
exit(Emergency)
etc, and so on. When your worker throws an exception, it'll terminate and send the linked process an Exit message containing the exception. Given this sort of thing, you can end up with something like:
// Foreman
def act() {
....
loop { react {
case Exit (worker: Actor, reason: WorkComplete) => {
// decrement counter, or list of workers, and exit if empty
}
case Exit (worker: Actor, reason: TasksExhausted) => {
// log something, or make shut down worker if too many are exhausted
}
case Exit (worker: Actor, reason: Exception) => {
// log the exception, then restart the actor
}
...
}}
}
It's unclear from your initial question if you want the workers to keep on working even when they're done until the foreman tells them they should exit when it's time. If that's the case, sending the workers a messages telling them to "exit when finished" works, and you can tell they've finished by using the trapExit mechanism.
Hope this spurs an interesting solution!