Microsoft Orleans is an open source programming framework for .NET, originally built by the eXtreme Computing Group at Microsoft Research that simplifies distributed app development using virtual actors. These are single-threaded objects with their own state, that don’t share any memory and communicate by exchanging messages using asynchronous remote procedure calls, that are activated on demand, garbage collected when they’re no longer in use and reactivated seamlessly as required.
Because the runtime takes care of activation, cleanup and distributing virtual actors (which Orleans calls grains) across servers, developers can write their code as if it was going to run on a single machine without worrying about concurrency and scale it out to as many servers as necessary.
When Microsoft open sourced Orleans in early 2015, it required the .NET Framework which meant it only ran on Windows Server. Version 2.0 makes Orleans cross-platform by moving to .NET Core, which can run on Linux and macOS (both .NET Core 2.0 and 2.1 are supported, so developers don’t have to upgrade their version of .NET to use new Orleans releases).
“We see a lot of interest in running Orleans on Linux, both internally in Microsoft and externally in the Orleans community, especially with containers,” Principal Software Engineering Lead Sergey Bykov from the Orleans team at Microsoft Research told the New Stack. “When they have other pieces of their stack running on that environment, they naturally want to run Orleans in the same environment.”
But while the main goal was getting compatibility with .NET Core, the team took the opportunity to make some breaking changes that Bykov said might have otherwise been hard to justify. “We made Orleans more developer friendly; we modernized the API to align with ASP.NET as well as .NET Core with configuration options and dependency injection. It’s much cleaner; it’s more structured and more flexible — and developers are much happier.”
While Orleans already supported dependency injection, it was primarily to allow developers to inject dependencies into their own application code at runtime. “In Orleans 2.0, we restructured everything so almost all the internal components of the runtime are injectable,” Bykov told us; that means developers can replace what used to be fundamental pieces of the runtime, either for testing or in production. “Dependency injection provides for assembling things are startup time, so you don’t have the rigid structure we used to have prior to 2.0. They can inject mocks and custom implantations for testing services.” A facility called reminders shows where you can schedule something you want to do every day or every week, and when the time comes the grain will be activated and called. If you want to run a test and run it faster than a day or a week, you can inject your own implementation of the reminder service.
Bykov describes the original Orleans code base as “much more monolithic: over time we moved to be more decoupled, using all these techniques for flexibly structuring a runtime.” Because there weren’t existing APIs for many of the pieces of the framework when the project started in 2010, the team had to create their own code for things like logging. Now that custom code is being removed so that Orleans developers (who are often familiar with ASP.NET) can use more familiar tools and patterns; for example, the logging implementation is the same implementation as in ASP.NET Core.
The way the runtime is organized has also changed because of this level of decoupling; while the abstractions for the virtual actor grains remain the same, configuring and starting the silos they run in is more like ASP.NET, Bykov noted. “We used to just scan the folder for grain assemblies, so if you had 150 assemblies in a folder it would take a minute or so to start. In Orleans 2.0, they have the same API name as in ASP.NET. We load them from this assembly and the startup time, especially for test cases, is very fast so developers can run their tests in a much shorter period of time. The configuration is more flexible; it’s not a black box that does the scaling.”
Open up Orleans Design
Having Orleans use familiar ASP.NET concepts makes it easier for new developers to learn, and it also makes it easier for the community to contribute to Orleans, something that is becoming increasingly significant to the project.
“One of the biggest changes in the upcoming 2.1 release is the scheduler that handles the execution of millions of tasks in a silo,” Bykov noted. The new scheduler delivers 30 percent better throughput — and it was written by a GitHub community member who’s only been contributing for three years, but whose contributions have doubled the performance of Orleans over that time.
Another important feature is the new code generator. “One of the big value propositions of Orleans is that it’s easy to use. You can write your code as if an object was local even though it’s remote; to do that we generate transparent proxy classes and serializers. You can write code natively that takes an argument of a particular type but that needs to magically transfer from one machine to another and we write serializers to do that.”
Much of the code for the new code generation package in Orleans 2.1 was also contributed by the community. This uses the Roslyn .NET compiler’s code analysis to avoid potential conflicts and clashing dependencies with different versions of the .NET Core SDK. “The new version of code generation uses code to code compilation: it doesn’t load assemblies at all, it doesn’t have version binding — it just looks up the code and generates the code for the serializers and proxy references.”
The level of decoupling inside Orleans that makes it easier for the community to create these significant contributions to Orleans makes Microsoft’s role in the project clearer too.
“Decoupling allows for the innovation we can’t scale to deliver,” Bykov explained. “It allows us to continue focusing on the key functionality where we provide the most value and allows other people to build plugins. Maybe you want to replace how messaging is done in Orleans; our messaging is good, it’s very fast and flexible — but it’s our way of doing messaging.”
Another developer might need messaging that performs a little differently, and now they can use dependency injection to replace something so fundamental to how Orleans works.