2
votes

Having an issue with akka.net. I need to access an actor which I have already created with a specific name. I can retrieve the actor from IActorContext but I am struggling to access it from the ActorSystem.

I have created a method called GetOrCreateActor which attempts to get the actor using ActorSelection. If it doesn't exist, the catch creates a new actor with the name. If it does exist, I want it to return the reference. However, it never returns from '.Result'. Assuming this could be some sort of deadlocking issue.

public static IActorRef GetOrCreateActor<T>(this ActorSystem actorSystem, string actorPath, string name = null) where T : ActorBase
    {
        try
        {
            return actorSystem.ActorSelection(actorPath).ResolveOne(TimeSpan.FromSeconds(1)).Result;
        }
        catch 
        { 

            return actorSystem.ActorOf(actorSystem.DI().Props<T>(), name); 
        }
    }

Edit I've tried to include a simplified version of the calling code below.

The actor system is created in an IOC container using AutoFac (ExampleActor is the ReceiveActor I am trying to access):

containerBuilder.RegisterAssemblyTypes(typeof(ExampleActor).Assembly).Where(x => x.Name.EndsWith("Actor")); 
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
    var system = ActorSystem.Create("ExampleActorSystem");
    new AutoFacDependencyResolver(lazyContainer.Value, system);
    return system;
}).As<ActorSystem>().SingleInstance();
return lazyContainer.Value;

ActorSystem is then injected into another class, where I call the GetOrCreateActor method (via the Execute method):

public class ExampleCommand : IExampleCommand
{
    private readonly ActorSystem _actorSystem;
    public ExampleCommand(ActorSystem actorSystem)
    {
        _actorSystem = actorSystem;
    }
    public void Execute()
    {
        SendMessage();
    }
    private void SendMessage()
    {
        string message = new Message();
        _actorSystem.GetOrCreateActor<ExampleActor>("akka://ExampleActorSystem/user/ExampleActor", "ExampleActor").Tell(message);
    }
}

The above command would be called from a RESTful endpoint

public ExampleGetModule(IExampleCommand exampleCommand)
{
    Get["/api/createExample"] = parameters =>
    {
        exampleCommand.Execute();
    };
}
1
Can you provide more information please? For example: how is the method being called when the deadlock occurs? Is it an async method calling GetOrCreateActor?easuter
I've edited the question to provide more information.Kelly

1 Answers

0
votes

Your deadlocking issue looks more like it has to do with how you're using your container than it does Akka.NET:

var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
    var system = ActorSystem.Create("ExampleActorSystem");
    new AutoFacDependencyResolver(lazyContainer.Value, system);
    return system;
}).As<ActorSystem>().SingleInstance();

In terms of what can go wrong here, self-referential Lazy<T> types are an infamous source of race-conditions. You should not be calling lazyContainer.Value inside of this registration method if the output of containerBuilder.Build depends on the input of containerBuilder.Register.

Last thing is to use step-through debugging to make sure that your application actually calls into the ResolveOne method here - if you're not getting a timeout exception back then it means that your application is deadlocking on producing the actor system (because of how DI is configured).