For user-defined jobs, a jobs table could be a better fit than hardcoding a lock around each task. Workers can claim available rows with SELECT FOR UPDATE SKIP LOCKED inside a transaction, then record who claimed the job and when the claim expires.
You'll need a recovery policy for workers that die. If a claim expires and another worker retries, the original worker might still be alive, so a lease by itself doesn't prevent overlapping execution. Use a claim version or fencing token where the downstream writes can enforce it, and make retries safe where possible.
Also decide how scheduled occurrences are created. A unique constraint on the job and scheduled time can prevent multiple schedulers from creating the same occurrence.
unreasonably-good-at-folding:
For work that can outlive a lost connection, consider how to stop a stale worker from writing. Idempotent operations or fencing enforced by the resource being updated can help.