Cron скрипт для работы в качестве очереди ИЛИ очереди для cron?

Держу пари, что кто-то уже решил эту проблему, и, возможно, я использую неправильные поисковые запросы для Google, чтобы сказать мне ответ, но вот моя ситуация.

У меня есть сценарий, который я хочу запустить, но я хочу, чтобы он запускался только по расписанию и только по одному за раз. (невозможно запустить скрипт одновременно)

Теперь липкая часть состоит в том, что, скажем, у меня есть таблица под названием «myhappyschedule», в которой есть нужные мне данные и запланированное время. Эта таблица может иметь несколько запланированных времен, даже в одно и то же время, каждый из которых будет запускать этот сценарий. Так что, по сути, мне нужна очередь каждый раз, когда запускается скрипт, и всем им нужно ждать каждого, прежде чем он завершится. (иногда выполнение скрипта может занять всего минуту, иногда много-много минут)

Я думаю о том, чтобы создать сценарий, который проверяет myhappyschedule каждые 5 минут и собирает те, которые запланированы, помещает их в очередь, где другой сценарий может выполнять каждое «задание» или вхождение в очереди по порядку. Что все это звучит беспорядочно.

Чтобы продлить это, я должен сказать, что я разрешаю пользователям планировать события в myhappyschedule, а не редактировать crontab.

Что с этим делать? Блокировки файлов и скрипты, вызывающие скрипты?

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
8
0
1 008
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете использовать команду at (1) внутри своего скрипта, чтобы запланировать его следующий запуск. Перед выходом он может проверить мое расписание на предмет следующего запуска. На самом деле вам вообще не нужен cron.

Ну .. это было то, о чем я не думал. Интересно, может ли запланированное время пройти, пока выполняется скрипт. Скажем, он выполняется в 8:00 утра, а сценарий выполняется в течение 10 минут и пропускает любое время, запланированное между временем начала и временем его завершения.

user30413 22.10.2008 20:49

Хорошая точка зрения. Вы можете манипулировать atq в любом процессе, редактирующем myhappyschedule. Мне придется подумать об этом за обедом.

Steven Huwig 22.10.2008 21:07

Я думаю, что если ваш скрипт проверяет myhappyschedule в конце своего выполнения и извлекает самое раннее запланированное время запуска из myhappyschedule, чтобы выяснить его следующее во время выполнения, он будет работать, как задумано. Ключ в том, чтобы взять самое раннее время в моем счастливом расписании.

Steven Huwig 22.10.2008 22:07
Ответ принят как подходящий

добавить столбец exec_status к myhappytable (возможно, также time_started и time_finished, см. псевдокод)

запускать следующий скрипт cron каждые x минут

псевдокод cron скрипта:

[create/check pid lock (optional, but see "A potential pitfall" below)]
get number of rows from myhappytable where (exec_status == executing_now)
if it is > 0, exit
begin loop
  get one row from myhappytable
    where (exec_status == not_yet_run) and (scheduled_time <= now)
    order by scheduled_time asc
  if no such row, exit
  set row exec_status to executing_now (maybe set time_started to now)
  execute whatever command the row contains
  set row exec_status to completed
  (maybe also store the command output/return as well, set time_finished to now)
end loop
[delete pid lock file (complementary to the starting pid lock check)]

Таким образом, сценарий сначала проверяет, не запущена ли ни одна из команд, а затем запускает первую команду, которая еще не запущена, до тех пор, пока в данный момент не останется больше команд для выполнения. Кроме того, вы можете увидеть, какая команда выполняется, запросив базу данных.

Потенциальная ловушка:, если сценарий cron убит, запланированная задача останется в состоянии «Execution_now». Вот для чего нужна блокировка pid в начале и в конце: чтобы увидеть, правильно ли завершился скрипт cron. псевдокод создания / проверки pidlock:

if exists pidlockfile then
  check if process id given in file exists
  if not exists then
    update myhappytable set exec_status = error_cronscript_died_while_executing_this   
      where exec_status == executing_now
    delete pidlockfile
  else (previous instance still running)
    exit
  endif
endif
create pidlockfile containing cron script process id

Этот псевдокод спас меня от ужасно неподдерживаемого пути в отношении очередей заданий. Большое спасибо Писквор.

bouvard 13.05.2009 21:05

Я столкнулся с этим вопросом, когда искал решение проблемы с очередями. Для пользы всех, кто ищет, вот мое решение.

Объедините это с cron, который запускает задания по расписанию (даже если они запланированы для одновременного запуска) и который также решает описанную вами проблему.

Проблема


  • Должен быть запущен не более одного экземпляра скрипта.
  • Мы хотим, чтобы запросы обрабатывались как можно быстрее.

т.е. Нам нужен конвейер к скрипту.

Решение:


Создайте конвейер для любого скрипта. Сделано с помощью небольшого сценария bash (ниже).

Скрипт можно назвать
./pipeline "<any command and arguments go here>"

Пример:

./pipeline sleep 10 &
./pipeline shabugabu &
./pipeline single_instance_script some arguments &
./pipeline single_instance_script some other_argumnts &
./pipeline "single_instance_script some yet_other_arguments > output.txt" &
..etc

Сценарий создает новый именованный канал для каждой команды. Таким образом, приведенное выше создаст именованные каналы: sleep, shabugabu и single_instance_script.

В этом случае первоначальный вызов запустит считыватель и запустит single_instance_script с some arguments в качестве аргументов. Как только вызов завершится, считыватель захватит следующий запрос из конвейера и выполнит с some other_arguments, завершит, захватит следующий и т. д.

Этот сценарий будет блокировать запрашивающие процессы, поэтому вызовите его как фоновое задание (и в конце) или как отдельный процесс с at (at now <<< "./pipeline some_script")

#!/bin/bash -Eue

# Using command name as the pipeline name
pipeline=$(basename $(expr "$1" : '\(^[^[:space:]]*\)')).pipe
is_reader=false

function _pipeline_cleanup {
        if $is_reader; then
                rm -f $pipeline
        fi
        rm -f $pipeline.lock

        exit
}
trap _pipeline_cleanup INT TERM EXIT

# Dispatch/initialization section, critical
lockfile $pipeline.lock
        if [[ -p $pipeline ]]
        then
                echo "$*" > $pipeline
                exit
        fi

        is_reader=true
        mkfifo $pipeline
        echo "$*" > $pipeline &
rm -f $pipeline.lock

# Reader section
while read command < $pipeline
do
        echo "$(date) - Executing $command"
        ($command) &> /dev/null
done

Другие вопросы по теме