Question

Difficulty: MediumImplement Durable Functions

You are developing a nightly backup verification workflow using Azure Durable Functions in Python. The orchestrator function must periodically call an activity function to check the status of a database backup job. If the backup is not yet complete, the orchestrator must wait for 15 minutes before checking the status again.

You write the following orchestrator function:

python
import azure.durable_functions as df
import time

def orchestrator_function(context: df.DurableOrchestrationContext):
backup_id = context.get_input()
max_attempts = 5

for attempt in range(max_attempts):
status = yield context.call_activity("CheckBackupStatus", backup_id)
if status == "Completed":
return "Success"

# Wait 15 minutes before the next check
time.sleep(900)

return "Failed"

During testing, the function fails because of thread blocking and invalid replay behavior.

Which modification should you make to ensure the orchestrator function runs correctly without blocking execution threads?

  1. Replace the time.sleep(900) call with yield context.create_timer(context.current_utc_datetime + datetime.timedelta(minutes=15)) after importing datetime.Answer
  2. B
    Replace the time.sleep(900) call with await asyncio.sleep(900) after importing the asyncio library.
  3. C
    Move the time.sleep(900) call into a new activity function called DelayActivity and call it using yield context.call_activity("DelayActivity", 900).
  4. D
    Change the hosting plan of the Function App to a Dedicated (App Service) plan to prevent the orchestrator from timing out during the time.sleep period.

Answer

Use context.create_timer with context.current_utc_datetime and datetime.timedelta to schedule a durable timer, allowing the orchestrator to yield and suspend execution safely.
The correct answer is to use a durable timer scheduled via the orchestrator context. Durable orchestrators must be deterministic and cannot execute blocking calls like sleep. Using context.create_timer allows the runtime to suspend the orchestrator, save its current state, and wake it up at the specified datetime without consuming resources or blocking worker threads.

Step-by-Step Solution

1
Analyze the orchestrator code for blocking or non-deterministic operations.
Identify that time.sleep(900) is a blocking call that keeps the thread active and violates the determinism constraint of Durable orchestrators.
Orchestrator functions must yield control back to the runtime to allow state checkpointing and replay without executing blocking side effects.
2
Identify the appropriate Azure Durable Functions API for implementing delays.
Select context.create_timer which schedules a message in the control queue to resume execution at a future timestamp.
Durable timers ensure the orchestrator is unloaded from memory while waiting, avoiding thread blocking and extra billing costs.
3
Calculate the expiration timestamp deterministically using orchestrator context.
Use context.current_utc_datetime instead of datetime.datetime.utcnow() combined with datetime.timedelta(minutes=15) to set the expiration.
Using context.current_utc_datetime guarantees that during replay, the time remains consistent, preserving orchestrator determinism.

Key Concept

Durable Functions Orchestrator Determinism and Timers
Rate this question