Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Eternal orchestrations are orchestrator functions that never end. They're useful when you want to use Durable Functions for aggregators, and any scenario that requires an infinite loop.
Eternal orchestrations are orchestrations that never end. They're useful when you want to use durable orchestrations for aggregators and any scenario that requires an infinite loop.
Important
Currently, the PowerShell Durable Task SDK isn't available.
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 schedules new work. If the orchestrator function goes into an infinite loop and continuously schedules work, the history can 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.
The Durable Task SDKs keep track of the history of each orchestration. This history grows continuously as long as the orchestration schedules new work. If the orchestration goes into an infinite loop and continuously schedules work, the history can 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 that becomes the new input for the next orchestrator function generation.
When you call continue-as-new, the orchestration instance restarts itself with the new input value. The same instance ID is kept, but the orchestrator function's history resets.
Instead of using infinite loops, orchestrations reset their state by calling the continue-as-new method on the orchestration context. This method takes a JSON-serializable parameter that becomes the new input for the next orchestration generation.
When you call continue-as-new, the orchestration instance restarts itself with the new input value. The same instance ID is kept, but the orchestration's history resets.
Eternal orchestration considerations
Keep these considerations in mind when using the continue-as-new method in an orchestration:
When an orchestrator function is reset by using the
continue-as-newmethod, the Durable Task Framework maintains the same instance ID but internally creates and uses a new execution ID going forward. This execution ID isn't exposed externally, but it's useful when debugging orchestration execution.When an unhandled exception occurs during execution, the orchestration enters a failed state and execution terminates. In this state, a call to
continue-as-newfrom thefinallyblock of a try-catch statement can't restart the orchestration.
Important
If the orchestration encounters an uncaught exception during execution, the orchestration enters a "failed" state and execution completes. In particular, this means that a call to continue-as-new, even in a finally block, does not restart the orchestration in the case of an uncaught exception.
Keep these considerations in mind when using the continue-as-new method in an orchestration:
When an orchestration is reset by using the
continue-as-newmethod, the Durable Task SDKs maintain the same instance ID but internally create and use a new execution ID going forward. This execution ID isn't exposed externally, but it can be useful when debugging orchestration execution.When an unhandled exception occurs during execution, the orchestration enters a failed state and execution terminates. In this state, a call to
continue-as-newfrom thefinallyblock of a try-catch statement can't restart the orchestration.The results of any incomplete tasks are discarded when an orchestration calls
continue-as-new. For example, if a timer is scheduled and thencontinue-as-newis called before the timer fires, the timer event is discarded.You can optionally preserve unprocessed external events across
continue-as-newrestarts. In .NET and Java,continue-as-newpreserves unprocessed events by default. In Python,continue_as_newdoesn't preserve events unlesssave_events=True. In JavaScript,continueAsNewrequires asaveEventsparameter (trueorfalse) to control this behavior. In all cases, unprocessed events are delivered when the orchestration next callswaitForExternalEventorwait_for_external_event.
Important
If the orchestration encounters an uncaught exception during execution, the orchestration enters a "failed" state and execution completes. In particular, this means that a call to continue-as-new, even in a finally block, does not restart the orchestration in the case of an uncaught exception.
Periodic work example
One use case for eternal orchestrations is code that does 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 aren't based on a schedule. For example, a CRON schedule that runs a function every hour runs at 1:00, 2:00, 3:00, and so on, and could potentially run into overlap issues. In this example, if the cleanup takes 30 minutes, then it schedules at 1:00, 2:30, 4:00, and so on, and there's no chance of overlap.
public class PeriodicCleanupLoop : TaskOrchestrator<object?, object?>
{
public override async Task<object?> RunAsync(TaskOrchestrationContext context, object? input)
{
await context.CallActivityAsync("DoCleanup");
// sleep for one hour between cleanups
await context.CreateTimer(TimeSpan.FromHours(1), CancellationToken.None);
context.ContinueAsNew(null);
return null;
}
}
The difference between this example and a timer-based approach is that cleanup trigger times aren't based on a schedule. For example, a schedule that runs every hour runs at 1:00, 2:00, 3:00, and so on, and could potentially run into overlap issues. In this example, if the cleanup takes 30 minutes, then it schedules at 1:00, 2:30, 4:00, and so on, and there's no chance of overlap.
Start an eternal orchestration
Use the start-new or schedule-new durable client method to start an eternal orchestration, just like you would for any other orchestration function.
Note
If you need to ensure a singleton eternal orchestration is running, 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, use the OrchestrationClient attribute instead of the DurableClient attribute, and use the DurableOrchestrationClient parameter type instead of IDurableOrchestrationClient. For more information about the differences between versions, see Durable Functions versions.
Use the schedule-new client method to start an eternal orchestration, just like you would for any other orchestration.
Note
If you need to ensure a singleton eternal orchestration is running, maintain the same instance id when starting the orchestration.
string instanceId = "StaticId";
await client.ScheduleNewOrchestrationInstanceAsync(
"PeriodicCleanupLoop",
null,
new StartOrchestrationOptions { InstanceId = instanceId });
Exit from an eternal orchestration
If an orchestrator function needs to eventually complete, don't 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.
If an orchestration needs to eventually complete, don't call continue-as-new and let the orchestration exit.
If an orchestration is in an infinite loop and needs to be stopped, use the terminate API on the durable task client to stop it.
await client.TerminateInstanceAsync(instanceId, "Cleanup no longer needed");