Replay, Restart, and Retry
Three ways to re-process an event or execution, and when to use each
Restaq gives you three ways to recover from or debug a failed execution. They sound similar but mean very different things, so it's worth knowing which one you actually want.
Three operations, one table
| Resume in place | Fresh execution | Re-run completed steps | |
|---|---|---|---|
| Retry | ✓ | ✗ | ✗ |
| Restart | ✓ | ✗ | ✓ |
| Replay | ✗ | ✓ | ✓ |
Retry
Resume the same execution, skipping completed steps.
await restaq.retryExecution(executionId);When to use: The handler is correct, but a flaky external service (email, payment gateway, etc.) timed out. Retrying continues from where it left off without re-running side effects that already succeeded.
Example: A receipt email provider times out. You retry, the email step runs again, and it succeeds. The original execution ID and history are updated with the new attempt.
Restart
Resume the same execution, but force every step to re-run from the beginning.
await relay.restartExecution(executionId);When to use: You found a bug in the handler logic itself (not external flakiness). You fix the code, then restart to re-process the same event data under the corrected logic.
Example: The fee calculation was wrong by one penny. You fix the code, restart the execution, and calculate-fee re-runs with the corrected logic. The original payment record (from record-payment) may be duplicated or need cleanup—restart assumes your handler is idempotent or that you'll handle side effect conflicts.
Caveat: Unlike retry, restart re-runs every step, including ones with side effects (database inserts, API calls). Make sure your handler is idempotent or you manually clean up duplicates.
Replay
Create a brand-new execution from historical event data.
await restaq.replayExecution(executionId);When to use: You want to test a handler change against real historical data without modifying the original execution's audit trail. Or you want to reprocess a high-priority order through a new fulfillment system.
What happens:
- A fresh
eventIdis generated (not the original's) - A new
Executionis created withattempt: 1 - The new execution's
replayedFromfield points to the source execution - Handler runs from scratch under current code
- Original execution is untouched
Example: A customer reports a payment wasn't processed correctly. You replay the original charge event through your handler (after investigating and fixing bugs), and you get a fresh execution record with the full step history. If it succeeds, the customer got the benefit; if it fails differently, you have a new audit trail to debug.
Automatic vs. manual
- Automatic retry: When a handler fails, Restaq schedules the next retry automatically after a backoff delay. No human action needed.
- Manual: You explicitly call
retryExecution,restartExecution, orreplayExecutionvia the API or CLI.
Implementation details
All three operations:
- Are available via the Relay API
- Are inspectable via
relay inspect <executionId>or the CLI - Are logged and appear in the execution's log stream as system events
- Support concurrent calling (internal locking prevents race conditions in the same process)
Replay also supports a dry-run mode via the CLI:
relay replay <executionId> --printThis shows you what would happen without actually posting to your webhook handler.