The Cost of Remembering
Now that you know how replay works — the orchestrator re-executes from the top on every wake, replaying completed operations from history — one consequence deserves its own lesson, because it turns "just add one more step" into a decision with a real price.
The one idea to hold on to
Every awaited durable operation you add to the loop is replayed on every later wake in that cycle — so operation count compounds. An orchestrator with N awaited operations is re-executed about N times over one cycle, and each of those executions re-walks all the operations before the current one. The total replayed work grows faster than the number of operations you added, not linearly with it.
Feel it — toggle operations into the loop and watch the replayed work climb:
Toggle the operations you'd add to the loop. Five are essential; the rest are things it's tempting to do "while we're here."
Lease responsiveness: snappy — Lean loop. In lease mode (1s cadence) replay overhead stays invisible.
5 awaited ops → the loop is re-walked from history ~5 times, replaying 15 step-executions. ContinueAsNew wipes history between cycles — but a fast lease loop pays this every single wake-up.
Every operation isn't just one more call — it's one more step that every later execution in that cycle has to replay. That's why "while we're in here, let's also submit to ESRI / add a telemetry signal / re-check the guard tour" is a load-bearing decision, not a freebie.
Why lease mode is where this bites
Standard mode wakes every ~10 seconds; the replay overhead of a cycle is lost in the noise. Lease mode wakes about every second — an operator is dragging the camera and wants it to move now. Every one of those fast cycles pays the full replay cost, so a loop that's a little heavy turns into visible lag exactly when responsiveness matters most. Keeping the loop lean isn't tidiness; it's the difference between a camera that tracks the operator's hand and one that stutters.
The engine already applies this discipline, and it does it without adding an operation at all. The entity write that stores each capture's position hands back whether a change is still unpublished, so the loop learns about the move on a round trip it was already paying for:
// inside the mode orchestrator, on the write it already performs
positionWrite = await context.Entities.CallEntityAsync<PositionWrite>(
entityId, nameof(CameraState.SetPTZ), imageResponse.Position);
// back in the loop, nothing new is asked of the entity
if (currentMode != Orchestrations.PANO && hasUnpublishedPositionChange && latestPosition is not null)
{
context.CallActivityAsync<bool>(nameof(SubmitCameraPositionToESRI), updateRequest); // fire and forget
await context.Entities.SignalEntityAsync(entityId, nameof(CameraState.ClearPositionChangedFlag));
}
Two details make that work. The write returns the position the entity kept, not the one that was offered — a zero-filled body mid-move is rejected, and publishing the offered value would put a bogus position on the map. And the flag accumulates on the entity rather than being consumed by a read: a pano pass never clears it, so the change it carries is published by the first pass that is allowed to publish.
ContinueAsNew is what makes the eternal loop affordableReplay showed that ContinueAsNew resets the history each cycle. Here's
why that's not optional: without it, an eternal loop would append to one ever-growing history,
and every replay would get slower as it re-walks more events — for a camera running weeks, that's
millions of events and an orchestration that eventually falls over. ContinueAsNew keeps each
cycle's history small, so the per-cycle replay cost stays bounded no matter how long the camera
runs. Removing it doesn't break the logic immediately; it degrades the orchestration until it does.
Wrapping up
Replay makes operation count expensive: each awaited operation is re-walked by every later
execution in the cycle, so cost compounds — most painfully in the fast lease loop, where every
second pays it. ContinueAsNew bounds the per-cycle history so the eternal loop stays cheap.
Before adding a step, ask whether it can be gated on change, made conditional, or skipped in
lease mode.
The busiest thing that flows through that lean loop is operator movements. Next: One Move at a Time.