Repository navigation
--batch: request priority (a free slot and the next prompt read go to the highest priority waiting) - #2109
--batch: request priority (a free slot and the next prompt read go to the highest priority waiting)#2109brocktice wants to merge 1 commit into
Conversation
…he next prompt read go to the highest; a long read gives way to a higher one) The field SGLang and vLLM read (an integer, higher first; absent = 0, the order as before). No running request is paused. Requests waiting for a slot never hold back the control lines, so a read that gave way (BYIELD) can always go on and free its slot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
167f7a2 to
90492a1
Compare
|
Rebased on New test: Tests: 🤖 Generated with Claude Code |
Why
We serve Qwen3.8-Flash-Next IQ3_S on two RTX PRO 6000 (one engine per card,
parallel: 4,--batch-mtp, all experts in VRAM) to several clients at once: an interactive coding agent, a log-alert summarizer that must answer within seconds, and an evaluation battery that sends thousands of requests a day.With first-come-first-served waiting, a burst of battery requests filled all 8 slots and the alert summarizer queued behind it, so its calls timed out and our monitoring paged us. More slots don't fix it, because a burst can always fill them. Under SGLang we used
--enable-priority-scheduling(a per-requestpriority), and we lost that when we moved to Strata.We've been running the same idea in a router in front of Strata for several days. The alert summarizer's slowest call over a full day was 4.6 s (1,210 calls) while the battery kept 8 requests in flight. This PR moves it into the server, where it can see the slots and the #656 give-way path.
What it does
"priority"in the request body is an integer, higher goes first. It's the same field SGLang and vLLM read, so gateways like LiteLLM already pass it through. If the field is absent or not an integer, it counts as 0.With
--batch:YIELDS_MAXstill caps how often a read gives way.With every request at 0, all checks reduce to the existing code paths.
Deadlock note: requests waiting for a slot never hold back the control lines. A read that gave way holds a slot and needs the lines to continue and free that slot, whatever its priority; otherwise long/long/short with mixed priorities would deadlock (the ENGINE_REVIEW finding 1 shape). A request that gave way and is letting others go first (
after_epoch) also doesn't outrank anyone until its epoch passes.Not covered: the single-request path without
--batch(the server'sfifolock). That lock is shared with the vision encoder and settings changes, so changing it seemed like a separate discussion.Tests
New tests in
serve/test_parallel.py, using the existing fake engine:test_a_free_slot_goes_to_the_highest_priority_waiting: both slots busy, a priority-0 request waits, then a priority-5 one arrives; the first free slot goes to the priority-5 request.test_a_long_read_gives_way_to_a_higher_priority: two prompts of the same length; the later, higher-priority one makes the first read give way.test_no_way_given_to_a_lower_priority: a short priority-0 prompt does not make a priority-5 read give way.Priority.test_request_priorityandtest_outranked_skips_a_request_letting_others_go_first(unit).All three service tests fail on
main(checked three times each) and pass with the change.serve/test_parallel.pypassed 5 runs in a row. The fullserve/suite passes excepttest_responses.NoLeakedSampler.test_server_close_ends_the_sampler, which also fails on unmodifiedmainin 2 of 3 runs here (a sampler thread from an earlier test is still shutting down).Real engine: v0.1.43 on one RTX PRO 6000 (Blackwell), Qwen3.8-Flash-Next IQ3_S,
parallel: 4,--batch-mtp. The same request mix was run with and without the field:No errors; every request finished.
docs/BATCHING.mdhas a short paragraph describing the behavior.Happy to change the shape: a config default per API key,
/metricswaiting counts per priority, or the non-batch path.🤖 Generated with Claude Code