Как не дать скрипту выполняться, если другая его копия уже запущена?
Я написал скрипт на Python, который ищет в определенной папке CSV файлы и если находит их, то производит над данными некоторые преобразования, а затем загружает их в MySQL, откуда я уже могу забирать данные в BI и работать с ними.
Сейчас я хочу немного автоматизировать процесс и доработав скрипт добавить его в crontab, чтобы просто закинув файлы в нужную папку самостоятельно (или каким то еще скриптом) я был уверен что все обработается нормально и данные попадут в БД.
Но, я не очень представляю себе как именно Python и Linux работают с файлами. Файлы могут быть очень большими и скрипт может выполняться вплоть до десятков минут. При этот ставить в crontab выполнение раз в несколько часов не хотелось бы.
Я планирую сделать такую логику скрипта:
1. Смотрим сколько файлов в папке и берем первый.
2. Разбиваем его на части и забираем в датафрейм панд первую часть.
3. Удаляем эту часть из файла (если строки кончились, удаляем сам файл).
4. Производим нужные манипуляции с данными и отправляем их БД.
5. exit()
И соответственно ставим все это чудо в crontab запускаться каждую минуту.
Вопрос в том будет ли все это нормально работать или нужно делать как-то по другому? Возможны ли при такой логике следующие ситуации и как их лучше избежать:
1. Crontab запустил первую копию скрипта, она выполняется, и в момент записи обновленной версии файла, из которого убрали ту часть данных, с которой сейчас работаем, crontab запускает вторую копию скрипта, которая заберет неполный файл?
2. По каким-то причинам сервер будет загружен и скрипт не будет успевать за минуту обработать ожидаемый объем, в результате я через пару часов получу около сотни одновременно работающих копий которые все повесят?
Если существует файл /tmp/ваш_скрипт.lock - завершить работу.
Создать файл /tmp/ваш_скрипт.lock
Выполнить работу.
Удалить файл /tmp/ваш_скрипт.lock
Подвох: если ваш скрипт упал, не дойдя до последнего пункта - больше он не запустится. Так что на п. 1 стоит предусмотреть - "если файл существует и моложе 10 минут", например.
Шаг 1 избыточен, т.к. между шаг 1 и 2 может вклиниться другая запущенная копия и создать файл раньше. Если уж использовать лок файлы, то нужно его создавать в эксклюзивном блокируемом режиме.
Adamos, почему не может? Как часто я это слышу от разработчиков и так же часто это "не может" выстреливает им в ногу. Например, сервер тупит и планировщик ОС не дает процессу ЦПУ или диск. Или админы выкрутили nice процесса в дно и ему планировщик ресурсов не дает.
В общем это вопрос возможности. Это возможно. А что возможно и бывает и стреляет. Шанс не очень высокий да, но нужно понимать, что он есть.
Алексей Сундуков, вообще-то в случае жесткого тупняка второму запущенному скрипту довольно сложно будет обогнать аналогичный первый. Учитывая, что ему сначала нужно выполнить те же самые действия.
Adamos, если рассматривать процесс как линейный и последовательный то да. Но в реальной ОС общего назначения (и даже в RTOS) все происходит не линейно и не последовательно. Что порождает "магические" сложно отлавливаемые баги в ПО.
Алексей Сундуков, я понимаю ваш подход "программируй любой хелловорд так, как будто он будет управлять часами судного дня" и он мне даже импонирует. Так что оспаривать его не буду. Применять, впрочем, тоже ;)
Adamos, ничуть не судного. Затраты через реализацию lock файла, PID и мьютекса на семафорах примерно равнозначны. Но последний работает быстрее (ввиду отсутствия дисковых операций) и в пространстве ядра. Так почему бы его не использовать?
Adamos, практика показывает, что джунам все варианты равнозначны, т.к. изначально они даже не понимают проблемы "запустилась вторая копия программы, пошла гонка за общие ресурсы". Поэтому на мой взгляд важно сразу показать более быстрый и устойчивый вариант.
При запуске проверять наличие pid-файла в /var/run. Если файл есть сразу завершать работу. Если нет, то создавать, регистрировать atexit-обработчик для его удаления и выполнять основную работу.
А вас не смущает, что между фазами "проверить наличие файла" и "создать файл" проходит некоторое время в течении которого другой процесс может создать такой файл раньше?
Adamos, засегфолтить питона надо очень постараться :) Ну, и я не знаю ни одного сервиса, который умел бы не оставлять в таком случае pid-файл, блокирующий повторный запуск. Так что это неприятная, но нормальная ситуация.
А вас не смущает, что между фазами "проверить наличие файла" и "создать файл" проходит некоторое время в течении которого другой процесс может создать такой файл раньше?
Странно, 90% линукс сервисов десятилетиями живут именно по этой схеме - создают файл и проверяют его наличие. Что же вас смущает?
Алексей Сундуков, повторюсь, на сколько мне известно, не один сервис с такой проблемой не справляется. PID-файл остаётся при падении PostgreSQL, MySQL, Apache HTTPd, Nginx и многих других.
Что есть вариация других опубликованных здесь ответов, но зато при сбое по какой-либо причине скрипт всё-таки будет запущен в следующий раз и не будет ждать пока вы сами удалите оставшийся lock файл.
Стандартное решение в Линукс - это создание PIDFILE с номером выполняющегося процесса.
При запуске скрипта, проверяется файл, проверяется что процесс который в нем указан запущен. Если запущен - значит завершить работу, чтобы не мешать уже выполняющемуся.
Если не запущен - первым делом создать PIDFILE.
В конце скрипта удалить за собой PIDFILE.
Можно поискать в питоне готовую библиотеку, посмотреть как с ней работать
import pidfile
У решения есть несколько проблем который нужно понимать, что бы использовать этот метод. PID имеет ограничение по размеру поэтому ОС его переиспользует. И возможна ситуация, когда ваш скрипт умер, в PID файл записался ID, но к моменту перезапуска вашего скрипта этот ID операционкой был выдан другому процессу. В самом плохом варианте демону (допустим nginx). Тогда работа вашего ПО заблочиться до тех пор, пока демон не умрет/перезапуститься. Если конечно вы про эту ситуацию заранее не подумайне и не заложите в код.
Может возникнуть вопрос, каков шанс такой ситуации? Сильно вероятный когда на сервере очень часто останавливаются и запускаются программы и скрипт часто крашиться без удаления PID файла.
Алексей Сундуков, Шанс никакой, потому что при ПРАВИЛЬНОМ использовании PID вы можете проверить cmd процесса, и увидеть что там запущено. Поэтому и рекомендуется пользоваться готовой библиотекой питона, где это предусмотрено.
P.S. Даже без этого, можно примерно посмотреть ситуацию с процессом. насколько для него критично получить шанс в 1/65534*кол-во процессов запускающихся в течение дня