Скрипт выполняется восемь минут, а запускается каждые пять. Второй экземпляр уже работает
Представим домашний сервер: каждые пять минут cron запускает Python-скрипт, который собирает данные. Обычно он справляется за минуту, но внешний API замедлился, и один запуск занял восемь минут.
В 12:00 скрипт начал работу. В 12:05 cron запустил ещё один экземпляр. Первый всё ещё пишет результат, второй читает и меняет те же файлы. Расписание соблюдено, но появилась параллельная работа, на которую скрипт не рассчитан. Для одной машины с локальной файловой системой можно использовать flock. Например, такая строка пользовательского crontab запускает задачу только при свободной блокировке:
*/5 * * * * /usr/bin/flock --verbose -n /opt/myjob/task.lock /usr/bin/python3 /opt/myjob/task.py >> /opt/myjob/task.log 2>&1
Пути нужно заменить своими. Каталог должен существовать, а пользователь cron — иметь права на создание lock-файла и запись журнала. Python-скрипт в этой схеме работает в переднем плане.
-n значит: если блокировка занята, не ждать освобождения. Поэтому запуск в 12:05 будет пропущен. Если первый закончит в 12:08, следующая попытка по расписанию состоится в 12:10. Все способы запуска, включая ручной, должны использовать ту же блокировку. Один прямой вызов python3 task.py обойдёт эту защиту.
Без -n команда по умолчанию ждёт блокировку. Это полезно не всегда: если работа постоянно занимает дольше интервала, ожидающие процессы начнут накапливаться. Для ограниченного ожидания есть -w, например -w 30 — ждать не больше 30 секунд.
Перед настройкой полезно решить, что делать с пропущенной работой. Если нужен только актуальный снимок данных, следующий запуск может заменить пропущенный. Если нужно обработать каждый отдельный период, понадобится учёт этих периодов и очередь заданий.
У systemd.timer по-другому: если тот же service-unit уже активен, таймер не создаёт второй его экземпляр. Но такое срабатывание само по себе не превращается в отдельную задачу очереди. Команда сервиса тоже должна оставаться под управлением systemd до завершения работы. В журнале стоит различать начало, успешное завершение, ошибку и пропуск из-за блокировки. Ещё полезнее проверять время последнего успешного результата: отсутствие второго процесса ничего не говорит о том, когда данные обновлялись в последний раз.
Блокировка решает проблему параллельного запуска. Дальше нужно определить судьбу пропущенной работы: достаточно ли обновить данные при следующем запуске или каждый период обязательно должен быть обработан. От этого и зависит выбор между пропуском, ожиданием и очередью.