Перейти к содержанию

Расписания

Расписания выполняются в каждом воркере. Каждый наступивший запуск ставится в очередь один раз на весь кластер; отдельного процесса beat нет (akadze beat — заглушка).

from datetime import timedelta

@app.periodic("digest", cron="0 9 * * *", timezone="Europe/Berlin")
async def digest() -> None: ...

@app.periodic("ops.heartbeat", every=timedelta(seconds=30), overlap=False)
async def heartbeat() -> None: ...

@app.periodic принимает параметры задачи (queue, max_attempts, timeout, backoff) и ровно один из cron и every. cron — пять полей; алиас вроде @hourly раскрывается в них. Выражение, которое не разбирается или никогда не срабатывает (0 0 31 2 *), бросает AkadzeError при регистрации; так же — задача, которую нельзя вызвать без аргументов. Шестое поле читается как секунды, седьмое — как год; на них пишется предупреждение. Если расписание всё же падает на тике, воркер пишет это в лог один раз и продолжает выполнять остальные.

Из конфигурации

Расписания можно задавать в конфиге приложения, а не декораторами. Разберите YAML, TOML или JSON сами, превратите строки в готовые значения (текст cron или timedelta, а не строки вида «30s») и вызовите:

@app.task("ops.prune", queue="ops")
async def prune() -> None: ...

app.add_schedule("ops.prune", every=timedelta(seconds=30), overlap=False)
# или сразу несколько
app.add_schedules([{"name": "ops.prune", "cron": "0 * * * *"}])

Задача к этому моменту уже должна быть зарегистрирована.

Часовые пояса и переход на летнее время

Cron работает в UTC, если в расписании не указан пояс IANA (timezone="Europe/Moscow"). Выражение читается по местным часам этого пояса, переход на летнее время — как в классическом cron:

  • Часы переводят назад: запись с фиксированным часом срабатывает один раз (в первый проход); запись с * в поле часа срабатывает в оба прохода.
  • Часы переводят вперёд: время с фиксированным часом внутри пропуска срабатывает со сдвигом на величину пропуска (02:30 становится 03:30), так что ежедневная задача не теряется. Если сдвинутый запуск совпал с другим запуском того же расписания, выполняется один.

timezone — строка с именем IANA. Для расписаний every и неизвестных поясов она отклоняется.

Перекрытие

overlap=False придерживает запуск, пока предыдущее задание расписания ещё в queued или running. Каждый тик планировщика пробует последний наступивший запуск снова; он ставится в очередь, когда предыдущее задание завершится, если запуск не старше grace приложения (по умолчанию минута). Запуски не копятся: более новый заменяет придержанный, а придержанный дольше grace пропускается. Долгий запуск сдвигает следующий старт, а не проигрывает пропущенные потом.