The lifecycle of an Actor in Akka

Reading Time: 3 minutes

Hola Amigo, I presume you are already familiar with the Actor Model i.e why you are looking for the lifecycle of an actor. So, let’s dive deeper into the Akka Actor Lifecycle.

So, what happens when we create an actor and when it stops?

The lifecycle of an actor starts automatically after its creation(instantiation). A started actor can right away start processing messages. So, here’s what basic Akka actor lifecycle looks like:

diagrammatic representation of basic lifecycle of an actor.

Akka provides us with some hooks which we can override to specify a set instruction that should be executed before or after an actor reaches a particular state. For instance, we can override the preStart() hook to do any work/initialization before an actor starts serving. And postStop() execute after the actor is being stopped to release any acquired resources. An actor can stop itself and by any other actor as well.

After an actor reaches a terminated state, it now becomes available for garbage collection.
In Scala, to start an actor, we create an actor system and then instantiate an actor.

val system = ActorSystem("name_of_system")
val actor = system.actorOf(Props[ActorClass])

There are several ways to stop an actor.

context.stop(self) // to stop itself
or
system.stop(actorRef) // actor system will stop the specified actor
or
context.stop(actorRef) // to stop any other actor from current actor
or
actorRef ! PoisonPill // get into the mailbox at the end
or
actorRef ! Kill // get into the mailbox at the end

Now, what we need to know is when an actor stops, its child actors also forcefully stops before the parent. Stopping an actor is an asynchronous and recursive task. An actor finishes processing the current message, if any and wait for its child actor confirmation before its termination.

PoisonPill and Kill messages

These messages are treated as normal messages. The actor stops itself when it processes them. PoisonPill internally calls “context.stop(self())” whereas Kill throws an ActorKilledException. Both of these messages allow an actor to process all the messages which were already there in the mailbox.

What if we want to notify an actor about the termination of others?
Akka provides us with a mechanism called DeathWatch. An actor can watch another actor and get notified of its termination. Here’s how we do that,

context.watch(actorRef)

the current actor will watch for the specified actor’s termination. But we need to handle a message called Termination(value) so that we can check which actor died if the actor was watching multiple actors. If we don’t handle this message then we could get DeathPactException.
Note: An actor cannot monitor its own death, so never do

context.watch(self) // does not makes sense.

What happens to the lifecycle when an actor fails?

So, when some failure occurs, some other hooks come into picture, such as preRestart() and postRestart(). Here’s the full lifecycle of an actor:

Full Lifecycle of an Actor

These hooks, preRestart() and postRestart() are called whenever an actor is restarted by its supervisor upon failure. This is how we can define these hooks:

def preStart(): Unit = ()

def postStop(): Unit = ()

def preRestart(reason: Throwable, @unused message: Option[Any]): Unit = {
  context.children.foreach { child =>
    context.unwatch(child)
    context.stop(child)
  }
  postStop()
}

def postRestart(@unused reason: Throwable): Unit = {
  preStart()
}

Thank you for scrolling.
See you next time.