Akka anti-patterns: race conditions
Contents
The actor model makes it possible to build highly-concurrent applications through the notion that actors obey the actor send rule and the actor subsequent processing rule, hence creating a single-threaded environment inside of an actor.
That being said, it’s all an illusion: as we have briefly talked about previously, Akka’s dispatchers make sure that messages are being processed by actors and so the thread by which one message is processed may not be the same as the one by which the next message is going to be processed.
Therefore you should always respect the following guideline when working with actors:
Do not, under any circumstance, close over mutable state.
And since the actor model is a model, not a framework, it is up to you to make sure that you do, indeed, follow this guideline. Akka will not magically warn you if you misstep, rather, your application will start behaving in wonderous ways.
In the following article we will explore a few ways in which you could misstep.
Offender #1: sharing mutable state
We already talked about this in length — go read the article if you haven’t done it already. If you happen to share the inner, secret, precious mutable state of an actor by, for example, making it available through a message to a third party, chances are that you’re in for trouble. By doing so, you open up the pandora box, releasing vast amounts of “anything could happen, really” (also known as indeterminism) — another thread may now read and write this state while out-of-sync with the subsequent threads fueling the processing of your initial actor. This is not good.
Offender #2: closing over the sender
When processing a message you can get access to its sender by calling the sender()
method. This method returns the actor reference of the sender of the currently processed message. Now, let’s imagine that you were doing something like this:
|
|
The response
in the code above is a future, which is to say that everything in the body of the response.map
closure will run asynchronously. In other words, the actor’s message processing will be able to continue before the future holding the response of the HEAD call will complete. As a result, the actor may already be processing another message when sender()
is called — possibly returning the wrong sender as a result thereof.
The safer alternative is to capture the original sender:
|
|
Offender #3: closing over an actor’s context
We’ve just seen that closing over the sender() method is dangerous. Well, it turns out that an actor’s context accessed via the context()
method, also holds mutable state and is therefore not safe to close over.
Consider the following example (taken from chapter 6 of Reactive Web Applications — check this book out if you’re planning on building a reactive application):
|
|
Offender #4: using the scheduler with the Runnable variant
Akka lets you schedule tasks thanks to its scheduler, either by scheduling one task to be executed once in the future or by scheduling a repetitive task to be executed after a certain interval. There are two signatures available for this (simplified here):
|
|
The first signature is the one idiomatic to the actor model: once the interval has passed, a message is sent to the actor referenced in the call. The second signature lets you call a Runnable
instead — which may feel more familiar when getting started with using the actor model.
And here’s the trap: if your Runnable
closes over mutable state in your actor, then you are again in a situation in which both the thread fueling the processing of the actor and the thread fueling the Runnable
may try to interact with the same mutable state concurrently. Game over.
Actors and futures: use pipes
In this article we have so far focused on how to do things the wrong way. When it comes to mixing short-lived asynchronous computation using futures and long-lived asynchronous computation using actors, there is a pattern at your disposal: pipes.
In an akka actor, a pipe lets you send the result of a completed future (be it a suceeded one or a failed one) to another actor, hence leveraging the message-passing paradigm of the actor model.
You can find out more about futures, actors and pipes in this talk I gave at Scala.io 2014 (slides) or otherwise in chapter 6 of Reactive Web Applications.
That’s it for this anti-pattern. Stay tuned for more!