A first look at Microsoft Orleans
Microsoft Orleans is a framework used to build scalable distributed systems. It reduces the complexity of distributed system by abstracting away concurrency issues together with state management. Processes can then be run on multiple instances and form a cluster, they can be hosted on different environment and be scaled up and down on demand.
Today I will give a broad overview of the concepts utilized and developed by the team behind Orleans. This post will be compose by 3 parts:
1. Motivations 2. Definitions 3. How does it work?
The two main benefits of Orleans is that it:
- reduces the complexity of coding applications by providing a simpler model to maintain object oriented codebase. Orleans moves away from the N-tier type of architecture where business logic is held in stateless services and state is persisted in database by providing its own implementation of the actor pattern where the actor is a stateful unit of work where consistency is enforced within the actor.
- reduces the complexity of building a distributed system by coming up with its own concept, virtual actors. Orleans handles the location of the unit of work which can be distributed in an expendable number of nodes, invisible to the caller. The unit of work trying to be accessed is guaranteed to be alive within the system but it’s exact location is abstracted.
This two problems, code and infrastructure compose most of the problems we, developers, face on a daily basis:
- Where to place the business logic?
- How do we reinitialize the state of our objects?
- Where do we store the state?
- How do we map our persistence to our objects?
- How can we ensure availability of the system?
- How can the system recover from disaster?
All these questions are answered by Orleans.Orleans official documentation is available here.
Before diving into how we can build an application, we will see some of the keywords which can are employed in the framework but also in the documentation.
Grains are the heart of the application, business logic, validation of business logic and storage are handled from within the grain. It is the actor containing the business logic. A grain can be either stateless or stateful and can also have its state persisted. We interact with grains in a asynchronous fashion, any call is asynchronous and return a
Task. It is also possible to have the grain return a value back to the caller.
Concurrency is also handle by Orleans, every grain is ensure process request in a synchronous way. This makes thinking about the state within the grain much easier as there will never be concurrent threads modifying the grain state.
A silo is the process where a grain get executed. Multiple silos together form a cluster. In the most simplistic development, all silos share the same set of grain implementation.
When a client needs to get a grain, it will contact the silo through its gateway, then Orleans decides which silo will instantiate the grain. This is one of the properties of the virtual actor, we do not decide how silos are managed and where grains are instantiated, Orleans takes care of it for us. To get a grain, all we know is the cluster we wish to contact and where to find the gateways available (membership table).
A cluster is composed by a set of silos. Within the cluster, the availability of the silos is maintained through via the membership table. A cluster is also identified by the deployment identifier. It can be seen as the unit accessible by the client. The cluster can be seen as the
A client is the caller needed the grain. The Orleans client is the service provided to talk to the cluster. It contains more in it like the message gateway which keeps track of alive/dead gateways. Usually the
client is created within the client application needing to call grains, like an ASP.NET application.
The liveness is what defines how do silos know about other silos joining the cluster or being alive/dead. In reliable environment, the liveness implemented through a membership table stored in a reliable structure such as SQLserver or ZooKeeper. But during development, the liveness is provided by a membership table stored in the state of a grain, inside the
primary silo (which is also the seed node).
Now that we had an overview of what elements compose an Orleans application, we can start to see how it works together.
In an application, we usually talk about client and server. The client here would be the application needing to access the grains, for example an ASP.NET web app. And the server here would be the cluster.
In the cluster, multiple silos will run, ideally more than three, which will be the composition of the cluster.
When the client needs a grain, it will check for available gateways and request for it.
If the grain has not been activated in any silo, Orleans triggers an activation and return the newly activated grain instance. Subsequent calls trying to access this grain will then be directed to the silo where the grain is instantiated.
Availability of silos gateways is provided by the membership table. The membership table is where the state of the cluster is stored. It contains the most up to date information about all silos in the cluster including their status, whether pending/joining/alive/dead and their last IAmAlive status representing the last time the silo stated that it was alive.
Within the cluster, silos are responsible for checking if their siblings are still alive, if unresponsive, they will mark them as dead.
More details about the protocol can be found on Orleans documentation.
The membership table helps in notifying dead nodes but it also helps in notifying silos about new silos joining the cluster therefore making new gateways available to clients.
To summarize, the membership table is updated by silos, consulted by silos in order to know which of their siblings are still in the cluster and if new silos joined the cluster, consulted by clients to know what are the available gateways to get grains from.
Those concepts are the key for scalability, by spawning new silos, we can handle a very large amount of logic executed in grains scattered among silos which are separate process which could be running in different nodes (VMs).
Today we saw some of the keywords which can be seen in Orleans framework and documentation. We also had a general idea of what Orleans is and how it works. In the next part, we will see how we can implement a simplistic application with one silo and one client with membership table grain. Hope you enjoyed this post as much as I enjoyed writing it. See you next time!