days
1
4
hours
0
2
minutes
4
4
seconds
2
4
search
Anti-pattern #1

Akka anti-patterns: shared mutable state

Manuel Bernhardt
akka

© Shutterstock / Who is Danny

When I work with clients on designing actor systems there are a few anti-patterns that seem to make it into initial, non-reviewed designs no matter what. In this series of short articles I would like to cover a few of those.

Anti-pattern #1: sharing mutable state across actors

one-does-not-state

Even though the Akka documentation points this out in various places, one of the favorite anti-patterns I’ve seen is use is sharing mutable state across actor boundaries.

What do I mean with this? Well, let’s have a look at the following actor:

public class BadActor extends AbstractLoggingActor {
 
    private Map<String, String> stateCache = new HashMap<String, String>();
 
    public BadActor() {
 
        receive(
                ReceiveBuilder.match(GetState.class, request -> {
                    sender().tell(new State(stateCache), self());
                }).build()
        );
    }
 
    public static final class GetState {
 
        public static final GetState Instance = new GetState();
 
        private GetState() { }
 
    }
 
    public static final class State {
 
        private final Map<String, String> state;
 
        public State(Map<String, String> state) {
            this.state = state;
        }
 
        public Map<String, String> getState() {
            return state;
        }
 
        @Override
        public boolean equals(Object o) { ... }
 
        @Override
        public int hashCode() { ... }
 
    }
 
}

This actor has some state (the stateCache) that presumably hold information that it needs in order to function.

By sending it the GetState message, this state is passed verbatim – in other words, by reference – to the sender requesting the state through the State message.

So what’s the problem here exactly?

The actor model is a model, not a framework

The actor model provides the illusion that inside of an actor, everything is single-threaded. One message is processed after another, there is no way that two messages could access an actor’s inner state concurrently and therefore it is perfectly fine to modify mutable state inside of an actor as a result of receiving a message.

Well, except if you share the mutable state of an actor outside of that actor. If you do this (for example as shown in the code above) then there is no guarantee that another actor will not concurrently try to access or modify said state. And suddenly the illusion falls apart and all of the nice guarantees provided by the actor model no longer hold true.

“I know”, you’ll say, “but I promise I will only read from this state in other places”. That’s cool for your actor and all, but it is not going to help you, because:

One thread is like no other

dispatcher-1024x765

Internally what makes Akka Actors “tick” is a nifty little thing called a dispatcher. Every actor has a dispatcher, by default the same dispatcher is shared across the actor system but you can use specific and separate dispatchers for individual actors if you would like.

The dispatchers job, roughly speaking, is to distribute the work (the messages) of one or more actors across a number of threads. The default dispatcher is backed by a ForkJoinPool (very nicely explained here) and therefore juggles several actors and threads. This means, amongst other things, that subsequent messages processed by an actor may not necessarily be handled by the same thread.

Ergo, if you share the mutable state of your actor outside of your actor and hope that by having a reference on the state you will always be able to read the latest version of whatever data you’ve shared, you are in for a bad surprise given that multiple threads will see different versions of your shared mutable state.

“I know”, you’ll say, “but I will use a ConcurrentHashMap and everything will work then, right?”. Wrong.

Mind the gap

sharing-state-1024x494

SHARING SHARED MUTABLE STATE ACROSS JVM AND NETWORK BOUNDARIES – BUT SHOULD YOU?

One of the nice things about the actor model is the notion of location transparency which is, to put it simply, the ability to interact with actors without having to take into account where the said actors are running. They might run inside of the same ActorSystem, inside of the same JVM or in a clustered, networked environment – when building the actor system, this should be all the same to you. Deployment is merely a matter of configuration. (Well, almost – when working in a clustered environment, you often may want to do more such becoming aware of a node entering or leaving the cluster, but in principle if you are only interested in scaling out then you do not even need to be intimately aware of this either.)

So what happens then if your ConcurrentHashMap is shared in a message acrossVM boundaries or network boundaries? How does it get serialized? How does concurrency get serialized?.

The answer is simply that it doesn’t. There is no magic mechanism that makes the ConcurrentHashMap cluster-aware and spins up distributed locks (which would be a performance disaster, by the way).

Therefore:

Simply do not share mutable state across actors, period.

The most frequent reason I am given when asking why the decision to share state was taken is a fear of the overhead introduced through copying the state (maps or larger data structures especially). To which my answer is always the same: do all actors involved in the sharing need to know absolutely everything?

Often times, they do not – or if they do, maybe it is time to revise the actor hierarchy so as to size the responsibilities of a single actor down as much as possible (I’d be happy to help you with this, by the way). Which is to say that designing the actor system and the protocol (after a healthy event-storming session) are probably the most important activities to do when starting to work with the actor model. And once you have a sound actor hierarchy and message protocol, you’ll see that the need for shared mutable state has faded on its own.

Stay tuned, there are more anti-patterns to come!

This post was originally published on Manuel Bernhardt’s blog.

Author
Manuel Bernhardt
Manuel Bernhardt is a passionate engineer, author, speaker and consultant who has a keen interest in the science of building and operating networked applications that run smoothly despite their distributed nature. Since 2008, he has guided and trained enterprise teams on the transformation to distributed computing. In recent years he is focusing primarily on production systems that embrace the reactive application architecture, using Scala, Play Framework and Akka to this end. Manuel likes to travel and is a frequent speaker at international conferences. He lives in Vienna where he is a co-organizer of the Scala Vienna User Group. Next to thinking, talking about and fiddling with computers he likes to spend time with his family, run, scuba-dive and read. You can find out more about Manuel's recent work at http://manuel.bernhardt.io.

Leave a Reply

2 Comments on "Akka anti-patterns: shared mutable state"

avatar
400
  Subscribe  
Notify of
Francis Toth
Guest

Out of curiosity, by referential transparency, aren’t you actually referring to location transparency? These are two VERY different things…