Retry Policy¶
What triggers a retry¶
Only SQLSTATE class 40 errors trigger an automatic retry. SQLSTATE is PostgreSQL's own standard error-code system; class 40 covers transient conflicts between concurrent operations:
40001— Serialization failure (two transactions conflicted in a way that could not both succeed)40P01— Deadlock detected (two operations were each waiting on the other)
These are transient infrastructure problems, where trying again after a short pause is likely to succeed. Every other kind of error — type errors, constraint violations, permission errors, a syntax error in your action SQL — is a permanent failure. These are logged as error immediately, with no retry, because the same error would simply happen again on every attempt.
Retry backoff schedule¶
When a retry is scheduled, a new row is inserted into pgrelay.queue with a future run_at, based on the new row's retry_count:
| retry_count | Delay before next attempt |
|---|---|
| 1 | 3 seconds |
| 2 | 5 seconds |
| 3 and above | 10 seconds |
The original queue row is marked done, and a new row is created to represent the retry. The parent_id column links the retry row back to the original.
Log entries for retried events¶
A single event that is retried twice creates three log rows in total:
retry_scheduled— first failure, a retry is queued (the underlying error appears in theerrorcolumn, alongside the ID of the new retry row).retry_scheduled— second failure, another retry is queued.ok(orerror) — the final outcome.
If the retry limit is reached on the last attempt, that final row is max_retries_exceeded instead of retry_scheduled.
How max_retries is set¶
-- Allow up to 3 retry attempts (4 total attempts):
SELECT pgrelay.register('my_channel', 'SELECT my_fn($1)', max_retries := 3);
-- Change an existing channel to allow 5 retries:
SELECT pgrelay.update('my_channel', p_max_retries := 5);
Default max_retries is 0 — no retries.
Note
The retry mechanism described above (a queue-side row linked by parent_id) is shared infrastructure used by every action type. What actually triggers a retry differs by action type: SQL events retry on a SQLSTATE class 40 error, as described here; 'notify' events (SMTP, Microsoft 365, or webhook, via the pg_relay_notifier companion extension) retry on transient provider failures instead — see pg_relay_notifier compatibility — using the identical backoff schedule and the same max_retries setting.
Continue to Log Levels and Format to understand every message the Processor can write.