Question

Difficulty: Very hardImplement Durable Functions

You are developing a shipment monitoring workflow using C# Azure Durable Functions. The workflow must poll an external shipping provider API every 5 minutes for up to 2 hours, or terminate early if the status becomes 'Delivered'.

You write the following orchestrator function code:

csharp
[FunctionName("MonitorShipmentOrchestrator")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
string shipmentId = context.GetInput<string>();
DateTime endTime = DateTime.UtcNow.AddHours(2);

while (DateTime.UtcNow < endTime)
{
string status = await context.CallActivityAsync<string>("GetShipmentStatus", shipmentId);
if (status == "Delivered")
{
await context.CallActivityAsync("FinalizeOrder", shipmentId);
return;
}

await Task.Delay(TimeSpan.FromMinutes(5));
}
}

Which set of changes must you apply to the code to ensure that the orchestrator remains deterministic and executes correctly without blocking orchestrator threads?

  1. Replace DateTime.UtcNow with context.CurrentUtcDateTime, and replace await Task.Delay(TimeSpan.FromMinutes(5)) with await context.CreateTimer(context.CurrentUtcDateTime.AddMinutes(5), CancellationToken.None).Answer
  2. B
    Replace DateTime.UtcNow with context.CurrentUtcDateTime, but retain await Task.Delay(TimeSpan.FromMinutes(5)) as the await keyword prevents thread blocking by yielding execution back to the host.
  3. C
    Retain DateTime.UtcNow because the Durable Functions framework handles standard system clock calls during execution, but replace await Task.Delay(TimeSpan.FromMinutes(5)) with await context.CreateTimer(context.CurrentUtcDateTime.AddMinutes(5), CancellationToken.None).
  4. D
    Replace DateTime.UtcNow with context.CurrentUtcDateTime, and replace await Task.Delay(TimeSpan.FromMinutes(5)) with Thread.Sleep(TimeSpan.FromMinutes(5)) to execute the loop block synchronously.

Answer

Replace DateTime.UtcNow with context.CurrentUtcDateTime, and replace await Task.Delay(TimeSpan.FromMinutes(5)) with await context.CreateTimer(context.CurrentUtcDateTime.AddMinutes(5), CancellationToken.None).
The correct choice resolves both non-deterministic violations by using context.CurrentUtcDateTime, which guarantees the same timestamp is returned during replays, and context.CreateTimer, which registers a durable timer with the execution history and yields execution back to the runtime.

Step-by-Step Solution

1
Analyze the orchestrator's temporal checks for non-determinism.
DateTime.UtcNow will yield different timestamps on replays.
Durable orchestrators replay their execution history to reconstruct state; using the system clock directly violates the determinism constraint.
2
Identify the proper replacement for tracking current time in the orchestrator.
Use context.CurrentUtcDateTime.
This property returns a deterministic timestamp that is saved in the orchestration history and replayed consistently.
3
Analyze the delay mechanism used in the orchestrator.
await Task.Delay(TimeSpan.FromMinutes(5)) is non-deterministic and fails to schedule a durable timer.
Orchestrator functions must not call non-durable async APIs that create unmanaged tasks or sleep threads.
4
Replace the delay with the durable framework's timer API.
Use await context.CreateTimer(context.CurrentUtcDateTime.AddMinutes(5), CancellationToken.None).
This registers a durable timer with the framework, allowing the orchestrator to safely sleep, free up resources, and resume later.

Key Concept

Durable Functions Orchestrator Code Constraints and Determinism
Rate this question