Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Akka.NET actors and wrappers (potentially with Rx)

I started playing with the Actor model, and in particular with Akka.NET. Overall I think I have a good idea of what's all about, but of course the devil is in the detail. I was thinking about adopting Akka.NET in an already existing codebase, and therefore I'd like to estimate how much of the existing abstractions can be kept. The idea is that some specific high-level interfaces could be kept, and some adapter implementations would be written to seamlessly go back and forth across the actors world and the users of the existing interfaces, but I'm not sure if that's recommended and what types of specific problems I should expect to face.

Trivial example:

public interface IGetSet<in TK, TV>
{
    TV Get(TK key);
    void Set(TK key, TV value);
}

public class GetSet<TK, TV> : IGetSet<TK, TV>
{
    private readonly ActorRef _getSetActor;

    public GetSet(ActorRefFactory system)
    {
        _getSetActor = system.ActorOf(Props.Create(() => new GetSetActor()));
    }

    public TV Get(TK key)
    {
        var x =  _getSetActor.Ask<TV>(new GetSetActor.Get(key));
        return x.Result;  //blocking, I know, it's just an example
    }

    public void Set(TK key, TV value)
    {
        _getSetActor.Tell(new GetSetActor.Set(key, value));
    }

    class GetSetActor : ReceiveActor
    {
        readonly Dictionary<TK, TV> _values = new Dictionary<TK, TV>();

        public class Get
        {
            public Get(TK key) { Key = key; }
            public TK Key { get; private set; }
        }

        public class Set
        {
            public Set(TK key, TV value) { Key = key; Value = value; }
            public TK Key { get; private set; }
            public TV Value { get; private set; }
        }

        public GetSetActor()
        {
            Receive<Get>(g => Sender.Tell(_values[g.Key], Self));
            Receive<Set>(g => _values[g.Key] = g.Value);
        }
    }
}

...

var gs = new GetSet<string, int>(ActorSystem.Create("Wasp"));
gs.Set("a", 42);
var i = gs.Get("a");

In this case the IGetSet interface comes from the traditional world, and its implementation lets us transition back and forth with the actors world. I tried to be nice with actors, which are never used in ways different from message passing, so overall this (trivial, of course) exercise looks promising, but I'd like to know if there's anything more I should pay attention to since day 1.

I've been reading about avoiding additional non-actor-based async code taking closures on actors' state, that's clear and I'm not doing it, but maybe there's more which I can't see. My final goal is to use this pattern quite extensively, down to point I'd write actor-oriented implementations of Rx's ISubject (which BTW I already did and it was easy, but again I'm not sure I paid enough attention to all I should).

I also read a bit about Typed Actors, but I'm not a Scala expert so maybe I'm not grasping all the details from the code samples, and I'm not sure they are already available within Akka.NET (doc page is a 404)

like image 818
Wasp Avatar asked Apr 06 '15 14:04

Wasp


1 Answers

This looks good. What you should take into account is that actors have a delivery guarantee of "at most once" by default, so, you should take into account that when communicating with your actor that you might not get a response back. (Network failure, crashed remote nodes and such)

In a local system, it's very unlikely that a message would get lost, but in theory, the actor system could crash if someone does something too wild with it, and thus the actors die.

So when communicating with an actor using Ask better be safe and provide a timeout and handle that exception instead of block/wait forever.

Async/Await is supported as of the latest pre release bits(pre 1.0). It is however not the recommended approach. better stick with PipeTo and be explicit.

Another thing that could get iffy is since in your example you are treating the actor as a Key Value store, which is all fine. And your messages are also immutable, which is also good. But if the Key or Value properties are ref types, and people can mutate those from the outside, e.g. the consumers of IGetSet that could cause RC problems inside the actor as the actor might read those values when another thread is mutating them..

ActorSystems are also quite expensive, try to avoid spinning up many systems, aim for one system per process.

Other than that, you are good to go.

like image 130
Roger Johansson Avatar answered Oct 12 '22 23:10

Roger Johansson