Периодический воркер application-операции
Реализуй тонкую долгоживущую задачу планирования, которая владеет собственным
циклом, периодически вызывает уже собранную application-операцию и не знает её
внутренних зависимостей.
Порядок работы
- Изучи требования к ответственности конкретного воркера.
- Определи режим расписания, единицы времени, интервалы, timeout, влияние
публичного результата и политику ошибок.
- Получи готовую application-операцию из composition root.
- Реализуй внутри задачи один stop-aware цикл без бизнес-логики.
- Добавь unit-тесты с управляемыми часами.
Если требования уже заданы, следуй им без дополнительного проектирования. Не придумывай расписание или политику повторов.
Граница ответственности
- Ответственность конкретного воркера определяется требованиями.
- Не объединяй независимые операции по умолчанию.
- Несколько операций допустимы только как одна связная ответственность с общими lifecycle, расписанием, масштабированием и политикой отказа.
- Разные интервалы, зависимости или правила масштабирования обычно означают разные воркеры.
- Ответственность воркера должна описываться одной короткой фразой.
- Публикатор, синхронизатор и очистка могут использовать один паттерн, но остаются разными конкретными воркерами.
- Каждая задача владеет только своим циклом. Не создавай общий цикл, который на
каждой итерации последовательно вызывает все задачи процесса.
- Runtime запускает долгоживущие задачи и наблюдает их завершение, но не планирует
отдельные итерации.
- В новом сервисе называть класс
<OperationName>Task, размещать его в
presentation/background/<operation_name>.py и предоставлять публичный
run(). Не использовать суффикс Worker для задачи общего runtime.
- Получать готовые operation, stop event, heartbeat/readiness и параметры
расписания через конструктор; не создавать внутри task composition root.
Application-операция
- Composition root собирает операцию со всеми портами и передаёт воркеру готовый callable.
- Воркер не создаёт Unit of Work, репозитории, адаптеры или use case внутри итерации.
- Воркер не знает, какие порты нужны операции.
- Воркер не интерпретирует предметные данные результата.
- Воркер может сопоставлять варианты публичного DTO-перечисления с заранее
заданными действиями планирования, readiness и наблюдаемости.
- Допустимы
None, DTO-перечисление или небольшой публичный результат с
техническими показателями, если это предусмотрено контрактом.
- Отсутствие работы является нормальным успешным результатом.
- Результат влияет на следующий момент запуска только при явно заданной таблице
вариантов; не выводи политику из имени или внутреннего смысла операции.
- Для каждого варианта результата отдельно определить влияние на readiness. Не
считать любой нормально возвращённый DTO техническим успехом автоматически:
результат, требующий повтора из-за невыполненной операции, может увеличивать
счётчик ошибок без выбрасывания исключения.
Расписание
- Поддерживай явно выбранный требованиями
fixed-delay, fixed-rate или
result-aware delay.
- Не выбирай режим по умолчанию, если он не определён.
- Не допускай одновременных запусков одной операции.
- При
fixed-delay отсчитывай интервал после завершения операции.
- При
fixed-rate используй монотонные часы и не накапливай пропущенные запуски.
- Startup delay и jitter добавляй только по требованиям.
- Все ожидания должны прерываться stop event.
- Retry backoff не подменяет рабочий интервал.
- При result-aware delay для каждого варианта публичного результата явно задай
немедленный запуск или stop-aware задержку. Ошибочный исход и timeout описывай
отдельно от успешных вариантов DTO.
- В именах конфигурационных полей всегда указывай единицу времени. Преобразуй её
в секунды только у вызова API
asyncio, не смешивая значения разных единиц.
Подробная семантика приведена в periodic-worker-design.md.
Timeout и отмена
- Timeout одной итерации задавай только по требованиям и храни отдельно от интервала и shutdown timeout.
- После timeout полностью заверши или отмени текущую операцию до следующего запуска.
- Не подавляй
CancelledError.
- При остановке дай текущей операции согласованное время на завершение; дальнейшая принудительная отмена принадлежит runtime.
- Не повторяй автоматически операцию, если результат внешнего побочного эффекта неизвестен.
Политика ошибок
Политика определяется публичным контрактом operation и требованиями:
- какие ожидаемые ошибки завершают только текущую итерацию;
- какие допускают повтор с отдельным backoff;
- какие являются фатальными для задачи;
- как обрабатывается timeout.
Если иное не задано, неожиданная ошибка должна завершать задачу, чтобы runtime сработал fail-fast. Не создавай скрытый бесконечный retry. Внутренние ошибки нижних слоёв не должны пересекать публичную границу application-операции.
Логирование
Применяй python-service-logging-writing и профиль конкретного процесса.
Периодическая task владеет итогом каждой выполненной итерации и переходами
readiness, которые вычисляет из результата operation.
- Создавай operation context только после получения идентификаторов текущей
работы и очищай его в
finally.
- Измеряй длительность итерации монотонными часами.
- Сопоставляй каждый публичный результат, ожидаемую ошибку и timeout с одним
согласованным событием, уровнем, outcome и следующей задержкой.
- Записывай неожиданный окончательный сбой один раз перед его пробросом в runtime;
runtime не должен повторять этот stack trace.
- Записывай переход readiness только при изменении состояния, не на каждой
итерации.
- Не логируй полный DTO результата, пустой цикл, каждое ожидание и progress
heartbeat, если профиль прямо этого не требует.
- Не логируй transport-детали исходящего адаптера как итог application-операции.
Progress heartbeat и readiness
- Если heartbeat означает завершение итерации, обновляй его после каждого
завершённого цикла согласно требованиям. Не подменяй его независимым таймером.
- Счётчик последовательных ошибок и переходы readiness храни внутри конкретной
задачи. Ожидаемый успешный вариант, включая отсутствие работы, не считай
ошибкой.
- Технические порты heartbeat и readiness получай готовыми из composition root;
не связывай задачу с HTTP, файлом или конкретной системой метрик.
Границы
В область скила входят:
- фабрика периодической task;
- fixed-delay и fixed-rate;
- stop-aware ожидание;
- отсутствие overlap;
- timeout итерации;
- политика ошибок и retry backoff;
- технические показатели результата;
- сопоставление DTO-результата с задержкой, progress heartbeat и readiness;
- unit-тесты расписания и вызова операции.
Не входят:
- lifecycle процесса, сигналы и управление ресурсами;
- NATS, HTTP, PostgreSQL и другие технологии;
- реализация application-операции и её портов;
- Unit of Work, outbox и репозитории;
- domain-логика.
Проверка результата
- Граница ответственности соответствует требованиям.
- Операция передаётся готовой извне.
- Режим расписания задан явно.
- Запуски не перекрываются, пропущенные fixed-rate интервалы не накапливаются.
- Остановка прерывает ожидание.
- Timeout и retry отделены от рабочего интервала.
- Неожиданные ошибки не скрываются.
- Итоги итераций, длительности и переходы readiness соответствуют logging-профилю
и имеют по одной записи.
- Unit-тесты покрывают оба расписания, остановку, отсутствие overlap, пропуски, no-work, ошибки, timeout и отмену.
- Для result-aware delay тесты покрывают каждый вариант DTO, progress heartbeat
и переходы readiness.
- Имя, размещение и публичный
run() соответствуют общему runtime-каркасу.
1---2name: python-periodic-application-worker-writing3description: Используй при реализации или правке периодической фоновой задачи на Python, которая владеет собственным циклом и вызывает заданную application-операцию по fixed-delay, fixed-rate или явно заданной задержке по результату: stop-aware ожидание, timeout, политика ошибок, progress heartbeat, readiness, логирование итогов итераций и отсутствие перекрывающихся запусков. Не применять для общего lifecycle процесса, NATS, HTTP, реализации application/domain-логики, портов и адаптеров.4---56# Периодический воркер application-операции78Реализуй тонкую долгоживущую задачу планирования, которая владеет собственным9циклом, периодически вызывает уже собранную application-операцию и не знает её10внутренних зависимостей.1112## Порядок работы13141. Изучи требования к ответственности конкретного воркера.152. Определи режим расписания, единицы времени, интервалы, timeout, влияние16 публичного результата и политику ошибок.173. Получи готовую application-операцию из composition root.184. Реализуй внутри задачи один stop-aware цикл без бизнес-логики.195. Добавь unit-тесты с управляемыми часами.2021Если требования уже заданы, следуй им без дополнительного проектирования. Не придумывай расписание или политику повторов.2223## Граница ответственности2425- Ответственность конкретного воркера определяется требованиями.26- Не объединяй независимые операции по умолчанию.27- Несколько операций допустимы только как одна связная ответственность с общими lifecycle, расписанием, масштабированием и политикой отказа.28- Разные интервалы, зависимости или правила масштабирования обычно означают разные воркеры.29- Ответственность воркера должна описываться одной короткой фразой.30- Публикатор, синхронизатор и очистка могут использовать один паттерн, но остаются разными конкретными воркерами.31- Каждая задача владеет только своим циклом. Не создавай общий цикл, который на32 каждой итерации последовательно вызывает все задачи процесса.33- Runtime запускает долгоживущие задачи и наблюдает их завершение, но не планирует34 отдельные итерации.35- В новом сервисе называть класс `<OperationName>Task`, размещать его в36 `presentation/background/<operation_name>.py` и предоставлять публичный37 `run()`. Не использовать суффикс `Worker` для задачи общего runtime.38- Получать готовые operation, stop event, heartbeat/readiness и параметры39 расписания через конструктор; не создавать внутри task composition root.4041## Application-операция4243- Composition root собирает операцию со всеми портами и передаёт воркеру готовый callable.44- Воркер не создаёт Unit of Work, репозитории, адаптеры или use case внутри итерации.45- Воркер не знает, какие порты нужны операции.46- Воркер не интерпретирует предметные данные результата.47- Воркер может сопоставлять варианты публичного DTO-перечисления с заранее48 заданными действиями планирования, readiness и наблюдаемости.49- Допустимы `None`, DTO-перечисление или небольшой публичный результат с50 техническими показателями, если это предусмотрено контрактом.51- Отсутствие работы является нормальным успешным результатом.52- Результат влияет на следующий момент запуска только при явно заданной таблице53 вариантов; не выводи политику из имени или внутреннего смысла операции.54- Для каждого варианта результата отдельно определить влияние на readiness. Не55 считать любой нормально возвращённый DTO техническим успехом автоматически:56 результат, требующий повтора из-за невыполненной операции, может увеличивать57 счётчик ошибок без выбрасывания исключения.5859## Расписание6061- Поддерживай явно выбранный требованиями `fixed-delay`, `fixed-rate` или62 result-aware delay.63- Не выбирай режим по умолчанию, если он не определён.64- Не допускай одновременных запусков одной операции.65- При `fixed-delay` отсчитывай интервал после завершения операции.66- При `fixed-rate` используй монотонные часы и не накапливай пропущенные запуски.67- Startup delay и jitter добавляй только по требованиям.68- Все ожидания должны прерываться stop event.69- Retry backoff не подменяет рабочий интервал.70- При result-aware delay для каждого варианта публичного результата явно задай71 немедленный запуск или stop-aware задержку. Ошибочный исход и timeout описывай72 отдельно от успешных вариантов DTO.73- В именах конфигурационных полей всегда указывай единицу времени. Преобразуй её74 в секунды только у вызова API `asyncio`, не смешивая значения разных единиц.7576Подробная семантика приведена в [periodic-worker-design.md](references/periodic-worker-design.md).7778## Timeout и отмена7980- Timeout одной итерации задавай только по требованиям и храни отдельно от интервала и shutdown timeout.81- После timeout полностью заверши или отмени текущую операцию до следующего запуска.82- Не подавляй `CancelledError`.83- При остановке дай текущей операции согласованное время на завершение; дальнейшая принудительная отмена принадлежит runtime.84- Не повторяй автоматически операцию, если результат внешнего побочного эффекта неизвестен.8586## Политика ошибок8788Политика определяется публичным контрактом operation и требованиями:8990- какие ожидаемые ошибки завершают только текущую итерацию;91- какие допускают повтор с отдельным backoff;92- какие являются фатальными для задачи;93- как обрабатывается timeout.9495Если иное не задано, неожиданная ошибка должна завершать задачу, чтобы runtime сработал fail-fast. Не создавай скрытый бесконечный retry. Внутренние ошибки нижних слоёв не должны пересекать публичную границу application-операции.9697## Логирование9899Применяй `python-service-logging-writing` и профиль конкретного процесса.100Периодическая task владеет итогом каждой выполненной итерации и переходами101readiness, которые вычисляет из результата operation.102103- Создавай operation context только после получения идентификаторов текущей104 работы и очищай его в `finally`.105- Измеряй длительность итерации монотонными часами.106- Сопоставляй каждый публичный результат, ожидаемую ошибку и timeout с одним107 согласованным событием, уровнем, outcome и следующей задержкой.108- Записывай неожиданный окончательный сбой один раз перед его пробросом в runtime;109 runtime не должен повторять этот stack trace.110- Записывай переход readiness только при изменении состояния, не на каждой111 итерации.112- Не логируй полный DTO результата, пустой цикл, каждое ожидание и progress113 heartbeat, если профиль прямо этого не требует.114- Не логируй transport-детали исходящего адаптера как итог application-операции.115116## Progress heartbeat и readiness117118- Если heartbeat означает завершение итерации, обновляй его после каждого119 завершённого цикла согласно требованиям. Не подменяй его независимым таймером.120- Счётчик последовательных ошибок и переходы readiness храни внутри конкретной121 задачи. Ожидаемый успешный вариант, включая отсутствие работы, не считай122 ошибкой.123- Технические порты heartbeat и readiness получай готовыми из composition root;124 не связывай задачу с HTTP, файлом или конкретной системой метрик.125126## Границы127128В область скила входят:129130- фабрика периодической task;131- fixed-delay и fixed-rate;132- stop-aware ожидание;133- отсутствие overlap;134- timeout итерации;135- политика ошибок и retry backoff;136- технические показатели результата;137- сопоставление DTO-результата с задержкой, progress heartbeat и readiness;138- unit-тесты расписания и вызова операции.139140Не входят:141142- lifecycle процесса, сигналы и управление ресурсами;143- NATS, HTTP, PostgreSQL и другие технологии;144- реализация application-операции и её портов;145- Unit of Work, outbox и репозитории;146- domain-логика.147148## Проверка результата149150- Граница ответственности соответствует требованиям.151- Операция передаётся готовой извне.152- Режим расписания задан явно.153- Запуски не перекрываются, пропущенные fixed-rate интервалы не накапливаются.154- Остановка прерывает ожидание.155- Timeout и retry отделены от рабочего интервала.156- Неожиданные ошибки не скрываются.157- Итоги итераций, длительности и переходы readiness соответствуют logging-профилю158 и имеют по одной записи.159- Unit-тесты покрывают оба расписания, остановку, отсутствие overlap, пропуски, no-work, ошибки, timeout и отмену.160- Для result-aware delay тесты покрывают каждый вариант DTO, progress heartbeat161 и переходы readiness.162- Имя, размещение и публичный `run()` соответствуют общему runtime-каркасу.