Первоначальная версия VidConf
This commit is contained in:
72
workers/tasks/dispatch.py
Normal file
72
workers/tasks/dispatch.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""Общий хелпер безопасной постановки Celery-задачи по имени.
|
||||
|
||||
Вынесен из `_send_summarize_task` (`workers.tasks.pipeline`) как общая
|
||||
защита от потери шага при недоступности Redis:
|
||||
временная недоступность брокера в момент `app.send_task` не должна ронять
|
||||
вызывающую задачу — предыдущий шаг (фразы/саммари) уже закоммичен, и
|
||||
`failed` из-за одного лишь сбоя постановки СЛЕДУЮЩЕЙ задачи стёр бы уже
|
||||
проделанную работу. Используется дважды: `workers.tasks.pipeline.run_pipeline`
|
||||
(постановка `summarize_session`) и `workers.tasks.summarize.
|
||||
summarize_session` (постановка `notify_session`) — оба вызывающих
|
||||
места сами решают, что делать при `False` (не роняются, статус пайплайна не
|
||||
откатывается и не проваливается; восстановление — соответствующая beat-задача
|
||||
`workers.tasks.maintenance`, уровень 2 защиты).
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from kombu.exceptions import ConnectionError as KombuConnectionError
|
||||
from kombu.exceptions import OperationalError as KombuOperationalError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SEND_TASK_MAX_ATTEMPTS = 3
|
||||
"""Число попыток поставить задачу в очередь при недоступности брокера."""
|
||||
|
||||
SEND_TASK_BACKOFF_S = 2.0
|
||||
"""Базовая пауза (сек) между попытками; растёт линейно с номером попытки."""
|
||||
|
||||
|
||||
async def send_task_with_retry(task_name: str, args: list[str]) -> bool:
|
||||
"""Поставить задачу `task_name` в очередь с несколькими попытками при сбое брокера.
|
||||
|
||||
`args` — позиционные аргументы задачи (как ожидает `Celery.send_task`).
|
||||
Возвращает `True`, если постановка удалась хотя бы с одной попытки;
|
||||
`False` — все попытки исчерпаны. Вызывающая сторона логирует контекст
|
||||
(какой сеанс/задача) и оставляет восстановление уровню 2 защиты — не
|
||||
откатывает и не проваливает уже проделанную работу из-за одного лишь
|
||||
сбоя постановки следующей задачи.
|
||||
"""
|
||||
# Отложенный импорт `app`: на верхнем уровне модуля это создало бы цикл
|
||||
# (`workers.celery_app` импортирует `workers.tasks.pipeline`/`summarize`,
|
||||
# а те — `send_task_with_retry` из этого модуля) — импорт откладывается
|
||||
# до первого вызова, когда `workers.celery_app` уже полностью загружен.
|
||||
from workers.celery_app import app
|
||||
|
||||
for attempt in range(1, SEND_TASK_MAX_ATTEMPTS + 1):
|
||||
try:
|
||||
app.send_task(task_name, args=args)
|
||||
except (KombuOperationalError, KombuConnectionError) as exc:
|
||||
if attempt < SEND_TASK_MAX_ATTEMPTS:
|
||||
logger.warning(
|
||||
"send_task_with_retry: сбой постановки %s (попытка %d/%d): %s "
|
||||
"— повтор через %.1fс",
|
||||
task_name,
|
||||
attempt,
|
||||
SEND_TASK_MAX_ATTEMPTS,
|
||||
exc,
|
||||
SEND_TASK_BACKOFF_S * attempt,
|
||||
)
|
||||
await asyncio.sleep(SEND_TASK_BACKOFF_S * attempt)
|
||||
continue
|
||||
logger.error(
|
||||
"send_task_with_retry: не удалось поставить %s за %d попыток (%s)",
|
||||
task_name,
|
||||
SEND_TASK_MAX_ATTEMPTS,
|
||||
exc,
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return False # недостижимо: цикл либо возвращает, либо продолжает до предела
|
||||
Reference in New Issue
Block a user