TrySet and\or Flush that returns Task<bool> on IActorState?

Feb 21, 2013 at 7:20 AM
Edited Feb 23, 2013 at 7:04 PM
Maybe I am missing something but it seems it would be nice to have a TrySet in the IActorState interface that could affect if the flush was accepted or not or something similar. I'm not sure the best definition for this but I would like some way to have better control over if my update is taken. I'm trying to think in my head how two people updating the same actor replica set could not stomp on each other's feet. Thanks.
Developer
Feb 26, 2013 at 5:17 PM
Hi AceHack. One of the beauties of the actor model is that actor methods have exclusive access to the actor state while they are running. Even though there may be many clients calling actor methods at once, these requests are all essentially serialized so that only one actor method is running at once. This means that your Set operations will always succeed, and that there is no need to employ any sort of locking in your actor method.

Now it may be the case that you set KeyA to ValueA in an actor method, and the next actor method to run sets KeyA to ValueB. In that case, the ultimate value for KeyA will be ValueB, because "last set wins". But that is just the normal consequence of serializing operations that are essentially concurrent, and should not be considered a "stomp".

Hope that helps,
--Joe
Feb 27, 2013 at 8:12 AM
I get you that my operations are serialized but let's say I want to do something as simple as a counter. I know there are better ways of doing a counter than this, I'm just using this as an example.

Person A reads 0, Person B reads 0, Person A writes 0+1 and it works, now person B writes 0+1 and it works so you have missed a count. The total is now 1 instead of 2. Even though this is serializing behavior I consider this a big STOMP.

What I really want is person A&B to say I only write my value if the some field I read is the same when I go to write, so first person wins. Same goes for monkey patching methods on the fly. In other works I want Optimistic concurrency or something like it. Is there a way I can achieve this effect. I'd even settle for some pessimistic locking (in certain extremely rare situations).

I get I could use something like a Commutative Replicated Data Type (CRDT) to implement a counter in a way that does not need optimistic concurrency and instead relies on Strong Eventual Consistency but I want something that will let me declare a winner using the logic I see fit if wanted.

Are there any ways to accomplish this with the existing interface? Even if it's something difficult to implement similar to PAXOS, ZooKeeper, Chubby, Distributed Hash Table, Controlled Scalable Decentralized Placement of Replicated Data (CRUSH)? It would be best if something difficult like that was already implemented in the framework but if not what one(s) do you think will work and fit best in this model if the interface cannot be changed to accommodate. Thanks.
Feb 27, 2013 at 10:29 AM
AceHack wrote:
Person A reads 0, Person B reads 0, Person A writes 0+1 and it works, now person B writes 0+1 and it works so you have missed a count. The total is now 1 instead of 2.
I don't think that actor model works in case where you start sharing state. It's more about partitioning your domain functionally and/or spatially into non-intersecting pieces that communicate with each other. In example with counter you would just make Increment() operation in some CounterActor and only that actor would read his currentState and then do +1. If you need some cooperative processing that shares state and relies that state is same across all participants then you don't have much choice other then using eventually consistent types or consensus ensuring algorithms be it PAXOS or just simple global lock.
Developer
Feb 27, 2013 at 4:24 PM
Hi AceHack,

Admittedly, if you issue your "counter bump" operations piecemeal from the clients in the way that you described, there is a fairly good chance that "stomp" will occur. The way to achieve the sort of transactional behavior that you crave is to encapsulate the counter operation in an actor method:
[ActorMethod]
public static object BumpCount(IActorState state, object[] args)
{
    int count = (int) state.Get("_count"); // You could also read the key from args[0] if you wanted to bump arbitrary counts
    state.Set("_count", count+1);
    return count+1;
}
Now, if you call the BumpCount() actor method from the client, there is no chance of "stomp", since actor methods have exclusive access to state and there is no chance of the A/B interleaving that you described. BumpCount() methods issued from two different clients by definition cannot run at the same time; their execution will be serialized.

@Rurouni: You can indeed share state between actor model clients. That is a huge part of the motivation behind actors and the distributed collections that we build on top of them.

Thanks,
--Joe