left-icon

Akka.NET Succinctly®
by Zoran Maksimovic

Previous
Chapter

of
A
A
A

CHAPTER 9

Supervision

Supervision


In the previous chapters, we have mentioned several times the fact that actors have dependencies and can define a supervision strategy. In this chapter, we are going to learn more about what the supervision is and how it works.

What is supervision?

Supervision describes the dependency relationship between actors. This intrinsically means that an actor can create other actors and delegate tasks to them. This ability also comes with the responsibility of handling those subactor’s failures.

The actor that creates other actors is also known as a supervisor, and its children are known as subordinates. The failures of subordinates will be propagated to the supervisor actor.

In general, there are only a handful of ways to handle failures propagated back; let’s take a look at them:

Possible supervision options

Figure 28: Possible supervision options

In Akka.NET terms, those actions to be taken are also known as Directives.

The following table briefly summarizes the various options.

Table 6: Possible supervision directives explained

Resume

Resuming a subordinate means that the subactor will be restarted, but it still keeps its internal state. This means the actor, after resuming, will keep doing its work from the point it stopped.

Resuming an actor also means resuming all of its own subordinates. This is a chained operation!

Restart

This would completely clear out any subordinate’s internal state. However, the messages that are in the queue remain intact!

This also means all of the subordinates’ created actors will be restarted.

Stop

Stops the subordinate permanently.

Escalate

This means the supervisor actor is not handling the error itself, but the handling of it is escalated to the supervisor’s parent actor.

The supervisor actor is failing itself!

After looking at these possible options, it becomes very clear that supervision is about forming a recursive fault-handling structure. Just remember what we have mentioned previously: the failure-handling is part of the actor model itself!

Actor supervision hierarchy

Recursive fault-handling structure brings us to the next topic: If our supervisor (actor) is not handling the errors, then who is the last supervisor to do so? Fortunately, Akka.NET ships an out-of-the-box the mechanism for this, by supplying the highest level actors that are at the top of the hierarchy. Those actors are always there, and provide the latest level of error propagation.

Let’s take a look at the structure.

Actors’ supervision guardians

Figure 29: Actors’ supervision guardians

User guardian

The user guardian defines the entry point for all of the actors defined by the application developer. Those are all the actors we are actually discussing in this book, as opposed to the system guardian, by which actors are actually managed and created by Akka.NET itself. Therefore, all of the actors created by actorSystem.ActorOf(), and their subsequent subactors, fall into this section.

User guardian supervision actors

Figure 30: User guardian supervision actors

In the previous chapter we saw that when we create an actor, the path created is under the /user section. All of the user guardian actors will be placed there.

System guardian

System guardian was introduced in order to enable the shutting down of the system in an orderly manner. We don’t have to worry about creating the system hierarchy, as the system will take care of it. System guardian actors will be recognized by the path starting with /system.

Root guardian

Root guardian is the ultimate place where the decision should be made. If the errors are propagated to the root guardian, it will make sure that the user guardian is shut down (or handled), therefore bringing down the whole system.

Supervision strategy

Now that we know which directives are available in order to handle the failure conditions, we have to understand how the parent handles this in terms of code. This is done through Supervision Strategies.

Whenever we create a new actor, the default supervision strategy is assigned to the actor, which is to Resume the actor. We obviously can override this behavior.

There are different strategies we can use, such as:

  • OneForOneStrategy: This strategy will apply the directive only to the failing actor that has thrown an error. No other actors are affected.
  • AllForOneStrategy: This strategy applies the directive to all of its children. This is particularly useful if the children actors are dependent on each other, so that the failure of one brings the logical failure of others.

The example we are going to build is a continuation of the music player we saw in Chapter 7, where the MusicPlayerCoordinatorActor creates a new child MusicPlayerActor for every user that plays a song, so that every user has its own MusicPlayer.

Let’s imagine a situation where the song played is not available, and therefore, the MusicPlayerActor will be in the faulted state. Just for this example, let’s imagine that when the player tries to play the song “Bohemian Rhapsody,” the SongNotAvailableException is thrown. The same happens when “Stairway to Heaven” plays, but this time, MusicSystemCorruptedException gets thrown. This is obviously hardcoded, and just to demonstrate that the actor is able to throw messages!

The following code contains the changes as described.

Code Listing 56: MusicPlayerActor throws exceptions

private void PlaySong(PlaySongMessage message)

{

    CurrentSong = message;

    if (message.Song == "Bohemian Rhapsody")

    {

        throw new SongNotAvailableException("Bohemian Rhapsody is not available");

    }

    if(message.Song == "Stairway to Heaven")

    {

        throw new MusicSystemCorruptedException("Song in a corrupt state");

    }

    Console.WriteLine(

              $"{CurrentSong.User} is currently listening to '{CurrentSong.Song}'");

}

public class SongNotAvailableException: Exception

{

    public SongNotAvailableException(string message): base(message)

    {

    }

}

public class MusicSystemCorruptedException : Exception

{

    public MusicSystemCorruptedException(string message): base(message)

    {

    }

}

In order to use one strategy or the other, we need to change the MusicPlayerCoordinatorActor and override the SupervisorStrategy method as follows:

Code Listing 57: Overriding SupervisionStrategy

public class MusicPlayerCoordinatorActor : ReceiveActor

{

    protected override SupervisorStrategy SupervisorStrategy()

    {

        return new OneForOneStrategy(e =>

        {

            if (e is SongNotAvailableException)

            {

                return Directive.Resume;

            }

            else if (e is MusicSystemCorruptedException)

            {

                return Directive.Restart;

            }

            else

            {

                return Directive.Stop;

            }

        });

    }

}

We can see that we need to return a SupervisionStrategy (in our case, the supervision strategy only acts on the faulty actor), and we also need to specify the Directive.

Code Listing 58: Client Code that calls the MusicPlayerCoordinatorActor

static void Main(string[] args)

{

    ActorSystem system = ActorSystem.Create("my-first-akka");

    var dispatcher = system.ActorOf(Props.Create<MusicPlayerCoordinatorActor>());

    dispatcher.Tell(new PlaySongMessage("Bohemian Rhapsody", "John"));

    dispatcher.Tell(new PlaySongMessage("Stairway to Heaven", "Andrew"));

    Console.Read();

    system.Terminate();

}

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.