In what order are actors stopped? In what order does a watcher receive the Terminated signals?

I will start the post with the usual introduction:
I am making some questions in the context of my master thesis, for which I desire to understand Akka’s design. In particular, I always wonder “Why does Akka do things this way? Is there a philosophical or technical reason?”.

Suppose that we have the actor hierarchy depicted below.

Actor w is watching all the numbered actors, i.e., 1, 2, 3, 4, 5, 6, 7.

Suppose that actor 1 stops; either voluntarily, i.e., by returning Behaviors.stopped , or because it is stopped by the user guardian.

The first question is: In what order are the numbered actors stopped?

In my mind, the following makes sense, but I don’t know if it is the actual way Akka does it:

  • Actors 2, 3 and 4 concurrently receive the signal to stop.
  • After 2 has received the signal to stop, actors 5, 6 and 7 concurrently receive the signal to stop.

To me, it can also make sense that all actors in the subtree rooted in 1 are stopped concurrently.

How does Akka does it? What are the respective advantages and disadvantages of these two approaches?

The second question is: In what order does actor w receive the Terminated signals?

Maybe the two design decisions are inter-dependent.

Thanks in advance to anyone who will participate in the discussion. :slight_smile:

When an actor is stopped, it will trigger async/parallel stop of all its children, pause normal message processing until it has observed all children terminate, then complete its own termination. Signalling to its parent and watchers that it stopped is sent as a part of that completion.

Since termination is an async/parallel task, there is no guarantee of the order of the death watch notification. The parent of an actor is sent a notification before sending a notification to all watchers, but those messages needs to be scheduled and are processed asynchronously so it could still potentially (although maybe unlikely) arrive in any order.

1 Like

Thanks, Johan.

The termination procedure you described is essentially bottom up.
Why is it so, as opposed to a top down procedure where the parent stops its children and terminates right away, without first waiting for their termination?

Thanks.

An actor can never be running with a parent that has terminated, both for typed and “classic” Akka actors, but especially important in classic actors where the parent is responsible for supervising its children.

Because of this I don’t think there is a sensible design where the actors and their children could stop in a different order.

1 Like

From your last reply, it seems that the reason is that a parent actor (even when it has stopped and has triggered termination of its children) is required to be always able to handle the failure of a child (thus, even when such failure occurs while terminating).

Is my deduction correct? Are there other reasons than this?

In the previous reply, you said that, when an actor is stopping, it pauses normal message processing:
Does that mean that the actor will process signals only? In light of my deduction above, this would allow for the Terminated signal from the child to arrive, and for the parent to handle the failure.

However, the requirement in question is still strange. Why should we want to handle the child failure if the child is about to stop anyways?

Thanks for you time, this information is really precious.

Good point. I think I was wrong about this, looking at the logic, the failure handling is disabled during shutdown, so supervision will in fact not be triggered if a current message is processed when the actor stop is triggered and throws an exception.

So in conclusion a child will never outlive its parent, however it can in practice not interact with a parent triggering the termination in any other way than the internal signalling that the child itself completed termination.

Something that might have more practical consequence is that a postStop() for an actor is only invoked once all children has stopped (and completed their postStop()s).

1 Like

So in conclusion a child will never outlive its parent

Something that might have more practical consequence is that a postStop() for an actor is only invoked once all children has stopped (and completed their postStop()s).

Could you please elaborate a bit on these points? What advantages do they bring?

My guess is that they have consequences on the order in which actors can acquire an release resources.

The first thing that comes to mind is some form of pooling of a resource where children are handed a resource or own their own resource but that resource has some form of dependency to the resource that the parent manages and where the “child” resources should always be stopped/cleaned up before their “parent” resource is.