Первоначальная версия VidConf

This commit is contained in:
2026-07-23 01:04:01 +03:00
commit 896455381a
335 changed files with 61527 additions and 0 deletions

72
workers/tasks/dispatch.py Normal file
View 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 # недостижимо: цикл либо возвращает, либо продолжает до предела