After the first four posts of “Our Fathers’ Faults” it’s time to turn a specific aspect of the application – the Akka framework. The code I’m managing is strongly based on this framework offering endless inspiration for misuse and abuse. Before going straight to the sins parade, I think it is proper a brief introduction to the Akka framework actors and their usage. Half of my two readers are ludicrously proficient in Akka and Scala that they might think of skipping this post wouldn’t it be for my witty ranting prose style, the rest of you two may actually be interested in the content as well.
BTW, actors, as most innovations in programming, are no longer that innovative. The actor model dates back to 1973 (geez, I was 5! I couldn’t even spell “actor”!), but it has been largely popularized by the reactive manifesto as a viable model for reliable concurrent programming.
Oversimplifying the concept – an actor is a purely reactive execution unit. I.e. an actor is a agent whose execution is triggered by the receipt of a message. Unlike a thread/task, the actor strives to return the control from the message handling as soon as possible and without blocking.
The actor internals easily maps to a finite state machine. An actor system is a mesh of actors chatting together with the goal of providing a complex and/or higher level functionality.
From the concurrent programming point of view, the actor model is interesting because there is no need for shared memory and at most one thread is executing inside an actor. Given these premises, there’s no need for mutex or other kinds of locks, yielding an inherently simpler and safer environment for concurrent programming.
There are two additional concepts in the actor model – the executor and the supervisor. The executor is basically a thread pool. One or more actors can be assigned to one executor and the executor grants that at most one thread is active per actor. You may have as many executors as you want, it is up to them to synchronize, you just design your system so to reach the desired level of reactivity. The highest efficiency is achieved by having a single executor per machine with as many threads as the number of cores. But in practice, all generalizations are false, how to partition the system and how many threads per executor are parameters to chose once you have designed the system and figured out where the performance issues are. Anyway, the logic of the system (i.e. the code in each actor of the system) and the executors are mostly orthogonal. This comes handy when you want to scale up the system, you can add threads where needed and no change the code is required.
Supervision is a safety net for actors. When an actor encounters a critical condition and cannot proceed, the supervisor is notified so that it can take proper action, such as restart the failing actor, or terminate it. Akka framework organizes actors in hierarchies based on creation. When an actor creates another one, the latter is a child of the first. The parent actor implicitly supervises its children. Supervision policies may be customized to hook-in user code and provide proper response.
Let’s get closer to the actor. In the traditional Akka model, you define your actor, by inheriting from Actor base class. The derived class must override the receive function. Here comes one of the few issues that raise eyebrows in both OOP and FP camps.
receive is a partial function that accepts an object of type
Any and yields
Unit (transliterated into C++, it would be like a
void receive(void const*) ). “This function is untyped!” the OOP fans scream looking at the argument. “This is not even a Function” the FP rooters yell looking at the return type.
Examine this function closer, when an actor is the target of a message, the framework invokes the
receive function passing the message as an argument. Being a partial function,
receive starts pattern matching the received object against known patterns. If a pattern is matched then the corresponding code is executed. No result is expected because there is no one interested in the resulting value.
If the sender of a message is interested in a reply, then the sender sends the message and then waits for a reply. There is some help from the framework to build this schema under the name ask pattern.
I think it is worth noticing that the most recent version of the framework (released early this year) adds strong typing to actors. In this new version, an actor exposes a list of accepted types so that the attempt to send this actor something that is not ready to receive is a syntax error.
Actors per se are not a bad idea. Maybe the Akka framework could have been done better, but for sure designing and implementing correctly an actor based system is not a trivial task. In the next posts, we’ll see how things can get hairy.