Skip to content

Allow flow handlers to select the next retry delay #655

Description

@jumski

Summary

Allow a flow handler to choose the delay before its next retry.

This supports rate-limited APIs that return Retry-After, polling workflows, and transient failures whose recovery time is known only after the handler runs.

Problem

Flow retry timing is currently derived from the static flow or step retry configuration. A handler can report failure, but it cannot durably pass a delay for that specific retry.

Applications that must honor a provider-selected delay therefore need to own queue visibility changes outside pgflow. That breaks pgflow's orchestration boundary and duplicates retry plumbing.

Proposed behavior

Provide one flow-worker error or equivalent directive:

throw new RetryAfterError(delaySeconds, { cause })

The exact public name is open. The behavior should be:

  • pass the selected delay to pgflow.fail_task;
  • use it instead of static exponential backoff when the task remains retryable;
  • preserve current retry behavior when no override is present;
  • ignore the override when attempts are exhausted and fail the task normally;
  • reject invalid, negative, non-finite, or out-of-range delays before the database call;
  • store the original error message and cause through the existing failure path.

Keep existing pgflow.fail_task callers compatible by adding an optional argument or overload rather than replacing the current contract.

Open design question: attempt accounting

An intentional delayed retry may represent scheduling rather than a failed attempt. Consider whether the directive should optionally avoid incrementing the attempt count, or reset the attempt count, when the handler selected the retry intentionally.

Do not make this part of the first implementation without a separate decision on semantics, limits, and abuse prevention. The minimum useful feature is an exact per-failure delay override with today's attempt accounting unchanged.

Acceptance criteria

  • A handler can select the exact visibility delay for the next retry.
  • Ordinary thrown errors retain the existing static backoff behavior.
  • An exhausted task fails instead of returning to the queue.
  • Invalid delay values fail before the database call.
  • Existing fail_task calls remain compatible.
  • SQL, worker, migration, and documentation tests cover both override and default paths.

Out of scope

  • Permanent-failure error types.
  • Final-failure hooks.
  • Jitter configuration.
  • Mutable retry-policy objects.
  • Queue routing.
  • Changes to attempt accounting until the open design question is resolved.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions