Nobody has started this yet — be first.
Business impact
The whole design of this ingestion service rests on "a batch flushes when EITHER it fills up OR batch_max_wait_seconds elapses, whichever comes first." The time-based half of that guarantee currently only holds if events stop arriving entirely for the full wait window. If events keep trickling in — individually spaced further apart than they'd need to be to fill a batch, but closer together than batch_max_wait_seconds — the wait timer effectively never expires. In production this means any customer whose event rate is below "fills a batch every batch_max_wait_seconds" but above "goes fully silent" can have events sit in the queue far longer than the configured batch_max_wait_seconds promises — potentially indefinitely.
Problem
The whole design of this service rests on a batch flushing when it fills up OR batch_max_wait_seconds elapses, whichever comes first. The wait side of that guarantee only holds today if events stop arriving entirely for the full window — a steady trickle keeps pushing the deadline back out, so a batch below batch_max_size can sit unflushed far longer than batch_max_wait_seconds promises, potentially indefinitely.
Current behavior
_collect_batch recomputes deadline = loop.time() + settings.batch_max_wait_seconds on every loop iteration instead of once at the start of collecting the batch, so each new arrival resets the wait timer back to a full window.
Expected behavior
The wait deadline should be fixed once at the start of collecting a batch and must not be pushed back out by subsequent arrivals; a batch open for batch_max_wait_seconds must flush with whatever it has.
Steps to reproduce
pytest practice_tickets/tests/test_ticket_11_batch_timer_trickle.py -v — posts events one at a time, each arriving just under batch_max_wait_seconds after the last, well below batch_max_size; the batch never flushes even though the total elapsed time is many multiples of batch_max_wait_seconds.Why this matters
This is the classic difference between a "deadline" (a fixed point in time, set once) and a "debounce"/"idle timer" (reset on every event) — both are legitimate patterns, but this function's contract calls for the former. Recomputing the deadline every loop iteration silently turns a bounded-wait-time guarantee into an unbounded one under continuous-but-sparse traffic — exactly the traffic pattern most likely to occur outside of a synthetic load test.
Suggested approach
Look at where deadline is assigned inside the while loop in _collect_batch (app/flush.py). Compare it against how remaining is computed and used afterward. The batch's wait budget needs to be fixed relative to one point in time, not recalculated on every pass through the loop.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_11_batch_timer_trickle.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 fix/batch-timer-never-fires-under-trickleFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/batch-timer-never-fires-under-trickleSubmit 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 1
Fix a bug
Read existing behaviour, correct it.