Skip to content

[feature] Rerun a failed run from the step that failed, reusing results already recorded #501

Description

@bilyiv

Primary owning public component

Workflow engine

User problem

A run that has failed terminally can only be replaced by a new run started from the top. Every step the failed run already completed is executed again, even though its result is recorded in that run's history and will not be different this time.

Where those steps are cheap and internal that is merely wasteful. Where they are paid, rate-limited or externally visible — credit checks, payment attempts, notifications, anything a third party bills for or a customer sees — re-running them is itself a business event. The practical consequence is that operators stop using the retry: they read the history, work out what actually needs redoing, and do it by hand. That is precisely the position durable execution is meant to remove them from.

Nothing in the public control plane continues a closed run. start makes a new one, signal, update and query talk to a live one, cancel and terminate end one, repair recovers a stuck one, archive and describe are about records. A failed run is finished, and the only forward path is to start again from nothing.

Child runs come closer: a child can be retried under a retry policy, and the history contract records retry_of_child_workflow_run_id, so the engine already models "this run is another attempt at that one". But a child retry begins the child again from its first step, and there is no equivalent for a top-level run at all.

So the gap gets filled outside the engine: read the failed run's history, work out how far it got, start a fresh run, and feed the earlier results back so the completed steps are not executed a second time. That works, but it depends on the recorded shape of history, which is engine-internal and not something an application should be reasoning about — and it is quiet when it is wrong. The failure mode is re-executing a step that should have been skipped, which looks like success and surfaces as a duplicate charge or a second notification.

Proposed public contract

A control-plane operation that continues a failed run from where it stopped, rather than from the beginning. It produces a new run; the failed one stays as it is, closed and readable.

Behaviour

  • Activities that completed successfully in the failed run are not executed again; their recorded results are returned to the workflow body as it replays.
  • The step that failed is attempted again, and execution carries on from there.
  • Called on a run that did not fail, it is refused with a reason rather than quietly starting a fresh run from the top. A run that is still going, one that completed, one that was cancelled or terminated — none of these has a failure to continue from.
  • The new run records its provenance: which run it continues, and which step it resumed at.

Operator experience

That provenance is readable through describe(), and steps served from the earlier run are distinguishable from steps executed afresh, in history and in any activity listing. Without that distinction an operator cannot answer "which external calls did this attempt actually make?" — which is the question the whole feature exists to make answerable, and the one that decides whether a customer was charged twice.

Worth deciding explicitly

  • Whether the resume point can be chosen rather than always being the failure. When the failing step is a symptom of a bad value produced two steps earlier, resuming at the failure just fails again.
  • Whether a resumed run is itself resumable, and what its history looks like to a second resume.
  • What happens to a failed run's children — both those that had already completed, and those still in flight when the parent failed.

Acceptance criteria

  1. A failed run can be continued through the public control plane, producing a new run that starts at the step which failed.
  2. Activities that completed in the original run are not executed again; their results are served from that run's recorded history.
  3. The step that failed is executed again, and the run proceeds from there.
  4. The new run names the run it continues and the step it resumed at, both through describe() and in its own recorded history.
  5. The original run is left as it was — closed, with its history intact and readable.
  6. Continuing a run that is running, completed, cancelled or terminated is refused with a stated reason, never silently converted into a fresh run from the top.
  7. Steps served from the earlier run are distinguishable from steps executed afresh, in history and in activity listings.
  8. Additive — callers that never invoke it see no change in behaviour, and existing runs are unaffected.

Supporting public evidence

Comparable durable-execution and orchestration products all provide some form of this, which suggests it is expected rather than exotic:

  • AWS Step Functions — Redrive. Restarts a failed execution from the point of failure; states that already succeeded are not run again. The closest match to what is asked for here, in both name and semantics.
  • Temporal — Reset. Starts a new run from a chosen point in a previous run's history and re-executes only from there.
  • Netflix Conductor — retry from last failed task. Resumes a failed workflow at the task that failed, keeping the work already done.

People arriving from any of these expect a retry-from-failure to exist, and tend to discover it does not the first time an expensive workflow fails in production.

patched(), with its VersionMarkerRecorded history entry, already gives a replayed prefix a defined answer when code has changed since it ran — so the harder half of this is shipped.

Dependencies and affected repositories

None.

Public intake checks

  • I searched open and closed GitHub issues for an existing request.
  • This request contains only public product contracts and public-safe rationale.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    authority:githubGitHub is the authoritative lifecycle record for this workintake:approvedCurrent issue title and body revision is approved for authority intakekind:featureA public product capability or experience is requestedpriority:P2Normal-priority product workstatus:readyReady for implementation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions