Nobody has started this yet — be first.
Business impact
Leader election with automatic failover is the headline feature of this project -- the README's entire "kill the leader and watch failover" demo depends on it working. As shipped, it doesn't: every graceful shutdown (a deploy, a rolling restart, a scale-down) permanently leaks the leadership lock. Once that happens, no instance in the cluster can ever become leader again until someone notices cron jobs have stopped materializing and manually intervenes. This turns routine, expected operational events (every deploy) into the trigger for a total, silent outage of the scheduling function, with the API, health checks, and worker pool all reporting healthy the entire time.
Problem
pkg/leader/leader.go's release is supposed to unlock the advisory lock before returning the connection to the pool, but the call to SELECT pg_advisory_unlock($1) is missing entirely. e.conn.Release() only returns the connection to the pgxpool's idle pool for reuse -- it does not end the underlying Postgres session, and Postgres advisory locks are held by the session (backend connection) until that session either explicitly unlocks or the connection itself is closed. The lock stays held by a connection the pool now thinks is just an ordinary, available connection.
Current behavior
After an Elector cleanly steps down (context cancelled), a brand-new, independent connection's SELECT pg_try_advisory_lock($1) with the same lock ID returns false indefinitely -- the lock is still held.
Expected behavior
release must call pg_advisory_unlock on the held connection before returning it to the pool, so that once this instance steps down, some other instance's pg_try_advisory_lock can actually succeed.
Steps to reproduce
Why this matters
This is a case where the comment on the type (Elector -- "Advisory locks in Postgres are session-scoped: they live and die with the connection that took them") correctly describes the semantics, but the code that's supposed to act on that semantic doesn't. pgxpool.Conn.Release() and "the Postgres session that connection represents ends" are two different things, and conflating them is exactly the mistake here.
Suggested approach
Compare release to attempt's handling of a dead connection a few lines away -- that path deliberately does not call pg_advisory_unlock (correctly: a dead connection's session, and therefore its lock, is already gone from Postgres's perspective). release's job is the opposite case: the connection is alive and healthy, and we're intentionally choosing to give up the lock, which requires explicitly telling Postgres to release it.
Acceptance criteria
Verification
TEST_DATABASE_URL="postgresql://chronos:chronos@localhost:5432/chronos?sslmode=disable" go test -race ./tickets/tests/... -run TestTicket16 -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/leader-release-unlockFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/leader-release-unlockSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inhttp://localhost:8081app-1http://localhost:8082app-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.
Level 1
Fix a bug
Read existing behaviour, correct it.