生产是 `gunicorn -w 2`,两个进程各自跑 lifespan,start_all() 会把同一个通道的 长轮询拉两遍:消息被其中一个消费、回复从另一个发出,游标还会互相覆盖。 改成 Postgres 会话级 advisory lock 做租约:抢到的进程收消息,另一个每 10 秒重试, 主进程一挂连接断、锁释放,备用进程立刻接上。页面上的"接收中"只认抢到租约的那个 (`manager.polling()`),不再把等锁的也算进去。 补测试 `test_only_one_process_holds_the_polling_lease`: 第一个抢到、第二个抢不到、第一个放手后第三个能接上。
This commit is contained in:
@@ -338,3 +338,19 @@ async def test_upstream_wait_status_is_normalized(monkeypatch: pytest.MonkeyPatc
|
||||
monkeypatch.setattr(wechat_ilink, "_post", fake_post)
|
||||
result = await wechat_ilink.qrcode_status("qr-x")
|
||||
assert result.status == "waiting"
|
||||
|
||||
|
||||
async def test_only_one_process_holds_the_polling_lease() -> None:
|
||||
"""生产是 gunicorn `-w 2`:同一个通道只能有一个进程在拉消息,另一个等着接班。"""
|
||||
channel_id = uuid.uuid4()
|
||||
|
||||
first = await wechat_bot.acquire_lease(channel_id)
|
||||
assert first is not None, "第一个进程该抢到"
|
||||
|
||||
second = await wechat_bot.acquire_lease(channel_id)
|
||||
assert second is None, "第二个进程不该同时抢到,否则两条轮询会互相覆盖游标"
|
||||
|
||||
await first.close() # 进程退出,连接断,锁释放
|
||||
third = await wechat_bot.acquire_lease(channel_id)
|
||||
assert third is not None, "前一个进程放手后,备用进程要能接上"
|
||||
await third.close()
|
||||
|
||||
Reference in New Issue
Block a user