Question

Difficulty: MediumImplement Durable Functions

You are developing a document approval workflow using Azure Durable Functions in C# (.NET Isolated). The workflow must wait for an external approval event named `DocumentApproved` for up to 2424 hours. If the event is received within 2424 hours, the document is processed. If the 2424-hour limit is reached without receiving the event, the document must be marked as expired. You write the following orchestrator function code:

csharp
[Function("ApprovalOrchestrator")]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var approvalTask = context.WaitForExternalEvent<bool>("DocumentApproved");
var timeoutTask = Task.Delay(TimeSpan.FromHours(24));

var completedTask = await Task.WhenAny(approvalTask, timeoutTask);
if (completedTask == approvalTask)
{
bool isApproved = approvalTask.Result;
await context.CallActivityAsync("ProcessDocument", isApproved);
}
else
{
await context.CallActivityAsync("ExpireDocument", null);
}
}

Which of the following describes the defect in this orchestrator code?

  1. A
    The `context.WaitForExternalEvent` method must be executed within a try-catch block to handle the `TimeoutException` thrown by the event listener.
  2. B
    Durable Functions do not support waiting for external events and timers simultaneously; you must split them into two separate sub-orchestrations.
  3. The use of `Task.Delay` violates the determinism constraint of orchestrator functions; you should use `context.CreateTimer` instead.Answer
  4. D
    The orchestrator must use `Task.WaitAny` instead of `Task.WhenAny` because `Task.WhenAny` executes asynchronously and bypasses the state replay logic.

Answer

The use of Task.Delay violates the determinism constraint of orchestrator functions; you should use context.CreateTimer instead.
The correct answer is correct because orchestrator functions in Azure Durable Functions must be completely deterministic. Because they replay their execution state, developers must avoid non-deterministic APIs such as Task.Delay, Guid.NewGuid, or DateTime.UtcNow. Instead, durable orchestrator APIs like context.CreateTimer must be used to schedule timers, as this registers the timer event in the orchestration history and allows the orchestrator to safely suspend execution without blocking resources.

Step-by-Step Solution

1
Analyze the orchestrator code to identify non-deterministic or blocking APIs.
Identify the use of Task.Delay(TimeSpan.FromHours(24)) on the second line.
Orchestrator functions must be deterministic, and Task.Delay is non-deterministic because it does not register with the Durable Functions state store.
2
Determine the correct Durable Functions API to replace the non-deterministic call.
Identify context.CreateTimer as the appropriate API for scheduling delays in orchestrators.
context.CreateTimer creates a durable timer that persists its state and allows the orchestrator to sleep and replay correctly.
3
Evaluate the rest of the orchestration logic (Task.WhenAny, WaitForExternalEvent, and CallActivityAsync).
Confirm that task orchestration and external events are correctly structured using task combinators.
Task.WhenAny is the correct asynchronous, non-blocking method to wait for the first of multiple tasks to complete.

Key Concept

Durable Functions Orchestrator Determinism
Rate this question