Nobody has started this yet — be first.
Business impact
Exponential backoff is the right default for most jobs, but it's a poor fit for some: a job hitting a downstream system with a known, fixed recovery time (e.g. "this dependency's health check flaps for about 30 seconds after a deploy, then it's fine") is better served by evenly-spaced retries than by the rapidly-widening gaps exponential backoff produces. Without a configurable strategy, every job owner is forced into one retry shape regardless of what actually matches their target's behavior -- leading some to work around it by setting an artificially high max_retries just to get enough total retry attempts within their expected recovery window, which then also changes when the job gets dead-lettered.
Problem
A backoff_strategy column was added to jobs (TEXT NOT NULL DEFAULT 'exponential'), and pkg/executor.NextAttemptFor(strategy, attemptCount) exists as an entry point, but only handles "exponential" (by delegating to the existing NextAttempt) and "" (empty, same default). Anything else panics with "TODO(ticket-E7): backoff strategy ... not implemented".
Current behavior
NextAttemptFor("linear", 3) panics instead of returning a computed future time.
Expected behavior
NextAttemptFor("linear", attemptCount) computes a linear backoff: a delay that scales roughly proportionally with attemptCount (a fixed base multiplied by the attempt number), rather than doubling every attempt.
Steps to reproduce
Why this matters
This is a small, self-contained addition to an existing dispatch function -- the hard part isn't the arithmetic, it's recognizing that this needs to compose with everything else already built around NextAttempt (jitter, the existing cap) without that becoming a required part of this ticket's scope.
Suggested approach
Look at NextAttempt right below NextAttemptFor for the existing pattern (compute a delay, apply jitter, add it to time.Now()). A linear strategy needs the same shape with a different delay formula -- pick a reasonable fixed base delay (e.g. 10 seconds) multiplied by attemptCount.
Acceptance criteria
Verification
go test ./tickets/tests/... -run TestTicket18 -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 feat/backoff-strategyFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/backoff-strategySubmit 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 2
Implement a feature
Extend the system within its own patterns.