Serverless computing is becoming extremely popular in the cloud world, with all the major players offering their own implementation of FaaS (Function as a Service) solutions. This success should not be surprising since there are many advantages in taking the serverless approach for the design of a distributed application.
First of all, a serverless cloud application does not require any management of the underlying infrastructure: there is no server to maintain, no operating system to update, etc. The cloud provider is responsible for the performance and the availability of the computing resources. Moreover, providers are also responsible for making the application scale correctly, relieving developers and operators from the responsibility of tuning and testing scaling policies. Last, but not least, serverless architectures may reduce the overall costs because the providers charge only for the time the functions run.
The development model usually associated with serverless computing is based on three main concepts: it forces an event-driven approach, requires functions to be short lived and requires functions to be purely stateless.
The event-driven requirement is a direct consequence of the serverless approach. Functions are loosely coupled entities in our application that exchange asynchronous messages that describe a change in a state and react accordingly. They run only when they are triggered by specific events and they do not care about who generated the trigger.
The requirements on the nature of the serverless functions are due to the provisioning models used by the cloud provider. Since function instances can be provisioned and de-provisioned at any time, on different servers, they must run for a short period of time and cannot carry any stored data with them.
Implementing a fully serverless application can be quite challenging. First of all, as for any event driven approach, when components are highly decoupled the system become opaque and can exhibit an unpredictable behaviour. Moreover, stateless components do not fit every possible use case and relying on external storage may not be the best option in terms of both performance and costs.
The most common usage of serverless functions is to implement some “glue” logic that interconnect several services in an efficient way. Managed services generate or consume events while serverless functions implement the application business logic one piece at a time. However, while the initial development of an application like this can be fast and straightforward, debugging and implementing error handling can be quite difficult. Even simple back propagation of an error condition can be difficult: since functions are stateless, how can a function notify an error event to the previous function in a sequence?
Talking about these limitations and the solutions offered by the Azure platform, Mikhail Shilkov, Microsoft Azure MVP, gave an interesting talk at Codemotion Milan 2018.
Shilkov showed how Azure Durable Functions can be used to overcome the limitations of Azure Functions as described above. In particular, Durable Functions bring orchestration abstractions to Azure Functions, allowing the implementation of stateful functions.
— Mikhail Shilkov (@MikhailShilkov) November 29, 2018
Durable Functions is implemented as an external (and open source) library and allows the implementation of several stateful patterns. The execution model is designed around two main entities: the Activity, which is the entity that runs a stateless task, and the Orchestrator, which adds context to the stateless application and coordinate the execution of one or more activities.
The simplest pattern implementable with Azure Durable Functions is function chaining. In function chaining, two or more Azure functions are triggered in a sequence. Error handling and retries are gracefully tracked and managed thanks to the Orchestration context.
Another common pattern is the Fan-out / Fan-in, in which multiple functions are triggered in parallel and then some aggregation processing is performed when all of them have finished. Without Durable Functions Fan-out can be implemented with multiple queued messages. However, fanning back in case of errors can be extremely complex.
Durable Functions can be used to easily orchestrate calls to external HTTP APIs, which may have long execution times and can even timeout. They can also be used to wait on external events, e.g. when dealing with human interaction.
In conclusion, although serverless computing is still young, Azure Durable Functions provide a possible answer to overcome the limitations of pure FaaS approaches.