Bounded FIFO inference scheduling: admission queue, OpenAI-shaped 429/503, queued-disconnect detection (#28)

This commit is contained in:
ZacharyZcR
2026-07-10 18:52:07 +08:00
committed by GitHub
parent 3cbc52ab00
commit 76e858a80f
4 changed files with 345 additions and 75 deletions
+140 -1
View File
@@ -5,7 +5,8 @@ import unittest
from urllib.error import HTTPError
from urllib.request import Request, urlopen
from openai_server import APIError, APIServer, END, generation_options, read_engine_turn, render_chat
from openai_server import (APIError, APIServer, ClientCancelled, END, GenerationScheduler,
generation_options, read_engine_turn, render_chat)
class FakeEngine:
@@ -19,6 +20,18 @@ class FakeEngine:
return {"prompt_tokens": 7, "completion_tokens": 2, "length_limited": False}
class BlockingEngine(FakeEngine):
def __init__(self):
super().__init__()
self.entered = threading.Event()
self.release = threading.Event()
def generate(self, prompt, maximum, temperature, top_p, on_text):
self.entered.set()
self.release.wait(2)
return super().generate(prompt, maximum, temperature, top_p, on_text)
class TemplateTest(unittest.TestCase):
def test_renders_text_subset_of_official_template(self):
prompt = render_chat([
@@ -66,6 +79,83 @@ class ProtocolTest(unittest.TestCase):
self.assertTrue(stats["length_limited"])
class SchedulerTest(unittest.TestCase):
def test_rejects_when_waiting_queue_is_full(self):
scheduler = GenerationScheduler(max_queue=0, queue_timeout=1)
with scheduler.admit():
with self.assertRaises(APIError) as caught:
with scheduler.admit():
pass
self.assertEqual(caught.exception.status, 429)
self.assertEqual(caught.exception.code, "queue_full")
self.assertEqual(scheduler.snapshot()["rejected"], 1)
def test_times_out_and_cancels_queued_requests(self):
scheduler = GenerationScheduler(max_queue=2, queue_timeout=0.02)
with scheduler.admit():
with self.assertRaises(APIError) as timed_out:
with scheduler.admit():
pass
with self.assertRaises(ClientCancelled):
with scheduler.admit(lambda: True):
pass
stats = scheduler.snapshot()
self.assertEqual(timed_out.exception.code, "queue_timeout")
self.assertEqual(stats["timed_out"], 1)
self.assertEqual(stats["cancelled"], 1)
def test_admits_waiters_in_fifo_order(self):
scheduler = GenerationScheduler(max_queue=2, queue_timeout=1)
entered = threading.Event()
release = threading.Event()
order = []
def run(name, block=False):
with scheduler.admit():
order.append(name)
if block:
entered.set()
release.wait(1)
first = threading.Thread(target=run, args=("first", True))
second = threading.Thread(target=run, args=("second",))
third = threading.Thread(target=run, args=("third",))
first.start(); entered.wait(1)
second.start()
for _ in range(100):
if scheduler.snapshot()["queued"] == 1: break
threading.Event().wait(0.005)
third.start()
for _ in range(100):
if scheduler.snapshot()["queued"] == 2: break
threading.Event().wait(0.005)
release.set()
first.join(1); second.join(1); third.join(1)
self.assertEqual(order, ["first", "second", "third"])
self.assertEqual(scheduler.snapshot()["completed"], 3)
def test_close_rejects_waiters(self):
scheduler = GenerationScheduler(max_queue=1, queue_timeout=1)
entered = threading.Event()
release = threading.Event()
errors = []
def active():
with scheduler.admit():
entered.set(); release.wait(1)
def waiting():
try:
with scheduler.admit(): pass
except APIError as error:
errors.append(error.code)
first = threading.Thread(target=active); first.start(); entered.wait(1)
second = threading.Thread(target=waiting); second.start()
scheduler.close(); release.set(); first.join(1); second.join(1)
self.assertEqual(errors, ["scheduler_closed"])
class HTTPTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
@@ -77,6 +167,7 @@ class HTTPTest(unittest.TestCase):
@classmethod
def tearDownClass(cls):
cls.server.scheduler.close()
cls.server.shutdown()
cls.server.server_close()
cls.thread.join(timeout=2)
@@ -96,6 +187,12 @@ class HTTPTest(unittest.TestCase):
self.request("/v1/models", key="wrong")
self.assertEqual(caught.exception.code, 401)
def test_health_reports_scheduler(self):
with self.request("/health") as response:
scheduler = json.load(response)["scheduler"]
self.assertEqual(scheduler["max_queue"], 8)
self.assertIn("queued", scheduler)
def test_browser_preflight(self):
request = Request(self.base + "/v1/chat/completions", method="OPTIONS", headers={
"Origin": "http://localhost:5173",
@@ -113,9 +210,11 @@ class HTTPTest(unittest.TestCase):
"max_tokens": 4,
}) as response:
body = json.load(response)
queue_wait = response.headers.get("x-colibri-queue-wait-ms")
self.assertEqual(body["object"], "chat.completion")
self.assertEqual(body["choices"][0]["message"]["content"], "Héllo")
self.assertEqual(body["usage"], {"prompt_tokens": 7, "completion_tokens": 2, "total_tokens": 9})
self.assertIsNotNone(queue_wait)
self.assertIn("<|user|>Hi<|assistant|><think></think>", self.engine.calls[-1][0])
def test_streaming_chat_completion(self):
@@ -148,5 +247,45 @@ class HTTPTest(unittest.TestCase):
self.assertEqual(caught.exception.code, 400)
class SchedulerHTTPTest(unittest.TestCase):
def setUp(self):
self.engine = BlockingEngine()
self.server = APIServer(("127.0.0.1", 0), self.engine, "test-model",
max_tokens=16, max_queue=0)
self.thread = threading.Thread(target=self.server.serve_forever, daemon=True)
self.thread.start()
self.url = f"http://127.0.0.1:{self.server.server_port}/v1/chat/completions"
def tearDown(self):
self.engine.release.set()
self.server.scheduler.close()
self.server.shutdown(); self.server.server_close(); self.thread.join(timeout=2)
def request(self):
body = json.dumps({"model": "test-model", "messages": [
{"role": "user", "content": "Hi"}]}).encode()
return urlopen(Request(self.url, data=body, headers={"Content-Type": "application/json"}), timeout=2)
def test_queue_full_returns_429_before_generation(self):
first_errors = []
def first_request():
try:
with self.request() as response: response.read()
except Exception as error:
first_errors.append(error)
first = threading.Thread(target=first_request); first.start()
self.assertTrue(self.engine.entered.wait(1))
with self.assertRaises(HTTPError) as caught:
self.request()
error = json.loads(caught.exception.read())["error"]
self.assertEqual(caught.exception.code, 429)
self.assertEqual(caught.exception.headers["Retry-After"], "1")
self.assertEqual(error["code"], "queue_full")
self.engine.release.set(); first.join(2)
self.assertEqual(first_errors, [])
if __name__ == "__main__":
unittest.main()