Nobody has started this yet — be first.
Business impact
Shutdown currently sets a stop event and lets flush workers drain whatever's in the queue, then the process exits — but nothing stops a brand-new HTTP request from being accepted onto the queue at any point during that window, including after the flush workers have already finished draining and are about to exit. An event accepted at exactly that moment sits in the queue with nobody left to pull it out before the process terminates — a narrow but real data-loss window.
Problem
app.state.draining already exists and POST /events / POST /events/batch already check it and immediately return 503 when it's True — but nothing ever sets it to True. It stays False for the entire life of the process, including during the shutdown finally block.
Current behavior
The shutdown finally block in app/main.py's lifespan calls stop_event.set() and drains flush workers, but never assigns app.state.draining = True anywhere, before or after.
Expected behavior
app.state.draining becomes True as the very first action in the shutdown finally block, before stop_event.set() and before flush workers are told to drain — gate first, drain second.
Steps to reproduce
pytest practice_tickets/tests/test_ticket_18_draining_flag.py -v — exits the app's lifespan context (shutdown fully completes) and checks app.state.draining, which is still False.Why this matters
Right now, shutdown works like this: stop_event.set(), then flush workers switch into drain mode and empty out whatever's currently in the queue, then the process exits. But nothing stops a new HTTP request from calling queue.put() at any point during that window — this is exactly the kind of gap that "start refusing new work before you start winding down existing work" is supposed to close, and the ordering matters: gate first, drain second.
Suggested approach
Look at the finally: block in app/main.py's lifespan function — specifically its very first line, and what would need to happen there relative to stop_event.set(). The check on the request-handling side (app/routers/events.py) is already correct and doesn't need to change.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_18_draining_flag.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/shutdown-accepts-new-eventsFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/shutdown-accepts-new-eventsSubmit 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:5432curl localhost:8000/healthzcurl localhost:8000/readyzWork the tickets in practice_tickets/tickets/ (ordered easy → hard); each names one dedicated test in practice_tickets/tests/:
pytest practice_tickets/tests/test_ticket_01_persisted_count.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 20, clean pass/fail summary
TICKET-17 needs a real Postgres to run at all (it checks which column a value lands in, which no fake/in-memory DB backend can meaningfully verify) — set TEST_DATABASE_URL=postgresql://<you>@localhost/ingest_test before running it or the full suite; without it, that one test is skipped, not failed.
Level 2
Implement a feature
Extend the system within its own patterns.