The moment you run more than one replica, every @Cron job, every sweep, every "once an hour" task fires once per replica. Three pods, three duplicate settlements. You need exactly one of them to act.
You don’t need a consensus library
Reaching for Raft or ZooKeeper here is over-engineering. If you already run Postgres, it ships with everything you need: advisory locks.
// Only the holder of this lock runs the job.
const [{ locked }] = await db.query(
'SELECT pg_try_advisory_lock($1) AS locked', [JOB_KEY],
)
if (!locked) return // another replica is leader
try { await runSweep() }
finally { await db.query('SELECT pg_advisory_unlock($1)', [JOB_KEY]) }A session-scoped advisory lock is released automatically when the connection drops. If the leader crashes mid-job, its lock dies with it and another replica picks up next tick. The failure mode is built in.
Properties you get for free
- Exactly one active worker at a time
- Automatic failover when the leader disappears
- No new infrastructure — it’s just your existing database
- Re-entrant per key, so different jobs use different lock keys
Leader election for people who only have one job is one row, one lock, and a finally block.
Reach for Raft when you need a replicated log. For "run this once," reach for the database you already have.