This commit is contained in:
Your Name
2026-07-30 10:06:53 +08:00
parent 8f68af1c2c
commit 3fc94c4a89
9 changed files with 275 additions and 10 deletions
+34
View File
@@ -495,6 +495,22 @@ async def _conversations_from_logs(
return list(seen.values())
async def _release_db_connection(db: AsyncSession) -> None:
"""Check this session's pooled connection back in before a long await.
The whole backend shares a handful of pooled connections (five by default
on SQLite). A session left open across credential validation or worker
initialization holds one of them for tens of seconds per account, so every
unrelated request then waits out ``pool_timeout`` and the panel looks
frozen during a bulk start. Committing ends the transaction and returns
the connection; ``expire_on_commit=False`` keeps loaded attributes usable.
"""
try:
await db.commit()
except Exception:
logger.debug("Releasing the database connection failed", exc_info=True)
async def _reset_account_credentials(account_id: int, db: AsyncSession) -> Account:
"""清除账号 Cookie、IM 会话等登录数据,并停止托管。"""
await manager.stop_worker(account_id)
@@ -1931,6 +1947,7 @@ async def update_account_cookie(
# are committed so no worker can start in the stop/commit gap.
async with manager.preparation_lock(account_id):
account = await get_owned_account(db, user, account_id, write=True)
await _release_db_connection(db)
await batch_start_queue.cancel_account(account_id)
await manager.stop_worker(account_id)
@@ -1970,6 +1987,7 @@ async def delete_account_cookie(
# before the cleared credentials are committed.
async with manager.preparation_lock(account_id):
account = await get_owned_account(db, user, account_id, write=True)
await _release_db_connection(db)
await batch_start_queue.cancel_account(account_id)
await manager.stop_worker(account_id)
@@ -2033,6 +2051,7 @@ async def delete_account(
):
await get_owned_account(db, user, account_id, write=True)
# 停止运行中的任务
await _release_db_connection(db)
await batch_start_queue.cancel_account(account_id)
await manager.stop_worker(account_id)
clear_cookie_file(account_id)
@@ -2066,6 +2085,7 @@ async def reset_account_credentials(
user: User = Depends(require_write),
):
await get_owned_account(db, user, account_id, write=True)
await _release_db_connection(db)
await batch_start_queue.cancel_account(account_id)
account = await _reset_account_credentials(account_id, db)
return {
@@ -2087,6 +2107,9 @@ async def _start_account_rpa_impl(
return {"status": "running", "message": "RPA worker is already running."}
cookie_data = _get_account_cookie_data(account)
# Credential assessment issues real network requests to Douyin, and a bulk
# start runs it for every queued account. Release the connection first.
await _release_db_connection(db)
assessment = await assess_account_credential(
cookie_data,
account.im_session_data,
@@ -2136,6 +2159,10 @@ async def _start_account_rpa_impl(
account.qr_code_base64 = None
account.error_message = None
await db.commit()
else:
# Nothing to persist, but the reads above may still hold a pooled
# connection, and waiting for readiness below takes seconds.
await _release_db_connection(db)
try:
started = await manager.start_worker(
@@ -2213,6 +2240,10 @@ async def start_account_rpa(
user: User = Depends(require_write),
):
account = await get_owned_account(db, user, account_id, write=True)
# Cancelling waits for an in-flight queued start, and the preparation lock
# waits for whichever start owns this account. Neither may keep a pooled
# connection checked out while it waits.
await _release_db_connection(db)
await batch_start_queue.cancel_account(account_id)
async with manager.preparation_lock(account_id):
return await _start_account_rpa_impl(account, db, body.login_mode)
@@ -2290,6 +2321,9 @@ async def stop_account_rpa(
):
account = await get_owned_account(db, user, account_id, write=True)
# Cancelling drains an in-flight queued start, which can take as long as
# the batch per-account deadline. Do not hold a pooled connection for it.
await _release_db_connection(db)
await batch_start_queue.cancel_account(account_id)
stopped = await manager.stop_worker(account_id)
# 强制将数据库中的状态重置为 offline