refactor: govern background tasks and query ownership

This commit is contained in:
jxxghp
2026-08-23 13:24:04 +08:00
parent 43c173a0e7
commit f1e542bef0
37 changed files with 1570 additions and 510 deletions
+7 -3
View File
@@ -4,7 +4,7 @@ import json
import time
from typing import Protocol, Union, Any, List, Optional
from fastapi import BackgroundTasks, Depends, Request
from fastapi import Depends, Request
from starlette.responses import PlainTextResponse
from app.schemas.message import MessageClearBefore as _SchemaMessageClearBefore
@@ -32,6 +32,8 @@ from app.runtime.extensions.service_config import ServiceConfigHelper
from app.runtime.log import logger
from app.adapters.external.wechat_crypt import WXBizMsgCrypt
from app.schemas.types import NotificationChannel, SystemConfigKey
from app.api.context import get_background_task_registry, resolve_background_task_registry
from app.runtime.tasks import TaskRegistry
router = ResponseAPIRouter()
@@ -116,7 +118,7 @@ def start_message_chain(body: Any, form: Any, args: Any):
@router.post("/", summary="接收用户消息", response_model=_SchemaResponse[None])
async def user_message(
background_tasks: BackgroundTasks,
task_registry: Annotated[TaskRegistry, Depends(get_background_task_registry)],
request: Request,
_: _SchemaTokenPayload = Depends(verify_apitoken),
):
@@ -150,7 +152,9 @@ async def user_message(
list(form.keys()) if form else [],
image_markers,
)
background_tasks.add_task(start_message_chain, body, form, args)
resolve_background_task_registry(task_registry).create_sync(
start_message_chain, body, form, args, owner="api.message.user"
)
return _SchemaResponse(success=True)
+16 -3
View File
@@ -60,6 +60,8 @@ from app.adapters.system.plugin.package import PluginPackageManager
from app.application.database import DatabaseWorkerOverloadedError
from app.runtime.log import logger
from app.schemas.types import SystemConfigKey
from app.api.context import get_background_task_registry, resolve_background_task_registry
from app.runtime.tasks import TaskRegistry
router = ResponseAPIRouter()
_plugin_release_refresh_tasks: set[asyncio.Task] = set()
@@ -112,11 +114,17 @@ async def _refresh_plugin_release_versions(plugin_id: str, repo_url: str) -> Non
logger.warning(f"后台刷新插件 {plugin_id} Release 列表失败:{e}")
def _schedule_plugin_release_refresh(plugin_id: str, repo_url: str) -> None:
def _schedule_plugin_release_refresh(
plugin_id: str, repo_url: str, task_registry: TaskRegistry | None = None
) -> None:
"""
保留后台任务引用,避免任务被回收,同时让 helper 负责同仓库强刷合并。
"""
task = asyncio.create_task(_refresh_plugin_release_versions(plugin_id, repo_url))
registry = resolve_background_task_registry(task_registry)
task = registry.create(
_refresh_plugin_release_versions(plugin_id, repo_url),
owner="api.plugin.release_refresh",
)
_plugin_release_refresh_tasks.add(task)
def _discard_task(completed_task: asyncio.Task) -> None:
@@ -368,6 +376,7 @@ async def plugin_releases(
_: ApiPrincipal = Depends(get_current_active_superuser_async),
repo_url: Optional[str] = "",
force: bool = False,
task_registry: TaskRegistry = Depends(get_background_task_registry),
) -> dict:
"""
查询指定插件可直接安装的 GitHub Release 版本。
@@ -404,7 +413,11 @@ async def plugin_releases(
)
release_items = await plugin_helper.async_get_plugin_release_versions(plugin_id, repo_url)
if force and has_release_cache:
_schedule_plugin_release_refresh(plugin_id, repo_url)
_schedule_plugin_release_refresh(
plugin_id,
repo_url,
resolve_background_task_registry(task_registry),
)
items = []
for item in release_items:
version = item.get("version")
+7 -3
View File
@@ -1,7 +1,7 @@
from typing import List, Any, Dict, Optional
from fastapi import Depends, HTTPException
from starlette.background import BackgroundTasks
from typing import Annotated
from app.schemas.common import JsonObject as _SchemaJsonObject
from app.schemas.response import Response as _SchemaResponse
@@ -42,6 +42,8 @@ from app.runtime.log import logger
from app.application.scheduling import Scheduler
from app.schemas.types import SystemConfigKey, MediaType
from app.domain import site as site_rules
from app.api.context import get_background_task_registry, resolve_background_task_registry
from app.runtime.tasks import TaskRegistry
router = ResponseAPIRouter()
@@ -169,13 +171,15 @@ async def update_site(
@router.get("/cookiecloud", summary="CookieCloud同步", response_model=_SchemaResponse[None])
async def cookie_cloud_sync(
background_tasks: BackgroundTasks,
task_registry: Annotated[TaskRegistry, Depends(get_background_task_registry)],
_: ApiPrincipal = Depends(get_current_active_superuser_async),
) -> Any:
"""
运行CookieCloud同步站点信息
"""
background_tasks.add_task(Scheduler().start, job_id="cookiecloud")
resolve_background_task_registry(task_registry).create_sync(
Scheduler().start, job_id="cookiecloud", owner="api.site.cookiecloud_sync"
)
return _SchemaResponse(success=True, message="CookieCloud同步任务已启动!")
+11 -4
View File
@@ -1,7 +1,7 @@
from typing import List, Any, Annotated, Optional
import cn2an
from fastapi import Request, BackgroundTasks, Depends, HTTPException, Header
from fastapi import Request, Depends, HTTPException, Header
from app.schemas.common import IdData as _SchemaIdData
from app.schemas.response import Response as _SchemaResponse
@@ -12,6 +12,10 @@ from app.schemas.token import TokenPayload as _SchemaTokenPayload
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
from app.schemas.workflow import Subscribe as _SchemaSubscribe
from app.api.response import ResponseAPIRouter
from app.api.context import (
get_background_task_registry,
resolve_background_task_registry,
)
from app.chain.subscribe import SubscribeChain
from app.runtime.events import eventmanager
from app.domain.context import MediaInfo
@@ -52,6 +56,7 @@ from app.api.dependencies.subscription import (
)
from app.adapters.external.server import MoviePilotServerHelper
from app.application.scheduling import Scheduler
from app.runtime.tasks import TaskRegistry
from app.schemas.event import SubscribeModifiedEventData
from app.schemas.types import (
MUSIC_ENTITY_ALBUM,
@@ -489,7 +494,7 @@ async def delete_subscribe_by_media_identity(
)
async def seerr_subscribe(
request: Request,
background_tasks: BackgroundTasks,
task_registry: Annotated[TaskRegistry, Depends(get_background_task_registry)],
authorization: Annotated[str | None, Header()] = None,
) -> Any:
"""
@@ -521,7 +526,7 @@ async def seerr_subscribe(
user_name = req_json.get("request", {}).get("requestedBy_username")
# 添加订阅
if media_type == MediaType.MOVIE:
background_tasks.add_task(
resolve_background_task_registry(task_registry).create_sync(
start_subscribe_add,
mtype=media_type,
media_source=MediaSource.TMDB,
@@ -531,6 +536,7 @@ async def seerr_subscribe(
# 电影不传季号,避免被误判为剧集(S00)并污染通知标题
season=None,
username=user_name,
owner="api.subscribe.seerr",
)
else:
seasons = []
@@ -543,7 +549,7 @@ async def seerr_subscribe(
]
break
for season in seasons:
background_tasks.add_task(
resolve_background_task_registry(task_registry).create_sync(
start_subscribe_add,
mtype=media_type,
media_source=MediaSource.TMDB,
@@ -552,6 +558,7 @@ async def seerr_subscribe(
year="",
season=season,
username=user_name,
owner="api.subscribe.seerr",
)
return _SchemaResponse(success=True)
+11 -5
View File
@@ -1,11 +1,13 @@
from typing import Any, Annotated
from fastapi import BackgroundTasks, Request, Depends
from fastapi import Depends, Request
from app.schemas.response import Response as _SchemaResponse
from app.api.response import ResponseAPIRouter
from app.chain.webhook import WebhookChain
from app.adapters.web.security.access import verify_apitoken
from app.api.context import get_background_task_registry, resolve_background_task_registry
from app.runtime.tasks import TaskRegistry
router = ResponseAPIRouter()
@@ -19,7 +21,7 @@ def start_webhook_chain(body: Any, form: Any, args: Any):
@router.post("/", summary="Webhook消息响应", response_model=_SchemaResponse[None])
async def webhook_message(
background_tasks: BackgroundTasks,
task_registry: Annotated[TaskRegistry, Depends(get_background_task_registry)],
request: Request,
_: Annotated[str, Depends(verify_apitoken)],
) -> Any:
@@ -29,13 +31,15 @@ async def webhook_message(
body = await request.body()
form = await request.form()
args = request.query_params
background_tasks.add_task(start_webhook_chain, body, form, args)
resolve_background_task_registry(task_registry).create_sync(
start_webhook_chain, body, form, args, owner="api.webhook.message"
)
return _SchemaResponse(success=True)
@router.get("/", summary="Webhook消息响应", response_model=_SchemaResponse[None])
async def webhook_message_get(
background_tasks: BackgroundTasks,
task_registry: Annotated[TaskRegistry, Depends(get_background_task_registry)],
request: Request,
_: Annotated[str, Depends(verify_apitoken)],
) -> Any:
@@ -43,5 +47,7 @@ async def webhook_message_get(
Webhook响应,配置请求中需要添加参数:token=API_TOKEN&source=媒体服务器名
"""
args = request.query_params
background_tasks.add_task(start_webhook_chain, None, None, args)
resolve_background_task_registry(task_registry).create_sync(
start_webhook_chain, None, None, args, owner="api.webhook.message"
)
return _SchemaResponse(success=True)