Supervision

In essence, supervision in Akka Typed refers to the fact that all exceptions thrown from a behavior are caught and acted upon. An action can have one of three forms: resume, restart, and stop.

Let's see how supervision can be defined and which effect it has.

First, let's run our system as it is and observe its output:

...
[INFO] Opening Bakery
[INFO] Go shopping to Actor[akka://Typed-Bakery/user/Seller#1376187311]
[INFO] Mixing Groceries(13,650,130,65)
[ERROR] [akka://Typed-Bakery/user/Chef/Mixer_5] null
ch12.Mixer$MotorOverheatException
at ch12.Mixer$.$anonfun$mix$1(Mixer.scala:19)
at akka.actor.typed.internal.BehaviorImpl$ReceiveBehavior.receive(BehaviorImpl.scala:73)
...
at java.lang.Thread.run(Thread.java:745)
[INFO] PostStop Actor[akka://Typed-Bakery/user/Chef/Mixer_5#-1604172140]
...

We can see how our actors start processing messages up to the point where an exception is thrown by the Mixer. This exception is handled using the default supervision strategy that is stopping the actor. The mixer logs the PostStop event via the monitoring function we defined earlier and attaches it to the actor's behavior, like so:

def mix: Behavior[Mix] = Behaviors.receive[Mix] {
...
}.receiveSignal(monitoring)

Let's see what will happen if we override the default supervision strategy. To change the behavior, we just wrap it into the supervising behavior by using the standard constructor. Let's restart the mixer instead of stopping it in the case of the motor overheating:

val controlledMix: Behavior[Mix] =
Behaviors
.supervise(mix)
.onFailure[MotorOverheatException](SupervisorStrategy.restart)

If we use this behavior by using the Chef actor to create mixers, running the app will produce a slightly different output:

...
[INFO] Mixing Groceries(6,300,60,30)
[ERROR] Supervisor [restart] saw failure: null
ch12.Mixer$MotorOverheatException
at ch12.Mixer$.$anonfun$mix$1(Mixer.scala:29)
...
[INFO] PreRestart Actor[akka://Typed-Bakery/user/Chef/Mixer_2#-1626989026]
[INFO] PreRestart Actor[akka://Typed-Bakery/user/Chef/Mixer_4#-668414694]
[INFO] PreRestart Actor[akka://Typed-Bakery/user/Chef/Mixer_4#-668414694]

Now, the exception has been reported by the supervisor and the mixers have been restarted, as we can conclude by observing the PreRestart events that have been logged by the mixers. There is no PostStop event here.

There is still one more supervision strategy to look at, so let's check it out:

val controlledMix: Behavior[Mix] =
Behaviors
.supervise(mix)
.onFailure[MotorOverheatException](SupervisorStrategy.resume)

With this strategy, we'll still see a log output from the supervisor, but actors won't log any life cycle events:

...
[INFO] Mixing Groceries(5,250,50,25)
[ERROR] Supervisor [resume] saw failure: null
ch12.Mixer$MotorOverheatException
at ch12.Mixer$.$anonfun$mix$1(Mixer.scala:29)
...

It is possible to define different supervision strategies for different types of exceptions that are thrown by the same behavior by nesting supervisor constructors:

val controlledMix: Behavior[Mix] =
Behaviors.supervise(
Behaviors.supervise(
Behaviors.supervise(
mix) .onFailure[MotorOverheatException(SupervisorStrategy.stop)) .onFailure[SlowRotationSpeedException(SupervisorStrategy.restart))
.onFailure[StrongVibrationException](SupervisorStrategy.resume)

The definition is obviously a bit verbose.

The supervision strategies are sticky. They are recursively applied to new behaviors that are returned by the supervised behavior.

Sometimes, it might be useful to try and restart an actor a few times, and if the situation is not improving, then finally stop it. A special constructor is available for this:

Behaviors.supervise(mix).onFailure[SlowRotationSpeedException](
SupervisorStrategy.restartWithLimit(
maxNrOfRetries = 4,
withinTimeRange = 2.seconds))

In an unlucky case, the mixer actor would throw an exception from the Behavior.setup constructor every time it was constructed, and we would see the following output:

...
[INFO] Mixing Groceries(6,300,60,30)
[ERROR] Supervisor [restartWithLimit(4, 2.000 s)] saw failure: null
ch12.Mixer$MotorOverheatException
at ch12.Mixer$.$anonfun$mix$1(Mixer.scala:26)
...
[ERROR] Supervisor [restartWithLimit(4, 2.000 s)] saw failure: null
...
[ERROR] Supervisor [restartWithLimit(4, 2.000 s)] saw failure: null
...
[ERROR] Supervisor [restartWithLimit(4, 2.000 s)] saw failure: null
...
[ERROR] [akka://Typed-Bakery/user/Chef/Mixer_1] null
akka.actor.ActorInitializationException: akka://Typed-Bakery/user/Chef/Mixer_1: exception during creation at akka.actor.ActorInitializationException$.apply(Actor.scala:193)
...
Caused by: ch12.Mixer$MotorOverheatException at ch12.Mixer$.$anonfun$mix$1(Mixer.scala:26)
...
[INFO] Message [ch12.Mixer$Mix] without sender to Actor[akka://Typed-Bakery/user/Chef/Mixer_1#-263229034] was not delivered.

The supervisor tried to restart the actor four times, but then gave up and stopped it. Because of the fact that the failure happened in the setup block, the actor was able to receive neither the Mix command nor life cycle event notifications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.101.109