Failover¶
When the leader dies¶
A crashed or killed leader stops renewing. Once expires_at passes (at most lease_ttl, 30 s by
default), the next standby to try acquires the lease with a higher fencing token and starts
claiming. Standbys try at every renewal interval (10 s), so failover takes at most
lease_ttl + lease_renew_interval. The distributed test suite kills a real leader process and
measures it.
A leader that stops cleanly (SIGTERM, a deployment) releases the lease on the way out: a standby takes over at its next attempt.
Occurrences in flight¶
An occurrence claimed but not yet recorded as dispatched stays claimed. The next leader
resubmits claimed rows older than the lease TTL, with the same idempotency key. If the previous
leader had in fact submitted it, the task runs twice: that is the at-least-once window.
When the leader is only paused¶
A leader frozen by a GC pause or a VM snapshot may wake up after a standby took over. It cannot do damage:
- its local deadline (
lease_ttl - lease_renew_interval) has passed, so it stops dispatching; - any claim it attempts carries a stale fencing token and is refused by the database;
- if it froze inside a claim transaction, PostgreSQL ended that session after 10 s.
When PostgreSQL is down¶
Every scheduler stops dispatching: no lease can be renewed. They retry with exponential backoff at every renewal interval and resume once the database is back. No occurrence is claimed twice; an occurrence submitted just before the outage but not yet recorded is resubmitted with the same key (the at-least-once window). Missed occurrences follow the misfire policy.
When the task backend is down¶
Submissions fail and executions become submit_failed, retried with backoff (5 s doubling, at
most 5 minutes) up to max_submit_retries, then failed.