The Actor model is an approach to software design that has undergone a resurgence recently and I have being spending some time the last few months learning about this area and associated frameworks.
The Actor model is probably better known on platforms such as Scala & Erlang but the .net platform also offers a number of implementations such as Akka.net & Microsoft’s Project Orleans (these are by no means the only options but probably the better known).
But what is the Actor model anyway and why should you care?
The Actor model approach was developed way back in 70s by Carl Hewitt and his team. You can read Hewitt’s original paper – warning it’s not the easiest thing to read but some of the ideas around parallel computation are way ahead of their time.
An actor based system is composed of units of computation called actors.
An actor is a small set of code focussed on performing a specific task.
For example you might have an actor concerned with decrypting a message and another that say sends an email.
Actors are also restricted in what they can do – they can:
- Talk to other actors via messages (more about this shortly)
- Create new actors
- Modify their internal state
You could perhaps think of Actors as being like someone stuck on a desert island. This individual can only communicate with others via a message in a bottle that cannot be changed (or have its state modified).
When an actor receives a message it is placed in an actor’s inbox (a queue). Messages in the inbox are then processed sequentially although usually frameworks make no guarantee that messages will be processed in the order they are sent.
It is important that the contents of messages are immutable to ensure we don’t have any weird state issues and that messages could be processed on entirely different machines or processes!
Any actual implementation of the actor model will tend to adhere to these characteristics (Orleans is a bit different in that it is a virtual actor model which I'll talk more about in future) but it’s worth mentioning that Actor frameworks such as Akka and Project Orleans all add their own additional features that we will discuss in a future posts.
It was many years however before there was much interest in the Actor model.
Why did the actor model not take off?
One explanation is that the fast multicore processors that we take for granted today obviously did not exist in the 1970s.
Code written using an actor approach wouldn’t perform so well and offered limited advantages due to current hardware (and I guess software).
Fast forward to today however when fast multicore CPUs are in abundance - if we are to take full advantage of multiple cores and CPUs we need to ensure our applications make full usage of the resources available. This means (amongst other things) that we need to run code on multiple threads.
Writing multi-threaded applications is notoriously difficult and we normally need to utilize some kind of locking mechanism to avoid different threads modifying state simultaneously.
The actor model offers an alternative approach to using locks by ensuring that all communication occurs via immutable messages and management of state by an actor itself..
This approach is very suited to a cloud environments where we can spin up machines at a moment’s notice as we can potentially spin up new machines to host actors.
What does an actor system look like?
Let’s take a possible implementation of a simple online order processing system (and ignore all the complexities of error handling and edge cases).
This system could be made up of 4 actors:
- Order Router actor
- Order actor
- Stock Availability actor
- Process order actor
The system might work as follows:
- An order request is received from a web based endpoint (e.g. a Web API controller in the .net world)
- This endpoint passes the message to an instance of an order router actor which after some processing will create a new order actor to process the order
- The Order actor sends a message to a stock availability actor which replies that the items ordered are in stock.
- The Order actor then sends a message to the Process Order actor
Multiple instances of actors
In your actor systems there are going to be some Actors that it’s important there are only a single instance of.
In the above example you probably wouldn’t want multiple instances of a single order actor as this could result in some weird state issues.
However in the case of the Order Router actor having only one instance would probably result in scalability issues – remember that as all messages are processed in a queue and if we only have one Order Router actor this actor would be like a shop keeper that can only deal with one order at a time which er probably is not what you want. In this instance you would probably have some kind of pool of Order Router actors to ensure optimum throughput.
Actors and Halo 4
If you want to see a real system example of an Actor system check out the Microsoft research paper at http://research.microsoft.com/apps/pubs?id=210931 where Microsoft describe developing a “Presence service” for tracking active game sessions on the game Halo 4.
What advantages does the Actor approach have?
Developing a system using an actor approach offers a number of advantages:
- Scalability & Performance – the approach allows systems to be easily distributed across servers and could be more performant than traditional approaches by avoiding locking mechanisms – Hewitt was way ahead of his time & describes this as being like “A swarm of bees” in his earlier referenced paper. Additionally by designing your applications so work is divided into units you could potentially create more actor instances in hot areas of your applications e.g. Order Routing in the earlier example should they be required
- Resiliency – issues that occur at runtime can be isolated to individual actors. The Actor framework Akka for example offers rich functionality for “supervising” child actors and different strategies to handle failure
- Maintainability. The Actor approach encourages good general design principals such as Single Responsibility Principals (SRP) – at least in a block of related logic sense, loose coupling of components and immutability of messages avoiding weird state issues.
- Avoid the need to deal with complex multithreaded issues such as race conditions and deadlocks. This can also lead to more maintainable and testable code. It is worth noting that if you have multiple actors dealing with a single entity you of course still can have these issues..
What type of problems are not well suited for an actor approach?
Like any approach actors have disadvantages and are better suited to some types of problem than others.
The Actor approach probably is not so well suited when:
- A problem cannot be divided into independent tasks
- A system that needs to work with shared state
- Where you have behaviour that needs to occur in a specific order
In terms of disadvantages I see the following:
- Debugging could be tricky - imagine following system flow through multiple actors..
- It commits you to a particular framework. Although the approach encourages division of code which helps with maintainability I think a fair bit of code could be concerned with message routing and error handling that is framework specific
- It can be harder to reuse & compose behaviour (Paul Mackay)
- Learning curve – like any new approach it will take time to become proficient with the actor approach
The Actor model is particularly interesting where you have a problem that can be broken down into independent tasks and scalability is important – why not take a look & add it to your toolbox of approaches?
- [Reactive Messaging Patterns with the actor model by Vaghn Vernon] (http://www.amazon.com.au/Reactive-Messaging-Patterns-Actor-Model-ebook/dp/B011S8YC5G)