A metaphor on the actor model
One of the things that I enjoy the most is to design software using the actor model. Since I’m a Scala enthusiast, this article is about Akka, one of the libraries that I love. There are many good posts about the technical aspects of Akka and the documentation is pretty good as well. Even so, I decided to make this article, where my main focus is software design with the actor model. I will also show code with some technical details like Routers, Supervisor Strategies, etc.
Nowadays, I see many people using the actor model to solve very challenging problems but I also see people using it when they probably don’t need it (guilty as charged), just because it is funnier. So, when we don’t need it to solve our problems, why are we using it anyway? When the code goes to production, we will have more to care about than if we had used a simpler design.
So, to give an example of solving a problem that fits the actor model, and to identify some of the aspects that makes a problem a good fit, let’s use the metaphor of “coding a software company”.
Let’s consider that our software company has a board, a team leader and a team of software engineers, and in the near future it will have multiple teams that can be at different places. The company works like this: the board asks the team leader to execute projects, and the team leader distributes the project’s tasks by their team. After the end of the project, the team leader makes a simple report of the time that the team spent, and lets the board know that the project is finished. So with this little description we can identify 4 aspects that are good indicators that we can use the actor model:
- The entities should do concurrent work (the software engineers and team leaders should work concurrently)
- We have well defined entities and they have an hierarchy
- Entities should maintain an internal state
- We are planning to scale the number of team leaders and teams
Thinking in terms of actors, we have 3 kinds of actors in the following hierarchy: the board actor that has 1 team leader actor, but can have more than one, and each team leader actor has a team of software engineer actors. Let’s begin the coding with the models for projects and tasks and some messages for the actors:
With these models in mind, let’s define our software engineer actor (“the one that always does the hard work”), with the requirement that each software engineer should store how many tasks he does:
The software engineer actor receives tasks, updates the counter and sends a message to the parent (team leader) saying that the task was done in some amount of time. I could have simulated the time of work with some thread sleeps, but since thread sleep isn’t a good practice inside actors, I preferred to keep that away from the example.
Now that we have the representation of a software engineer, let’s think about how they could work with team leaders. Each team leader has their own team of software engineers (workers), so we can represent software engineers as a pool of workers and the team leader as the router using Akka Routing. One of the team leader’s requirements is that when a team finishes a project he should produce a simple report saying how much time the team spent on it. So let’s take a look at the code for it:
The team leader has two behaviors: waiting to receive a project from the board and managing a project. When a team leader receives a project from the board, he distributes all tasks to software engineers and changes the behavior to managing when he becomes too busy to take on a new project. While managing, the team leader updates the internal state with the messages from engineers whenever they finish a task. When all the tasks are done, the team leader creates the report of the current project, resets his internal state and sends a message to the parent saying that the project was a success.
Now, we are just missing the board representation, and we can do it in the main object of the sample application instead of making a specific actor for that, using an Inbox as follows:
It seems the actor system fits perfectly our requirements. We were able to model our hierarchy, the software engineers are able to do concurrent work and we can maintain the internal states without issues with shared mutable state.
However, let’s introduce some new requirements to show where this solution really shines: consider a “possible and realistic failure” saying the software engineers can get the flu:
To deal with failures, Akka has a default supervisor strategy: if there is an exception in one of the workers, the actor will be restarted. In this case, the task that the actor was working on will be lost, and the internal state of the actor will be lost too. Since we don’t want unfinished tasks or possibly depressed software engineers when they find that all their accomplishments were lost, we have to override the default supervisor strategy in TeamLeader:
That was easy! When a software engineer gets the flu, the team leader just sends the ongoing task to another engineer.
Moving on to another possible requirement, let’s say that we need a new team leader with a new team of 10 software engineers:
And if this team has to work remotely, we are able to send it to a client’s office, and use the address of that office, using the Akka Remote feature:
And that’s what I had in mind: to show you a simple example of using the actor model, with Akka in this case, to model a problem that in my humble opinion will be more painful to model using another approach. I hope that with this metaphor I’ve shown you some aspects that we should identify before using the actor model, and some of the advantages of using it. You may check the full example here.