Stopping an actor is a routine operation that developers often find confusing. This is because each actor is like a micro process, and all interaction with an actor is asynchronous. So shutting down an actor is more complex than stopping entities in procedural code.

In this post, I’m going to review one of the common questions that I’ve heard lately:

How do I stop an actor?!

The Ways of Stopping an Actor

In short, there are three ways to stop an actor:

  1. Stop() the actor: stops the actor immediately after it finishes processing the current message.
  2. Kill the actor: this throws an ActorKilledException which will be logged and handled. The actor will stop immediately after it finishes processing the current message.
  3. Send the actor a PoisonPill: the actor will finish processing the messages currently in its mailbox, and then Stop.

Now let’s go over each in detail.

1) The Default: Stop() an Actor

This is the go-to method to stop an actor, and should be your default approach.

What Happens When I Stop() an Actor?

This is the sequence of events when you Stop() an actor:

  1. Actor receives the Stop message and suspends the actor’s Mailbox.
  2. Actor tells all its children to Stop. Stop messages propagate down the hierarchy below the actor.
  3. Actor waits for all children to stop.
  4. Actor calls PostStop lifecycle hook method for resource cleanup.
  5. Actor shuts down.

The point of this sequence is to make sure that an actor—and any hierarchy beneath it—have a clean shut down.

How Do I Use Stop()?

You Stop() an actor via the ActorContext, like this:

// targetActorRef dies immediately after
// it's finished processing current message
Context.Stop(targetActorRef);

When Do I Use Stop() vs. My Other Options?

Stop() is your go-to method and should be your default approach.

Use Stop unless you have a specific reason to use PoisonPill or Kill.

2) Graceful Shutdown: Sending an Actor a PoisonPill

PoisonPill shuts down the actor AFTER it finishes processing the messages in its mailbox.

What Happens When I Send an Actor a PoisonPill?

Like Stop, PoisonPill message is an auto-received, system-level message. But the actor handles a PoisonPill in a different manner than Stop. Rather than being handled immediately, the actor treats a PoisonPill like an ordinary message. The PoisonPill goes to the back of the actor’s mailbox.

The actor then processes the messages that are ahead of the PoisonPill in the mailbox. Once it reaches the PoisonPill, the actor tells itself to Stop and the sequence above will begin.

How Do I Use PoisonPill?

You send an actor a PoisonPill like this:

// targetActorRef dies once it processes
// all messages currently in mailbox
targetActorRef.Tell(PoisonPill.Instance);

When Do I Use PoisonPill vs. My Other Options?

You should use PoisonPill when you want the actor to process its mailbox before shutting down. There are many times this may come up, but they are use-case dependent.

3) Noisy on Purpose: Kill the Actor

Kill will cause an actor to be Stopped by its supervisor. In the process, the supervisor will log the ActorKilledException.

What Happens When I Kill an Actor?

  1. The actor throws an ActorKilledException. The actor’s supervisor logs this message.
    • Note: This suspends the actor mailbox from processing further user messages.
  2. The actor’s supervisor handles the ActorKilledException and issues a Stop directive.
  3. The actor will stop per the Stop sequence outlined above.

How Do I Use Kill?

You Kill an actor like this:

// targetActorRef dies immediately once it's finished
// processing current message, by throwing an ActorKilledException
// which is logged and handled by supervisor of targetActorRef
targetActorRef.Tell(Kill.Instance);

When Do I Use Kill vs. My Other Options?

When you want it to show in your logs that the actor was killed. This is pretty uncommon, but it does come up.

4) Bonus! Shutdown With Confirmation: GracefulStop

Bonus time! There is actually a fourth way to shut down an actor: GracefulStop. This convenience method wraps the methods above.

Sometimes you want to stop an actor and have your caller get confirmation that the target IActorRef has been stopped. For this, you can use the GracefulStop extension method.

Why Use Akka.NET?

What are the compelling reasons for using Akka.NET? What sorts of problems can it solve for you and your company?

Click here to subscribe to our mailing list and download our "Why Use Akka.NET?" white paper.

What Happens When I Send an Actor a GracefulStop?

By default, the actor will be sent a PoisionPill and will return your caller a Task<bool> which will complete within the timeout you specify.

There is also an overload you can use to swap in a different message for PoisonPill (e.g. Stop instead of PoisonPill).

How Do I Use GracefulStop?

You GracefulStop an actor like this:

// targetActorRef is sent a PoisonPill by default
// and returns a task whose result confirms shutdown within 5 seconds
var shutdown = targetActorRef.GracefulStop(TimeSpan.FromSeconds(5));

When Do I Use GracefulStop vs. My Other Options?

GracefulStop is if you want your caller to get confirmation that the actor was stopped within the timeout you set. This is just syntactic sugar for an external caller to retrieve proof that an actor was stopped within a fixed window of time.

PoisonPill, on its own, is also a graceful shutdown process. But it does not return the caller any confirmation of shutdown.

FAQs: Stopping an Actor

Is There A Way To Stop An Actor In The Middle Of Processing A Message?

No. Nothing about actors is synchronous, including stopping them.

In Between Killing an Actor and Its Supervisor Issuing It a Stop Directive, Can Other User Messages Get Processed?

No. When an actor faults—such as the ActorKilledException thrown by Kill— the actor’s mailbox is suspended and will not process any more user messages. The actor will still receive the system-level Stop directive and will then shut down.

Does An Actor Ever Get “stuck”? i.e. It Can’t Be Stopped

Sort of. If you have an actor that is stuck in some sort of infinite loop or that has indefinitely blocked while waiting for some call to complete, that could block the shutdown.

This is one more excellent reason to use the PipeTo pattern with behavior switching, and to always push dangerous work down and out to purpose-built actors via the Character Actor pattern.

What Happens to Messages That Queue Up Behind a PoisonPill?

They will be drained to DeadLetters, along with any other messages sent to the now-dead actor instance.

What About Shutting Down Routers?

One common case for a PoisonPill is shutting down a router.

Gracefully Shut Down a Pool Router

To gracefully shut down a Pool router, send a PoisonPill message to the router, which will shut down the router and in turn, the child routees that it created/supervises (recall that Pool routers are the parent of their routees.) That looks like this:

// gracefully shut down a pool router
poolRouter.Tell(PoisonPill.Instance);

Of course, you could also just Stop() a Pool router, which would also clean up its children. But that would potentially cause some of its messages to not be processed, which may not be what you want.

Gracefully Shut Down a Group Router

To gracefully shut down a Group router, send it a Broadcast message which contains a PoisonPill, and then send the router itself a PoisonPill.

That looks like this:

// gracefully shut down a group router
groupRouter.Tell(new Broadcast(PoisonPill.Instance));
groupRouter.Tell(PoisonPill.Instance);


###

Hopefully that clears it up! Do you have any questions about when or how to use each of these approaches to shutting down an actor?

If so, let us know in the comments below!

If you liked this post, you can share it with your followers or follow us on Twitter!
Written by Andrew Skotzko on September 9, 2015

 

 

Observe and Monitor Your Akka.NET Applications with Phobos

Did you know that Phobos can automatically instrument your Akka.NET applications with OpenTelemetry?

Click here to learn more.