Is there any way to wait for actor to be completely stopped?

1.6k Views Asked by At

As I know, all operations in Akka.Net are asynchronous, and Context.Stop() simply sends a Stop message to the actor. That means, actor will be alive for some time before it completely shuts down.

And if I'll call Context.Child() right after Context.Stop() with the name of the actor I just stopped, I will get the same actor.

Here is the example code

var actor = context.Child(actorName);

if (actor.Equals(ActorRefs.Nobody))
{
    actor = CreateNewActor();
}

Context.Stop(actor)
actor = context.Child(actorName);
// what do we get here, same actor or ActorRefs.Nobody ?

My application creates actors to process events from terminals. Each time new terminal connected, I create new actor by calling Context.Child() using terminal name. When terminal disconnects, I stop the actor.

Problem is that some times I receive Connect message right after Disconnect for same terminal, and as result I get actor that's going be stopped. Is there any way to check that actor received Stop message and will be stopped soon?

2

There are 2 best solutions below

0
On BEST ANSWER

I decide to finish up with handling Terminated messages.

After receiving Disconnect message and stopping an actor, I keep it's name in ActorsToBeStopped HashSet, and before creating a new actor on receiving Connect message, I check if it exists there. If so, I keep this Connect message in dictionary with actor name as key and Connect message as value and process it after receiving Terminated message for corresponding actor.

Something like this:

private readonly Dictionary<string, Connect> postponedConnectMessages = new Dictionary<string, Connect>();
private readonly HashSet<string> actorsToBeStopped = new HashSet<string>();

// ...

Receive<Disconnected>(t => 
{
    var actor = GetActorByName(t.Name);
    Context.Stop(actor);
    actorsToBeStopped.Add(actor.Path.Name);
});

Receive<Connected>(t =>
{
    var actor = GetActorByName(t.Name);

    if (actorsToBeStopped.Contains(actor.Path.Name))
    {
        postponedConnectMessages[actor.Path.Name] = t;
        return;
    }
    // work with actor
}

Receive<Terminated>(t =>
{
    var deadActorName = t.ActorRef.Path.Name;
    actorsToBeStopped.Remove(deadActorName);
    if (postponedConnectMessages.ContainsKey(deadActorName))
    {
        var connectMessage = postponedConnectMessages[deadActorName];
        postponedConnectMessages.Remove(deadActorName);
        var actor = GetActorByName(connectMessage.Name);
        // we sure we have new actor here
        // work with actor
    }
}

EDIT

Sad thing here is that I can't write test for it, because Akka.TestKit doesn't allow me to create TestActor with same name even after it was stopped:

public void StopTest()
{ 
    var t = CreateTestActor("AAAA");
    Watch(t);
    Sys.Stop(t);
    ExpectTerminated(t, TimeSpan.FromSeconds(10));
    var t2 = CreateTestActor("AAAA"); // test fails here
}

Or, maybe it wasn't stopped after ExpectTerminated, but anyway I don't know how to wait it's termination.

2
On

You can use

var shutdown = actor.GracefulStop(TimeSpan.FromSeconds(42));

It returns a task whose result confirms shutdown within 42 seconds

UPDATE

But, in case, if you want to recreate actor later with the same name, you should listen to Terminated message within actor's supervisor.