A "cron-as-a-service" job scheduler backed entirely by Postgres -- SKIP LOCKED leasing and advisory-lock leader election, no etcd/ZooKeeper/Consul -- twenty ways the coordination quietly breaks.
Nobody has started this yet — be first.
Service fails to restart after the first successful deploy
Every chronos replica applies migrations on startup before serving traffic. If Migrate() crashes on any restart after the very first one, the service can never come back up -- not after a routine redeploy, not after a crash, not after a node reschedule in Kubernetes. This is a cannot-start-at-all bug: the first deploy works, and every deploy after that is an outage. On-call would see every replica crash-looping against a healthy database, a uniquely confusing failure mode at 3am because the database is fine and the migrations are fine -- the code just refuses to believe it.
About this project
A distributed cron scheduler in Go, running as multiple horizontally-scaled replicas that coordinate safely through Postgres alone. Every replica runs the same binary and the same four background loops (leader elector, cron materializer, worker pool, reaper) plus an HTTP API; only the cron materializer is singleton, gated by a pg_try_advisory_lock-based leader election with automatic failover. Workers lease due work with SELECT ... FOR UPDATE SKIP LOCKED so N replicas can poll the same table concurrently without ever double-processing a row. Delivery is at-least-once, explicitly, not exactly-once -- a documented tradeoff, not an oversight -- and graceful shutdown drains in-flight executions within a bounded timeout before exiting.
Twelve tickets are bugs deliberately injected into the real production code (a migration sentinel-error check dropped, a seeding idempotency guard removed, a sign flip on a catch-up cutoff that silently disables the entire product, an inverted comparison that makes the reaper reclaim active leases instead of expired ones, a semaphore slot released in the wrong scope, a session-scoped advisory lock never explicitly unlocked); eight are enhancements that do not exist yet, each with a compile-only stub so the codebase and the verification tests build before the real implementation is written. Every ticket has a dedicated Go test that goes green when the fix is correct, and the project's own existing test suite is affected by the same injected bugs -- fixing ticket 1 first unmasks the real failures from tickets 8 and 12 in that suite.
Restarting the service creates duplicate demo jobs every time
seed.Run is called unconditionally on every boot. In a real deployment, instances restart routinely -- deploys, crashes, autoscaling, node maintenance. If seeding isn't idempotent, every restart adds another copy of every seeded job, each with its own cron schedule quietly firing against the same target_url. For the built-in demo jobs this is wasted load and a confusing job list; if this exact pattern were ever reused to seed real customer-configured jobs, it would mean duplicate charges, duplicate notifications, or duplicate side effects proportional to however many times the service has restarted -- a slow, silent, cumulative correctness bug.
app-2http://localhost:8090curl localhost:8081/healthzcurl localhost:8081/readyzWork the tickets in tickets/docs/ (ordered easy → hard); each names one Go test in tickets/tests/. Most tickets need a real Postgres (tickets 4, 11, and 18 are pure-function tests and do not):
export TEST_DATABASE_URL="postgresql://chronos:chronos@localhost:5432/chronos?sslmode=disable"
go test ./tickets/tests/... -run TestTicket08 -v # a single ticket
./tickets/run_tickets.sh # all 20, clean pass/fail summary
Use -race when working on tickets 15, 16, or 17 (the concurrency ones) -- run_tickets.sh already does this for every ticket. Tickets 15 and 16 are adjacent bugs in the same function (pkg/leader/leader.go's lock-release path); fix ticket 16 first, since ticket 15's test keeps failing for ticket 16's reason until that one is fixed too.
How to run it locally, step by step.