上一版用 `manager.polling()` 判"有没有人在收消息",那是进程内状态:请求落到持有者 回答 true、落到备用进程回答 false,页面上就会每两秒闪一次"已停止"。 改成查数据库里的 advisory lock(`pg_locks`,objsubid=2 的两段式锁),谁抢到租约所有 进程都看得见。`lease_held()` 自己开一条短会话查,不复用请求会话——接口里常在 `await session.commit()` 之后才拼返回值,那时原事务已经关了。
This commit is contained in:
@@ -54,6 +54,27 @@ def site_base() -> str:
|
||||
return ""
|
||||
|
||||
|
||||
async def lease_held(channel_id: uuid.UUID) -> bool:
|
||||
"""这个通道现在有没有进程在收消息。
|
||||
|
||||
不能看 `manager.polling()`:生产是 gunicorn 多进程,每个进程只知道自己那把锁,
|
||||
请求落到哪个进程就回哪个答案,页面会在"接收中"和"已停止"之间闪。
|
||||
advisory lock 是数据库级的,`pg_locks` 里看得见——谁抢到了,所有进程都认。
|
||||
"""
|
||||
async with SessionFactory() as session:
|
||||
result = await session.execute(
|
||||
sa_text(
|
||||
"SELECT 1 FROM pg_locks"
|
||||
" WHERE locktype = 'advisory' AND granted AND objsubid = 2"
|
||||
" AND classid::bigint = (hashtext(:ns)::bigint & 4294967295)"
|
||||
" AND objid::bigint = (hashtext(:cid)::bigint & 4294967295)"
|
||||
" LIMIT 1"
|
||||
),
|
||||
{"ns": LEASE_NAMESPACE, "cid": str(channel_id)},
|
||||
)
|
||||
return result.first() is not None
|
||||
|
||||
|
||||
async def active_channel_ids() -> list[uuid.UUID]:
|
||||
"""列出"已扫码登录、该收消息"的通道。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user