An async job API whose GET endpoint long-polls for a real status change instead of dumb fixed-interval polling -- measured at 15-30x fewer requests, not the "100x" round number a proxy-safe wait cap can't actually deliver.
Nobody has started this yet — be first.
Completed jobs increment the "failed" metric, not "completed"
`jobs_finished_total{status=...}` is the metric anyone would reach for to answer "what fraction of transcodes are failing right now" -- the kind of number that gates a deploy, drives an alert threshold, or shows up on the first panel of an on-call dashboard. Right now, every single job that completes successfully increments the "failed" bucket instead of "completed". In a healthy system processing thousands of successful jobs a day, this metric would show a 100% failure rate. Best case, whoever's watching that dashboard loses trust in it and starts ignoring it -- so the day this service actually starts failing for real, nobody notices. Worst case, it pages someone at 2am for a "failure spike" that is, in fact, every job succeeding normally.
About this project
An async job API: kicks off a simulated multi-step "transcode," and holds the request open until the job's status or progress actually changes, or seconds elapse, whichever comes first -- long-polling instead of naive fixed-interval polling. Two independent stores back each job: Redis () holds the job document (status, progress, result, error) as a TTL'd JSON blob, and an in-process registry () holds one per in-flight job, bumped on every write so a waiter that hasn't started waiting yet can never miss a wakeup. Real backs cancellation (not a status flag the worker ignores), a job's Redis key gets a TTL the moment it reaches a terminal status (refreshed to a longer TTL while still running), and an optional signed HMAC-SHA256 webhook callback fires on completion. The project's own README measures its signature claim -- long-polling costs far fewer requests than naive 1-second polling -- for real, landing at roughly 15-30x fewer requests depending on how chatty a job's progress reporting is, not the "100x" round number a proxy-safety-capped can't actually deliver.
max_wait_seconds default exceeds the 30s proxy-safety ceiling it documents
`GET /jobs/{id}?wait=N` holds a request open for up to `N` seconds, capped server-side at `max_wait_seconds` so a long-poll response can never still be pending when a load balancer or reverse proxy decides the connection is dead and kills it. The comment above this field names GCP's 30-second backend timeout as the shortest common default the cap has to respect -- but the value itself has been bumped past 30. The first client sitting behind infrastructure with a 30s timeout whose long-poll request lands near the new, larger ceiling sees their connection killed by the proxy while the app still thinks it's legitimately waiting -- a request that should return a clean response instead just dies with a connection reset, with nothing in this service's own logs pointing at the real cause.
POST /jobsGET /jobs/{id}?wait=Nwaitapp/store.pyapp/registry.pyasyncio.Eventasyncio.Task.cancel()waitNine tickets are bugs deliberately injected into an otherwise-working codebase (a wrong Prometheus label on the success path, a min/max swap that breaks the wait cap in both directions at once, an inverted TTL selection, a webhook signature computed over bytes that were never actually sent, a swallowed asyncio.CancelledError, a registry race reintroduced by reusing instead of replacing an Event); six are enhancements that do not exist yet (a GET /jobs listing endpoint, an idempotency key that's accepted but ignored, a batch-submit endpoint, a retry endpoint, and a concurrency-cap capstone). Every ticket has its own dedicated, currently-failing pytest test, run individually by practice_tickets/run_tickets.sh so one hung/crashed ticket can't take down the whole run or hide another ticket's result -- and the README documents, with real captured output, exactly which four of these bugs also break the project's own tests/ suite and why the rest don't.
EventGET /jobspractice_tickets/run_tickets.shtests/practice_tickets/tickets/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.
How to run it locally, step by step.