This is probably one of the most frequent (and dangerous) anti-patterns when it comes to working with Akka. Let’s look at an adequate description of the mindset you must be in in order to use it:
So maybe you didn’t actually want to see the world burn. Maybe you thought that this one time it was okay to call blocking code inside of an actor. Little did you know that, seven months later, this decision would bring down the entire system minutes after its official launch and announcement in the media.
Why is blocking inside of an actor so bad?
The reason why is is a very bad idea to execute blocking code inside of an actor is because Akka uses an evented model rather than a threaded one. If you don’t know what either of those are there’s an explanation (with pictures and metaphors and all) in the first chapter of my book (get it here for free). And yeah, sure, you could configure Akka to sort of morph into a threaded execution model but then you’d be breaking the hearth of the core developers of Akka and that’s just cruel, so don’t.
In short (or because you’re too lazy to read that chapter and still don’t quite know what is an evented or a threaded model and were hoping to continue reading without me noticing), when using an evented model you have a relatively small amount of threads so as to minimize context switching. Not paying the price of context switching is a big deal for performance as shows this very cool graph from ithare.com:
Which is to say, you really want to make sure not to have too much of that context switching going on when running an application.
Blocking by calling a synchronous API
We’ve covered this. If you are calling into a legacy / blocking API, you may be headed for trouble. Also chances are that, like the Joker, you just want to see chaos unyield on the planet. I can picture you laughing and am not amused.
Blocking by calling an API that calls an API that eventually calls an API that is synchronous
I deeply believe that most humans aren’t evil at their core. When I wrote in the beginning of this post that this anti-pattern is possibly one of the most frequent ones to be found in actor systems it is because it can sneak up on you without you knowing.
For example, let’s look at the following line of code. Can you tell me what is wrong with it?
You can’t? Look closer. Still not seeing anything?
Well, this is a hard one to see. I’ve left a hint though, I’ve called the order that we just received
orderBean. When the log statement is going to be evaluated, the
toString() method of the
orderBean will be called. And here’s the problem. You see, this
toString() method happens to call the database. Yes, you’ve read this right. This
toString() method has been crafted in the eternal fire of Mount Doom and now that you’ve incanted it from within your actor, your entire actor system is about to be subjugated to its demonic blocking effect, and everything is going to crash, and nothing is going to recover, ever.
What do we learn from this? Trust no one. The life of the distributed systems practitioner is a lonely one, like a wolf, you must always be at the lookout for evil lurking at every corner, ready to wreak havoc on your system.
Seriously though. I’ve not made this up. You can’t make this stuff up. I’ve seen it. I’ve looked a
toString() method in the eyes that called the database to display additional information regarding an order.
So you see, this is what I mean when I say that this anti-pattern is frequent - it sneaks up on you so silently that you often do not notice it, until it is too late and you’re experiencing a system-wide resource contention. There isn’t much you can do here except for running load tests on your system to highlight which parts are problematic, something you should do anyway before going live. And also you might want to try out the reactive audit tool that can help you identify parts of an application that are calling an API that will eventually be calling an API that is synchronous.
Thread.sleep is like a siren. It calls you, lulls you into believing that it will be fine to just wait for a few thousand milliseconds, it’s so easy, what could possibly go wrong…until you wake up, under water, unable to breathe, seeing the real face of the siren suddenly revealed as that fish-like creature with razor-sharp teeth, but now it’s too late and you’re out of threads (by the way, I never really understood why Odysseus needed to be strapped to a pole listening to the sirens whilst the rest of his crew, including, you know, the guy steering the wheel, were just having plugs in their ears and could do their work. Could he just not have used earplugs as well?).
Thread.sleep is bad because you’re blocking a thread for an arbitrary amount of time and so here again you’re blocking in a context that’s not designed for blocking operations. But maybe the more important problem about using
Thread.sleep is that by using it you’ve made an assumption as to the meaning of time and carved the sleep interval in stone (up until the next hot fix when you realize that you needed to wait 500ms more).
Is 1500ms on your laptop really the same as 1500ms in a docker container in kubernetes in a virtual machine somewhere in the cloud? Does time even obey the same laws in the Cloud as it does on our mortal plane? Impossible to say it is.
Please, if by any means you can use an event-based approach to change state rather than waiting for an arbitrary duration, do so. Of course I’m only talking about cases that are technical, not the cases in which there’s a valid business use-case for a timeout. If there is any way to hook into some kind of notification API in order to know that what you’re waiting for has happened, favor that approach over waiting for a fixed duration.
And if you really have to wait, do not use
Thread.sleep. Instead, use Akka’s built-in scheduler in combination with a
ControlAwareMailbox and send the actor a message after the desired time interval - this way, at least, you won’t block the thread.
That’s it for this anti-pattern. Stay tuned for more! If you’re working on an Akka system and are a little uneasy after reading this, contact me.