celery worker poll 频次机制调整| celery beat 设置poll检测任务| 拆镜复刻切片删除API | 交易流水时区BUG
This commit is contained in:
@@ -4,6 +4,7 @@ from celery import Celery
|
||||
from celery.signals import worker_process_init, worker_process_shutdown, worker_ready
|
||||
|
||||
from app.config import settings
|
||||
from app.enums.celery_queue import CeleryQueue, CeleryTaskName
|
||||
from app.models.base import engine
|
||||
from app.tasks.async_runner import close_loop, run_async
|
||||
|
||||
@@ -26,7 +27,7 @@ CELERY_TASK_IMPORTS = (
|
||||
)
|
||||
|
||||
|
||||
RECOVERY_QUEUE = settings.CELERY_RECOVERY_QUEUE or "gen_recovery"
|
||||
RECOVERY_QUEUE = settings.CELERY_RECOVERY_QUEUE or CeleryQueue.GEN_RECOVERY.value
|
||||
|
||||
|
||||
def _derive_redis_db(url: str, db_no: int) -> str:
|
||||
@@ -39,6 +40,21 @@ def _derive_redis_db(url: str, db_no: int) -> str:
|
||||
return url.rstrip("/") + f"/{db_no}"
|
||||
|
||||
|
||||
def _beat_schedule() -> dict:
|
||||
if not bool(getattr(settings, "POLL_DUE_DISPATCH_ENABLED", True)):
|
||||
return {}
|
||||
return {
|
||||
"dispatch-due-poll-tasks-every-minute": {
|
||||
"task": CeleryTaskName.DISPATCH_DUE_POLL.value,
|
||||
"schedule": max(1, int(settings.POLL_DUE_DISPATCH_INTERVAL_SECONDS or 60)),
|
||||
"options": {
|
||||
"queue": RECOVERY_QUEUE,
|
||||
"priority": settings.DOWNLOAD_TASK_PRIORITY_RECOVER,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
broker_url = settings.CELERY_BROKER_URL or (_derive_redis_db(settings.REDIS_URL, 1) if settings.REDIS_URL else "")
|
||||
backend_url = settings.CELERY_RESULT_BACKEND or (_derive_redis_db(settings.REDIS_URL, 2) if settings.REDIS_URL else "")
|
||||
|
||||
@@ -58,6 +74,7 @@ if broker_url:
|
||||
task_acks_late=True,
|
||||
task_reject_on_worker_lost=True,
|
||||
task_track_started=True,
|
||||
beat_schedule=_beat_schedule(),
|
||||
task_annotations={
|
||||
# 生成链路任务以数据库状态为准,不依赖 Celery result backend。
|
||||
# 这里忽略结果可避免任务误返回 ORM / 非 JSON 对象时触发结果序列化失败。
|
||||
@@ -80,24 +97,25 @@ if broker_url:
|
||||
"sep": ":",
|
||||
},
|
||||
task_routes={
|
||||
"generation.chatapi_create_generation_task": {"queue": "gen_chatapi_create"},
|
||||
"generation.poll_generation_task": {"queue": "gen_provider_poll"},
|
||||
"generation.download_generation_result_task": {"queue": "gen_result_download"},
|
||||
"hot_opening.start_image_prompt_optimize": {"queue": "gen_chatapi_create"},
|
||||
"hot_opening.start_video_prompt_optimize": {"queue": "gen_chatapi_create"},
|
||||
"shot_replicate.analyze_original_video": {"queue": "gen_chatapi_create"},
|
||||
"shot_replicate.analyze_custom_segment_video": {"queue": "gen_chatapi_create"},
|
||||
"shot_replicate.split_one_segment": {"queue": "gen_result_download"},
|
||||
"shot_replicate.start_image_prompt_optimize": {"queue": "gen_chatapi_create"},
|
||||
"shot_replicate.start_video_prompt_optimize": {"queue": "gen_chatapi_create"},
|
||||
CeleryTaskName.CHATAPI_CREATE.value: {"queue": CeleryQueue.GEN_CHATAPI_CREATE.value},
|
||||
CeleryTaskName.POLL_GENERATION.value: {"queue": CeleryQueue.GEN_PROVIDER_POLL.value},
|
||||
CeleryTaskName.DOWNLOAD_GENERATION_RESULT.value: {"queue": CeleryQueue.GEN_RESULT_DOWNLOAD.value},
|
||||
CeleryTaskName.DISPATCH_DUE_POLL.value: {"queue": RECOVERY_QUEUE},
|
||||
"hot_opening.start_image_prompt_optimize": {"queue": CeleryQueue.GEN_CHATAPI_CREATE.value},
|
||||
"hot_opening.start_video_prompt_optimize": {"queue": CeleryQueue.GEN_CHATAPI_CREATE.value},
|
||||
"shot_replicate.analyze_original_video": {"queue": CeleryQueue.GEN_CHATAPI_CREATE.value},
|
||||
"shot_replicate.analyze_custom_segment_video": {"queue": CeleryQueue.GEN_CHATAPI_CREATE.value},
|
||||
"shot_replicate.split_one_segment": {"queue": CeleryQueue.GEN_RESULT_DOWNLOAD.value},
|
||||
"shot_replicate.start_image_prompt_optimize": {"queue": CeleryQueue.GEN_CHATAPI_CREATE.value},
|
||||
"shot_replicate.start_video_prompt_optimize": {"queue": CeleryQueue.GEN_CHATAPI_CREATE.value},
|
||||
# 恢复扫描统一走独立队列,避免占用下载/轮询/创建业务 worker。
|
||||
"recovery.startup_recovery_once": {"queue": RECOVERY_QUEUE},
|
||||
"shot_replicate.recover_split_tasks_once": {"queue": RECOVERY_QUEUE},
|
||||
"generation.recover_download_tasks_once": {"queue": RECOVERY_QUEUE},
|
||||
"generation.recover_generation_tasks_once": {"queue": RECOVERY_QUEUE},
|
||||
"module_async.recover_module_async_tasks_once": {"queue": RECOVERY_QUEUE},
|
||||
"user_oauth.update_oauth_accounts": {"queue": "default"},
|
||||
"app.tasks.cleanup.*": {"queue": "default"},
|
||||
CeleryTaskName.STARTUP_RECOVERY.value: {"queue": RECOVERY_QUEUE},
|
||||
CeleryTaskName.SHOT_SPLIT_RECOVERY.value: {"queue": RECOVERY_QUEUE},
|
||||
CeleryTaskName.RECOVER_DOWNLOAD.value: {"queue": RECOVERY_QUEUE},
|
||||
CeleryTaskName.RECOVER_GENERATION.value: {"queue": RECOVERY_QUEUE},
|
||||
CeleryTaskName.MODULE_ASYNC_RECOVERY.value: {"queue": RECOVERY_QUEUE},
|
||||
"user_oauth.update_oauth_accounts": {"queue": CeleryQueue.DEFAULT.value},
|
||||
"app.tasks.cleanup.*": {"queue": CeleryQueue.DEFAULT.value},
|
||||
},
|
||||
)
|
||||
else:
|
||||
@@ -121,8 +139,8 @@ def on_worker_ready(sender=None, **kwargs):
|
||||
"""Celery worker 启动时做一次容灾恢复。
|
||||
|
||||
注意:
|
||||
- 不启用 Celery beat。
|
||||
- 启动容灾保留,但只投递一个 recovery.startup_recovery_once 协调任务。
|
||||
- 启动容灾只投递一个 recovery.startup_recovery_once 协调任务。
|
||||
- Celery Beat 只用于每分钟触发轻量 generation.dispatch_due_poll_tasks,不跑完整启动容灾。
|
||||
- 协调任务走独立 gen_recovery 队列,串行扫描并把真实业务任务投回原队列。
|
||||
- 所有 worker 都尝试抢 Redis 投递锁,只有抢到锁的 worker 投递恢复任务。
|
||||
"""
|
||||
@@ -182,4 +200,4 @@ def on_worker_process_shutdown(**kwargs):
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
close_loop()
|
||||
close_loop()
|
||||
|
||||
Reference in New Issue
Block a user