Nobody has started this yet — be first.
Business impact
FirstFailedAt/LastFailedAt are exactly the two fields an on-call engineer glances at first when triaging a dead-lettered job -- "how long has this been broken?" With the fields swapped, that duration (LastFailedAt - FirstFailedAt) comes out negative, which is either confusing (if a human reads it) or silently wrong (if a dashboard/alert computes "age of oldest failure" from it and gets nonsense). Neither is catastrophic on its own, but it's exactly the kind of subtly-wrong observability data that erodes trust in a monitoring dashboard over time.
Problem
dlq.NewJob is supposed to record the timestamp of the earliest attempt as FirstFailedAt and the latest as LastFailedAt. It assigns them backwards.
Current behavior
Given a retry.Result whose attempts are in chronological order (as retry.DoWithResult always produces them), the resulting Job's FirstFailedAt is actually the timestamp of the last attempt, and LastFailedAt is the timestamp of the first attempt.
Expected behavior
Job.FirstFailedAt should equal the earliest attempt's timestamp; Job.LastFailedAt should equal the latest attempt's timestamp.
Steps to reproduce
result := retry.Result{Attempts: []retry.Attempt{ {Number: 1, Timestamp: t0}, {Number: 2, Timestamp: t0.Add(3 * time.Second)}, }} job := dlq.NewJob("id", "op", nil, result) fmt.Println(job.FirstFailedAt.Equal(t0)) // got: false, want: true
Why this matters
FirstFailedAt/LastFailedAt are part of the public Job struct exposed via GET /v1/dlq and GET /v1/dlq/{id} -- any dashboard or alerting rule built on top of this API inherits the swap.
Suggested approach
Look at NewJob in pkg/dlq/job.go: it derives first/last from result.Attempts[0] and result.Attempts[len(result.Attempts)-1]. One of those two indices is assigned to the wrong variable.
Acceptance criteria
Verification
go test ./test/ -run TestTask05 -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/dlq-job-timestamp-swapFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/dlq-job-timestamp-swapSubmit 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:8080POST /v1/chargesTo run against Redis (the multi-replica-safe backend) with zero local setup:
docker compose up --build # app + redis, hot-reloading on save
Work the tickets in tasks/ (ordered easy → hard); each names one Go test in test/:
make test-race # go test -race -count=1 ./...
go test ./test/ -run TestTask01 -v # a single ticket
Level 1
Fix a bug
Read existing behaviour, correct it.