When using Azure functions we usually deal with a strict stateless model. Each function execution is independent from other executions. This is a key factor why it is so easy to scale functions.
However using Azure functions we are limited:
An Azure function has to complete within a certain time frame (e.g. max 10min for Consumption plan)
We cannot model complex workflows or long-running operations.
Consider Durable Functions when
You need to coordinate the execution of multiple functions and manage complex control flow and error handling.
Your application requires stateful execution and the ability to pause, resume, or retry workflows.
Human interaction, such as approvals or notifications, is an integral part of your application’s workflow.
Building blocks
Orchestrator
Orchestrates the function execution.
Can call other functions. The output can be stored in local variables.
Can be long-running. Lifespan of an orchestrator function can be from milliseconds to never-ending.
Activity
Are the units of work within a durable functions app.
Are called by an orchestrator.
External Event
Are events triggered by any external client.
Supports bindings and HTTP API.
Orchestrator sample
Activity sample
External event sample
Raise an external event
via orchestration client binding:
Raise an external event (2)
via HTTP API:
Orchestrator Function execution
Orchestrator function constraints
Orchestrator functions will be replayed multiple times from the beginning.
Therefore orchestrator functions need to be deterministic - they need to produce the same result every time they are being called.
We only can use deterministic APIs inside orchestrator functions to ensure this. A deterministic API is an API that always returns the same value given the same input.
Things to avoid:
DateTime.Now
Guid.NewGuid()
Random numbers
Avoid using static variables and environment variables in orchestrator functions because their values can change over time, resulting in nondeterministic runtime behavior.
Orchestrator code must never start any async operation except those defined by the orchestration trigger’s context object. For example, never use Task.Run, Task.Delay, and HttpClient.SendAsync.
Durable Function Patterns
The primary use case for Durable Functions is simplifying complex, stateful coordination requirements in serverless applications. We will have a look at typical application patterns that can benefit from Durable Functions.
Function chaining
A sequence of function has to execute in a specific order. The output of one function execution acts as input of the following function.
Fan out / fan in
Execute multiple functions in parallel and then wait for all functions to finish.
Async HTTP APIs
When dealing with HTTP requests we sometimes have the need to kick off a long-running operation. We often deal with this by having one endpoint for starting the operation and one endpoint for receiving the status or result.
Monitor
Recurring checks of other services.
Human interaction
Wait for events (often triggered by human interaction) that might occur much later.