Eternal orchestrations in Durable Functions (Azure Functions)
Eternal orchestrations are orchestrator functions that never end. They are useful when you want to use Durable Functions for aggregators and any scenario that requires an infinite loop.
Orchestration history
As explained in the orchestration history topic, the Durable Task Framework keeps track of the history of each function orchestration. This history grows continuously as long as the orchestrator function continues to schedule new work. If the orchestrator function goes into an infinite loop and continuously schedules work, this history could grow critically large and cause significant performance problems. The eternal orchestration concept was designed to mitigate these kinds of problems for applications that need infinite loops.
Resetting and restarting
Instead of using infinite loops, orchestrator functions reset their state by calling the continue-as-new method of the orchestration trigger binding. This method takes a JSON-serializable parameter, which becomes the new input for the next orchestrator function generation.
When continue-as-new is called, the orchestration instance restarts itself with the new input value. The same instance ID is kept, but the orchestrator function's history is reset.
Note
The Durable Task Framework maintains the same instance ID but internally creates a new execution ID for the orchestrator function that gets reset by continue-as-new. This execution ID is not exposed externally, but it may be useful to know about when debugging orchestration execution.
Periodic work example
One use case for eternal orchestrations is code that needs to do periodic work indefinitely.
[FunctionName("Periodic_Cleanup_Loop")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
await context.CallActivityAsync("DoCleanup", null);
// sleep for one hour between cleanups
DateTime nextCleanup = context.CurrentUtcDateTime.AddHours(1);
await context.CreateTimer(nextCleanup, CancellationToken.None);
context.ContinueAsNew(null);
}
Note
The previous C# example is for Durable Functions 2.x. For Durable Functions 1.x, you must use DurableOrchestrationContext
instead of IDurableOrchestrationContext
. For more information about the differences between versions, see the Durable Functions versions article.
The difference between this example and a timer-triggered function is that cleanup trigger times here are not based on a schedule. For example, a CRON schedule that executes a function every hour will execute it at 1:00, 2:00, 3:00 etc. and could potentially run into overlap issues. In this example, however, if the cleanup takes 30 minutes, then it will be scheduled at 1:00, 2:30, 4:00, etc. and there is no chance of overlap.
Starting an eternal orchestration
Use the start-new or schedule-new durable client method to start an eternal orchestration, just like you would any other orchestration function.
Note
If you need to ensure a singleton eternal orchestration is running, it's important to maintain the same instance id
when starting the orchestration. For more information, see Instance Management.
[FunctionName("Trigger_Eternal_Orchestration")]
public static async Task<HttpResponseMessage> OrchestrationTrigger(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage request,
[DurableClient] IDurableOrchestrationClient client)
{
string instanceId = "StaticId";
await client.StartNewAsync("Periodic_Cleanup_Loop", instanceId);
return client.CreateCheckStatusResponse(request, instanceId);
}
Note
The previous code is for Durable Functions 2.x. For Durable Functions 1.x, you must use OrchestrationClient
attribute instead of the DurableClient
attribute, and you must use the DurableOrchestrationClient
parameter type instead of IDurableOrchestrationClient
. For more information about the differences between versions, see the Durable Functions versions article.
Exit from an eternal orchestration
If an orchestrator function needs to eventually complete, then all you need to do is not call ContinueAsNew
and let the function exit.
If an orchestrator function is in an infinite loop and needs to be stopped, use the terminate API of the orchestration client binding to stop it. For more information, see Instance Management.