Azure Functions Durable

Activity Functions

Activity functions are the fundamental building blocks of Durable Functions. They represent individual units of work that can be composed into complex workflows by orchestrator functions.

What are Activity Functions?

An activity function is a standard Azure Function that performs a specific, discrete task. This could be anything from calling an external API, querying a database, performing a calculation, or sending a notification. Unlike orchestrator functions, activity functions do not need to be deterministic and can have side effects.

When an orchestrator calls an activity function, Durable Functions ensures that the activity is executed reliably and that its output is captured. If an activity fails, it can be retried according to the retry policy defined in the orchestrator.

Key Characteristics of Activity Functions:

  • Perform a single unit of work.
  • Can have side effects (e.g., writing to a database, calling external services).
  • Do not need to be deterministic.
  • Executed by the Durable Functions extension and their results are reliably tracked.
  • Can be called multiple times within an orchestration.

Defining and Calling Activity Functions

Activity functions are typically defined as standard Azure Functions in your preferred language (C#, JavaScript, Python, PowerShell, etc.). The orchestrator function then invokes them using methods provided by the Durable Functions SDK.

Example (C#):


// Define an Activity Function
[FunctionName("ProcessOrderActivity")]
public static async Task<string> ProcessOrderActivity([ActivityTrigger] Order order)
{
    log.LogInformation($"Processing order: {order.OrderId}");
    // ... perform order processing logic ...
    return $"Order {order.OrderId} processed successfully.";
}

// Define an Orchestrator Function that calls the Activity
[FunctionName("OrderWorkflow")]
public static async Task RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    Order order = context.GetInput<Order>();

    string processingResult = await context.CallActivityAsync<string>("ProcessOrderActivity", order);

    // ... further orchestration logic ...
}

public class Order
{
    public string OrderId { get; set; }
    // ... other order properties
}
                

Example (JavaScript):


// Define an Activity Function
const df = require("durable-functions");

module.exports = df.activity.createActivityFunction("processOrderActivity", async function(order) {
    console.log(`Processing order: ${order.orderId}`);
    // ... perform order processing logic ...
    return `Order ${order.orderId} processed successfully.`;
});

// Define an Orchestrator Function that calls the Activity
module.exports = df.orchestrator.createOrchestratorFunction("orderWorkflow", function* (context) {
    const order = context.df.getInput();

    const processingResult = yield context.df.callActivity("processOrderActivity", order);

    // ... further orchestration logic ...
});
                

Input and Output

Activity functions can accept input parameters and return a result. The orchestrator can pass data to the activity and receive the output back to use in subsequent steps of the orchestration.

If an activity function doesn't return a value, you can use Task (C#) or void (JavaScript) as the return type, and the orchestrator won't expect a return value.

Error Handling and Retries

Durable Functions provide robust error handling and retry mechanisms for activity functions. You can configure retry policies within your orchestrator function to automatically re-execute failed activities, specifying the number of retries, delay between retries, and backoff modes.

Example (C# Retry Policy):


[FunctionName("OrderWorkflowWithRetry")]
public static async Task RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var retryOptions = new RetryOptions(TimeSpan.FromSeconds(10), 3)
    {
        BackoffCoefficient = 2,
        Handle = ex => ex is SomeSpecificExceptionType
    };

    try
    {
        string processingResult = await context.CallActivityWithRetryAsync<string>("ProcessOrderActivity", retryOptions, order);
        // ...
    }
    catch (Exception ex)
    {
        // Handle permanent failure after retries
        log.LogError($"Activity failed after retries: {ex.Message}");
    }
}
                

Activity functions are the workhorses of your Durable Functions workflows, encapsulating specific business logic that can be orchestrated and managed reliably by orchestrator functions.