Files
vidconf/workers/tasks/dispatch.py

73 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Общий хелпер безопасной постановки 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 # недостижимо: цикл либо возвращает, либо продолжает до предела