akkadotnet / akka.net

Canonical actor model implementation for .NET with local + distributed actors in C# and F#.
http://getakka.net
Other
4.69k stars 1.04k forks source link

Need to document `ActorInitializationException` and how it occurs in actor lifecycle #5969

Open Aaronontheweb opened 2 years ago

Aaronontheweb commented 2 years ago

Well I learned something new today

Throwing an exception in an actor's PreStart block will not allow the actor to be restarted: https://replit.com/@Aaronontheweb/ActorsPreStart#main.cs

I'm trying to imagine if that's by design or not (it might be since the child actor can't really complete its startup sequence if it fails there.)

Either way, that means this bug is more severe than I thought. We'll get this patched right away.

Originally posted by @Aaronontheweb in https://github.com/akkadotnet/akka.net/issues/5962#issuecomment-1138580434


One implication of this is that the actor's PostStop method is also never called, which causes functions that depend on PostStop for cleanup such as IWithTimers to never run correctly.

ismaelhamed commented 2 years ago

I think that's because any exception in an actor's PreStart will throw ActorInitializationException, to which the default Supervisor Strategy will react by stopping (not restarting) the actor.

If you use a supervisor with a custom-defined Supervisor Strategy that restarts on every exception, then it'll work:

public class MySupervisorActor : UntypedActor
{
    private IActorRef actor;

    protected override SupervisorStrategy SupervisorStrategy() =>
        new OneForOneStrategy(Decider.From(_ => Directive.Restart));

    protected override void PreStart()
    {
        base.PreStart();
        actor = Context.ActorOf(Props.Create<MyActor>(), "myactor");
    }

    protected override void OnReceive(object message) => actor.Forward(message);
}

public class MyActor : UntypedActor
{
    public bool _failedOnce;

    protected override void PreStart()
    {
        if (_failedOnce)
            return;

        _failedOnce = true;
        throw new ApplicationException("crashing!");
    }

    protected override void OnReceive(object message) => Context.Sender.Tell(message);
}
Aaronontheweb commented 2 years ago

In that case this is working as intended, but we should probably expand on this inside the documentation: https://getakka.net/articles/actors/fault-tolerance.html