What is D·ASYNC?

D·ASYNC (also D-ASYNC or DASYNC, where D stands for Distributed) is an ambitious framework for writing cloud-native distributed applications in C# language using just its syntax and paradigms of Object-Oriented Programming with the help of built-in support for Task Parallel Library (the async and await keywords).

Motivation

Having experience working on several .NET projects that involve distributed systems, I’ve noticed that we’ve done the same thing over and over again – build a resilient framework that runs a distributed workflow. This is good on one hand, because it’s tailored to project’s needs and can be easily extended, but not that good on other hand, because (as with any internal tool) it often lacks documentation, no community around that technology, and only small group of people know how it works, and things get even more uglier when it gets too complex over time or original authors quit.

If there is a framework available that can be easily used by any software developer, it would save a lot of effort for all of us, and it would be much easier for the community to talk in terms of the same technology. And there are technologies that can help to build distributed workflows, but all of them miss one part – something that can be almost integrated into C# language and .NET framework, so anyone would know how to use it right away. So.. why not to use the syntax of C# itself!

Core Concept

A decade ago we had to build our own libraries that allow to run tasks in parallel using a thread pool, but then the Task Parallel Library (TPL) evolved which quickly became part of the .NET framework, and today we have very cool async-await syntactic sugar that behind the scenes decomposes your code into finite state machines.

If you take a look at a distributed workflow, in general it has exactly the same idea as an async method: you decompose a workflow into state machine, where a state transition ideally should be an idempotent action, then execute state transitions on available nodes where affinity to particular process or virtual/physical machine is not guaranteed (same as TPL does not guarantee execution on a particular thread). The only difference is when a state transition in a distributed workflow fails, you might want to re-try it (such aspect should be built-in to a framework), and to be able to run a workflow action in any process, you must be able to save and restore the state of a state machine.

Building state machines by hand can be very hard, tedious, error-prone, and it’s not easy to understand the flow with large number of small functions that represent state transitions, but it’s very easy to do with the async modifier:
    async Task FiniteStateMachine1()
    {
      // state transition 1
      await FiniteStateMachine2();
      // state transition 2
    }

If you look at how C# compiler generates state machines, you can notice that the await keyword serves as a delimiter between state transitions. Hence, if you can capture and restore the state (including input arguments and local variables) of such functions, and control the execution of underlying state machine (suspend before await, resume after await), then you can build a framework that runs your async functions as a distributed workflow. This is the key to the concept of the D·ASYNC technology, although there is much more beyond that.

Abstraction Layer

D·ASYNC engine acts as a PaaS middleware between a .NET application and a distributed platform, where the C# language is the contract: classes represent services, their async method comprise a resilient distributed workflow. It registers in the .NET runtime of your application to control the executeion of compiler-generated finite state machines, then breaks the normal execution flow and delegates state transition intents with all contextual data to a distributed platform. It’s up to the platform to decide how to serialize data and how to distribute the load between multiple nodes. Regardless how it’s done the D·ASYNC engine reconstructs all necessary state machines and services from a state transition intent, then resumes the execution of an application from saved state.

Normally, you would use a platform API directly in the application code, but D·ASYNC provides a very high level of abstraction and draws a strict boundary between two. For example, all exceptions occurred in the user code always stay in the user code, and all exceptions occurred in engine/platform code always stay there and never get propagated to the user level.

In Short

This technology allows to combine together a general purpose programming language with features of a workflow definition language to facilitate authoring of cloud native applications.


Read Next

D·ASYNC syntax mapping