feat(gateway): 接口5排队插队优先级,两级队列均支持
- 全局槽换为 _PrioritySemaphore:priority 大者排到等待队列最前,同级 FIFO, 无等待者时才允许直接获取(收紧 asyncio.Semaphore 的 barging); 授予槽位瞬间请求被取消时归还槽位,防泄漏 - worker 池 Condition FIFO 等待改为显式优先级等待队列,release 唤醒队首; 被唤醒但 worker 被抢时按原序号重排;授予/取消竞态时转手唤醒下一位 - forward.py 新增 PRIORITY_PATHS(现仅接口5 /api/v1/hairline/generate) - 新增 4 个单测:两级队列的插队 + 同级 FIFO
This commit is contained in:
@@ -234,3 +234,118 @@ def test_no_primary_config_all_are_primary():
|
||||
])
|
||||
w = _run_acquire(workers)
|
||||
assert w.url == "http://w1:1"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 优先级插队(接口5):全局槽 + worker 池
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_PRIO_CFG = {
|
||||
"dispatch": {
|
||||
"queue_wait_seconds": 5, # 测试用:等待足够长,避免排队超时
|
||||
"max_global_concurrency": 1,
|
||||
"max_queue_wait_seconds": 5,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_global_slot_priority_jumps_queue():
|
||||
"""普通请求已排队时,优先级请求插队到最前,先获得槽位。"""
|
||||
pool.init_global_slot(_PRIO_CFG)
|
||||
order = []
|
||||
|
||||
async def run():
|
||||
assert await pool.acquire_global_slot(_PRIO_CFG) is True # 占住唯一槽
|
||||
|
||||
async def normal():
|
||||
if await pool.acquire_global_slot(_PRIO_CFG):
|
||||
order.append("normal")
|
||||
pool.release_global_slot()
|
||||
|
||||
async def prio():
|
||||
if await pool.acquire_global_slot(_PRIO_CFG, priority=1):
|
||||
order.append("prio")
|
||||
pool.release_global_slot()
|
||||
|
||||
t1 = asyncio.create_task(normal())
|
||||
await asyncio.sleep(0.05) # 确保 normal 先进入等待队列
|
||||
t2 = asyncio.create_task(prio())
|
||||
await asyncio.sleep(0.05) # prio 此时应已插队到队首
|
||||
pool.release_global_slot() # 释放槽位 → 应唤醒 prio 而非 normal
|
||||
await asyncio.wait_for(asyncio.gather(t1, t2), timeout=3)
|
||||
|
||||
asyncio.run(run())
|
||||
assert order == ["prio", "normal"], f"优先级请求应先获得槽位,实际顺序={order}"
|
||||
assert pool.get_pool_status()["global_busy"] == 0
|
||||
|
||||
|
||||
def test_global_slot_same_priority_fifo():
|
||||
"""同优先级仍按 FIFO,不插队。"""
|
||||
pool.init_global_slot(_PRIO_CFG)
|
||||
order = []
|
||||
|
||||
async def run():
|
||||
assert await pool.acquire_global_slot(_PRIO_CFG) is True
|
||||
|
||||
async def waiter(tag):
|
||||
if await pool.acquire_global_slot(_PRIO_CFG):
|
||||
order.append(tag)
|
||||
pool.release_global_slot()
|
||||
|
||||
tasks = []
|
||||
for tag in ("a", "b", "c"):
|
||||
tasks.append(asyncio.create_task(waiter(tag)))
|
||||
await asyncio.sleep(0.05) # 保证入队顺序
|
||||
pool.release_global_slot()
|
||||
await asyncio.wait_for(asyncio.gather(*tasks), timeout=3)
|
||||
|
||||
asyncio.run(run())
|
||||
assert order == ["a", "b", "c"], f"同优先级应保持 FIFO,实际顺序={order}"
|
||||
|
||||
|
||||
def _run_worker_prio_scenario(acquire_specs):
|
||||
"""构造「唯一 worker 忙 → 多个请求排队 → 释放」场景,返回获得顺序。
|
||||
|
||||
acquire_specs: [(tag, priority), ...],按此顺序发起请求。
|
||||
"""
|
||||
workers = _make_pool([
|
||||
("http://w1:1", True, True, True), # 唯一 worker,初始即忙
|
||||
])
|
||||
order = []
|
||||
|
||||
async def run():
|
||||
pool._pool_condition = asyncio.Condition()
|
||||
pool._worker_waiters.clear()
|
||||
orig = pool._workers
|
||||
pool._workers = workers
|
||||
try:
|
||||
async def waiter(tag, prio_):
|
||||
w = await pool.acquire_worker(_PRIO_CFG, priority=prio_)
|
||||
order.append(tag)
|
||||
await pool.release_worker(w)
|
||||
|
||||
tasks = []
|
||||
for tag, prio_ in acquire_specs:
|
||||
tasks.append(asyncio.create_task(waiter(tag, prio_)))
|
||||
await asyncio.sleep(0.05) # 保证入队顺序
|
||||
# 释放 worker → 唤醒优先级最高的等待者
|
||||
await pool.release_worker(workers["http://w1:1"])
|
||||
await asyncio.wait_for(asyncio.gather(*tasks), timeout=3)
|
||||
finally:
|
||||
pool._workers = orig
|
||||
pool._worker_waiters.clear()
|
||||
|
||||
asyncio.run(run())
|
||||
return order
|
||||
|
||||
|
||||
def test_worker_queue_priority_jumps_queue():
|
||||
"""普通请求已排队时,优先级请求插队到最前,先获得 worker。"""
|
||||
order = _run_worker_prio_scenario([("normal", 0), ("prio", 1)])
|
||||
assert order == ["prio", "normal"], f"优先级请求应先获得 worker,实际顺序={order}"
|
||||
|
||||
|
||||
def test_worker_queue_same_priority_fifo():
|
||||
"""同优先级仍按 FIFO,不插队。"""
|
||||
order = _run_worker_prio_scenario([("a", 0), ("b", 0), ("c", 0)])
|
||||
assert order == ["a", "b", "c"], f"同优先级应保持 FIFO,实际顺序={order}"
|
||||
|
||||
Reference in New Issue
Block a user