Хочу рассказать о простом скрипте, позволяющем защититься от двойного запуска заданий cron.
Допустим, раз в минуту вы обновляете некий кеш, чтобы стремительно отдать его миллионам посетителей сайта. Все идет прекрасно, но ровно до тех пор, пока глубокой ночью не запустится еженедельный бэкап и ваш кеш сформируется не за 10 секунд, а за 70, и на 60-й секунде его настигнет еще один процесс формирования кеша.
Что будет в таком сценарии дальше — вопрос весьма интересный. Велика вероятность, что два процесса будут активно мешать друг другу (они ведь работают с одними и теми же объектами), и их общее время выполнения будет отнюдь не в два раза больше, чем обычно, а если и третий настигнет…
Описанная ситуация банальна до неприличия, хотя чаще встречается вырожденный вариант: нет никакого крона, простов полночьв момент истечения срока годности кеша каждый новый посетитель начинает толстый процесс и сервер превращается в тыкву.
Как разработчик, вы обязаны предусмотреть такой сценарий и защититься от него. Но еслиБуратино был тупой разработчики вашего ПО не позаботились об этом, спасаться придется самостоятельно.
Я использую простую и удобную утилиту lockrun. Принцип ее работы прост: для каждого процесса она создает файл и вешает на него lock. Как только процесс завершается, лок пропадает. Лок также пропадает в случае внезапной смерти процесса, и нет необходимости проверять pid на существование или делать другие телодвижения. Если процесс запускается повторно, а лок-файл еще не освободился, работа скрипта прерывается и выдается сообщение в STDERR.
Утилита написана на C, так что перед использованием ее придется скомпилировать на целевой машине. Итак, качаем, компилируем и кладем куда надо:
Если вы не root, последней строчкой придется пренебречь и либо указывать полный путь, либо изменить PATH.
Пример использования:
Собственно команда и параметры lockrun разделяются двумя минусами.
Принимаются следующие параметры:
--lockfile=/path/to/file
Обязательный параметр, задающий имя файла для лока. Если такого файла нет, он создастся автоматически. Разумеется, для каждого задания должен быть свой файл.
--maxtime=N
Время в секундах, отводящееся скрипту на «нормальную» работу. Если скрипт работал дольше, в STDERR будет выведено сообщение, которое cron может отправить вам на почту.
--wait
Если этот параметр указан, lockrun не отменит выполнение скрипта, а будет ждать, пока предыдущий процесс освободит лок.
--verbose
Как всегда, выдача более подробной информации о ходе процесса.
--quiet
Не выдавать сообщений об ошибках. Можно включить, если отказ в запуске задания не является серьезной проблемой.
Вот и все. Как видите, действительно просто и эффективно.
UPD: В комментариях мне сообщили, что есть также родное средство для Linux.
Разумеется, никто не утверждал, что описанная утилита — единственное решение.
Допустим, раз в минуту вы обновляете некий кеш, чтобы стремительно отдать его миллионам посетителей сайта. Все идет прекрасно, но ровно до тех пор, пока глубокой ночью не запустится еженедельный бэкап и ваш кеш сформируется не за 10 секунд, а за 70, и на 60-й секунде его настигнет еще один процесс формирования кеша.
Что будет в таком сценарии дальше — вопрос весьма интересный. Велика вероятность, что два процесса будут активно мешать друг другу (они ведь работают с одними и теми же объектами), и их общее время выполнения будет отнюдь не в два раза больше, чем обычно, а если и третий настигнет…
Описанная ситуация банальна до неприличия, хотя чаще встречается вырожденный вариант: нет никакого крона, просто
Как разработчик, вы обязаны предусмотреть такой сценарий и защититься от него. Но если
Я использую простую и удобную утилиту lockrun. Принцип ее работы прост: для каждого процесса она создает файл и вешает на него lock. Как только процесс завершается, лок пропадает. Лок также пропадает в случае внезапной смерти процесса, и нет необходимости проверять pid на существование или делать другие телодвижения. Если процесс запускается повторно, а лок-файл еще не освободился, работа скрипта прерывается и выдается сообщение в STDERR.
Утилита написана на C, так что перед использованием ее придется скомпилировать на целевой машине. Итак, качаем, компилируем и кладем куда надо:
$ wget unixwiz.net/tools/lockrun.c
$ gcc lockrun.c -o lockrun
$ sudo cp lockrun /usr/local/bin/
Если вы не root, последней строчкой придется пренебречь и либо указывать полный путь, либо изменить PATH.
Пример использования:
* * * * * /usr/local/bin/lockrun --lockfile=/tmp/megacache.lockrun -- /path/to/megacache/generator
Собственно команда и параметры lockrun разделяются двумя минусами.
Принимаются следующие параметры:
--lockfile=/path/to/file
Обязательный параметр, задающий имя файла для лока. Если такого файла нет, он создастся автоматически. Разумеется, для каждого задания должен быть свой файл.
--maxtime=N
Время в секундах, отводящееся скрипту на «нормальную» работу. Если скрипт работал дольше, в STDERR будет выведено сообщение, которое cron может отправить вам на почту.
--wait
Если этот параметр указан, lockrun не отменит выполнение скрипта, а будет ждать, пока предыдущий процесс освободит лок.
--verbose
Как всегда, выдача более подробной информации о ходе процесса.
--quiet
Не выдавать сообщений об ошибках. Можно включить, если отказ в запуске задания не является серьезной проблемой.
Вот и все. Как видите, действительно просто и эффективно.
UPD: В комментариях мне сообщили, что есть также родное средство для Linux.
Разумеется, никто не утверждал, что описанная утилита — единственное решение.