Nobody has started this yet — be first.
Business impact
JobCreateRequest already accepts an idempotency_key field, and Settings.idempotency_ttl_seconds already exists as a config knob for how long a key should be remembered -- but nothing reads either of them. Two POST /jobs calls with the same idempotency_key today create two entirely independent jobs. This is arguably worse than the field not existing at all: a client that reads the field's presence in the API schema and builds retry logic around it gets silent data duplication instead of a clear "not supported" error.
Problem
Two POST /jobs calls carrying the identical idempotency_key create two independent jobs with different ids, instead of the second call returning the original job's id.
Current behavior
JobCreateRequest.idempotency_key and Settings.idempotency_ttl_seconds both already exist, but nothing in create_job() reads either of them, so two POST /jobs calls with the identical key create two independent jobs.
Expected behavior
A POST /jobs call carrying an idempotency_key that matches a still-"remembered" prior submission (within idempotency_ttl_seconds of that first submission) returns the original job_id instead of creating a new job. A call with no key, or an expired key, behaves as it does today.
Steps to reproduce
pytest practice_tickets/tests/test_ticket_08_idempotency_key_ignored.py -vPOST /jobs calls with the same idempotency_key (e.g. "checkout-order-9001") return two different job_ids.Why this matters
The textbook use case: a client's POST /jobs call times out from its own side (network blip, client-side timeout) before it ever sees the response, even though the server successfully created the job. Without idempotency, the client's only safe options are "assume it failed and resubmit" (risking a duplicate, possibly-expensive job) or "assume it succeeded and do nothing" (risking silently losing a job that never actually started). An idempotency key turns "resubmit if unsure" into the actually-safe default.
Suggested approach
The natural place to remember "this key maps to this job_id, for this long" is Redis, alongside the job document itself (app/store.py already owns that connection and TTL patterns) -- a small idempotency:{key} -> job_id mapping with its own TTL is the same shape as everything else this module already does. create_job() is where that lookup (and, on a miss, the write) needs to happen, before a new job is actually created.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_08_idempotency_key_ignored.py -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/idempotency-key-ignoredFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/idempotency-key-ignoredSubmit 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:8000localhost:6379curl localhost:8000/healthz{"status": "ok", "redis": true}Work the tickets in practice_tickets/tickets/ (ordered easy → hard); each names one dedicated pytest file in practice_tickets/tests/:
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
pytest practice_tickets/tests/test_ticket_01_completed_jobs_counted_as_failed.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
Each ticket test runs in its own pytest process with a 60s timeout, so one hung/crashed ticket can't take down the whole run or hide another ticket's result. Run pytest tests/ (the project's own suite, against fakeredis, hermetic and fast) after each fix to check for regressions -- the README documents exactly which fixes are expected to change that suite's results.
Level 2
Implement a feature
Extend the system within its own patterns.