This page answers frequently asked questions about Azure Durable Functions, a powerful extension for Azure Functions that enables you to write stateful functions in a serverless environment.
Durable Functions is an extension of Azure Functions that allows you to easily implement stateful functions in a serverless compute environment. It extends the capabilities of Azure Functions by providing the ability to coordinate and manage the execution of multiple functions, enabling scenarios like function chaining, fan-out/fan-in, and human interaction.
Durable Functions solve the challenge of managing state and long-running processes in a serverless, stateless environment. Traditional serverless functions are stateless, meaning they don't inherently remember previous executions. Durable Functions provide a way to orchestrate these stateless functions into complex, stateful workflows without the need for external state management services like databases or queues, simplifying development for scenarios such as:
The core concepts include:
orchestrationClient
, orchestrationTrigger
, activityTrigger
) to integrate Durable Functions into your Azure Functions project.Orchestrator functions have specific constraints to ensure their deterministic execution and reliable state management:
context.CurrentUtcDateTime
).yield
, await
) is actually executed.Long-running operations should be implemented as activity functions. The orchestrator function will then call these activity functions. The Durable Functions runtime manages the execution of the activity function, including retries and error handling, and updates the orchestrator's state once the activity is complete.
Example:
// Orchestrator function
[Function("MyOrchestrator")]
public async Task RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
// ...
var result = await context.CallActivityAsync<string>("MyActivityFunction", "input");
// ...
}
// Activity function
[Function("MyActivityFunction")]
public async Task<string> MyActivityFunction(
[ActivityTrigger] string input)
{
// Perform long-running operation
await Task.Delay(TimeSpan.FromSeconds(10)); // Simulate work
return $"Processed: {input}";
}
You can configure timeouts for activity function calls within the orchestrator. The CallActivityAsync
method overload allows you to specify a timeout for the activity execution.
// Orchestrator function
[Function("MyOrchestrator")]
public async Task RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var timeout = TimeSpan.FromSeconds(30);
var retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: 3);
try
{
var result = await context.CallActivityWithRetryAsync<string>(
"MyLongRunningActivity",
retryOptions,
"input data",
timeout); // Specify the overall timeout for the activity call
// ...
}
catch (FunctionTimeoutException)
{
// Handle timeout
}
// ...
}
Yes, activity functions can call other activity functions or orchestrator functions using the IDurableActivityContext
or TaskOrchestrationContext
, respectively.
Calling another Activity from an Activity:
// Activity function
[Function("CallingActivity")]
public async Task<string> CallingActivity(
[ActivityTrigger] string input,
[DurableClient] IDurableClient starter)
{
// To call another activity, you typically need the orchestrator context.
// If you truly need to call an activity from an activity without an orchestrator,
// you might need to start a new orchestrator that calls the activity.
// However, the common pattern is to chain activities within an orchestrator.
// If the goal is to delegate work, the orchestrator is the correct place.
// If you need to initiate a new, independent workflow from an activity:
var orchestrationId = await starter.StartNewAsync("AnotherOrchestrator", input);
return $"Started new orchestration: {orchestrationId}";
}
Note: The more common and idiomatic approach is for the orchestrator function to orchestrate calls to multiple activity functions. Calling activity functions directly from other activity functions can lead to complex dependency management and may not be the intended use of Durable Functions for workflow orchestration.
Entity Functions are ideal for managing stateful entities. Use them when you need to:
Orchestrator Functions are better suited for complex workflow orchestration, defining sequences of operations, handling complex branching logic, and long-running processes that involve multiple steps and interactions.
Entity Functions are designed to handle concurrency using a single-threaded execution model for each entity. When multiple requests target the same entity, they are queued and processed sequentially. This ensures that the entity's state is always consistent and avoids race conditions.
Durable Functions use Azure Storage (typically Azure Table Storage, Queue Storage, and Blob Storage) to persist the state of orchestrations and entities. You must configure a connection string to an Azure Storage account in your local.settings.json
(for local development) or in your Azure Functions app settings.
You can monitor Durable Functions using:
Both Durable Functions and Azure Logic Apps are used for workflow orchestration, but they target different scenarios and developer experiences:
You can also use them together, for instance, a Logic App could trigger a Durable Function, or a Durable Function could call out to a Logic App.