I have written a Durable Function orchestrator function, whose primary job is to fan-out an average of 1,000 parallel activities. Since the completion of these activities is something a front-end user would technically be waiting on, I would like be able to query the progress while the activities are still running (to show a progress bar on the front end).
Below is a chunk of the current orchestrator code, but I am having doubts on whether or not it fits the constraints for orchestrator functions (https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-checkpointing-and-replay#orchestrator-code-constraints).
Basically, if the DF framework is replaying the orchestrator up to each await, this feels like an unreasonable number of awaits for it to process:
var replicationTasks = new List<Task<ReplicationOutput>>();
var replicationResults = new List<ReplicationOutput>();
// start up each simulation
for (int i = 0; i < inputs.NumberOfReplications; i++)
{
var replicationInput = new ReplicationInput();
var task = context.CallActivityAsync<ReplicationOutput>("SimulationOrchestrator_SimulateReplication", replicationInput);
replicationTasks.Add(task);
}
// set initial custom status
var progress = new Progress();
progress.NumberCompleted = 0;
progress.Total = inputs.NumberOfReplications;
progress.TimeStarted = context.CurrentUtcDateTime;
progress.ElapsedTime = context.CurrentUtcDateTime.Subtract(progress.TimeStarted);
context.SetCustomStatus(progress);
// as each task finishes
while (replicationTasks.Any())
{
Task<ReplicationOutput> nextFinished = await Task.WhenAny(replicationTasks);
replicationTasks.Remove(nextFinished);
replicationResults.Add(await nextFinished);
// update progress object and custom status
progress.NumberCompleted++;
progress.ElapsedTime = context.CurrentUtcDateTime.Subtract(progress.TimeStarted);
context.SetCustomStatus(progress);
}
// aggregate replications together into a single set of results
return new Results(replicationResults);
This doesn't necessarily fail under simple testing conditions, but the orchestrator documentation warns (fairly aggressively) about keeping the history tables clear, avoiding waiting/blocking, etc.
Is there a documented or "best practices" method for accomplishing the goal of having queryable progress? All fan-out/fan-in examples I've seen just use await Task.WhenAll(replicationTasks)
to only continue once all tasks are complete, which I don't think would allow incremental progress checks.