How to handle exceptions within the actor?
Asked Answered
C

1

22

Is there a standard pattern to deal with exceptions within actors in Akka.NET?

I saw some patterns to create supervisors, but it seems the SupervisorStrategy is a way to deal with things that cannot be resolved by the actor.

I have an actor that receives lots of data and needs to store it in an external server. The external database may be unreachable. If it is, the server may be restarting or the network may be down. I don't need to restart the actor or anything, I just want to notify the sender with some information about what is happening, so he can persist the message on disk and reschedule for later.

The sender is not a parent of this actor connecting to the database. Should I create a supervisor just to handle this as well? Or should I encapsulate my receive handlers in try/catch blocks and just use Tell to notify the senders a with a custom response as if it was a normal message?

I know there is a Failure class, but I'm not sure if I'm suppose to use that for this situation.

Corneille answered 7/2, 2015 at 21:27 Comment(1)
Roger's answer is correct, but I wanted to link to a detailed explanation of how hierarchical supervision and the error kernel pattern both work in Akka.NET: petabridge.com/blog/…Persuasion
H
19

Yes there is. First of all, always delegate dangerous work to child actors, give them all your knifes, flame thrower and such. if they crash and burn, your state is still intact and you can spawn new children.

So for the unreachable database example; Spin up a DB-communication actor. You can then let this actor have two states, DB up and DB down, this can be modeled as an FSM or using Become/Unbecome.

So when a message arrives and requesting a DB query, if things blow up, the DB communicator actor puts it self into DB-Down state. If any query is received in DB-Down state, you can immediately respond with a Failure event.

So how do we go from DB-Down to to DB-Up again? The DB-Communicator actor can ping it self using ScheduleOnce, e.g. pass it self a "CheckDBStatus" message every x seconds. When the CheckDBStatus message is received, you check if DB is up again, and if so, you revert back to DB-Up state.

This way, you will not flood your DB in scenarios where it is unable to respond due to high load, adding more load in that case will only make things worse. So this kind of circuit breaker will prevent that from happening.

So in short:

In DB-Up state:

If a DBQuery message is received, try to run the query, and send back the response. if things blow up, go directly to DB-Down state and respond with a failure event.

In DB-Down state: if a DBQuery message is received, respond with a Failure event directly w/o touching the DB. Ping yourself every x seconds to see if DB is up, and revert to DB-Up state if possible.

In this scenario, you would not use any supervisor to transit the state, normal try/catch would be enough to deal with this.

Hope this clear things up.

Hom answered 8/2, 2015 at 7:8 Comment(8)
Yes, it does. Just one thing: is a Failure event just a custom message I create and send with Tell, or is there something special to it?Corneille
Its just a common event, there is a built in "Failure" event for general purposes alsoHom
Am I right this DB-communication actor should be a top-level actor?Slade
When you say "respond with a Failure" event, could you be more specific? Do you mean Akka.Actor.Failure or Akka.Actor.Status.Failure or something else? When I respond to an Ask<T>() with either of those classes, I seem to get an invalid cast exception trying to cast the Failure to the intended return type instead of re-throwing the exception, so the original error is lost in favor of the invalid cast error.Conchaconchie
This is common pattern in distribute systems and it's called Circuit Breaker. AFAIK there's implementation of this actor in Akka(.NET) as wellRandolph
@JoelMueller not an answer to your question, but some info I managed to dig up: Akka.Actor.Failure was added as part of the supervisor implementation, its usage there was removed as part of the actor cell fault handling implementation but the class remained. Akka.Actor.Status.Failure is part of remoting: "[...] Used for internal ACKing protocol, but also exposed as a utility class for user-specific ACKing if needed."Gurevich
@JoelMueller Did you ever find a answer to this? I am experiencing the same Failure casting exception and losing my actual exception.Atonic
@Slade No, the exact opposite. The DB-communication actor should be a child at the very bottom of the hierarchy and should be considered disposable. If the DB-communication actor throws an unhandled exception, let it die and replace it with a new instance of DB-communicaton. This is called the 'character actor' pattern and is explained in detail here: petabridge.com/blog/top-akkadotnet-design-patternsZeitler

© 2022 - 2024 — McMap. All rights reserved.