¿Secuencia de comandos cron para actuar como una cola O una cola para cron?

StackOverflow https://stackoverflow.com/questions/226473

  •  03-07-2019
  •  | 
  •  

Pregunta

Apuesto a que alguien ya resolvió esto y tal vez estoy usando términos de búsqueda incorrectos para que google me diga la respuesta, pero aquí está mi situación.

Tengo un script que quiero ejecutar, pero quiero que se ejecute solo cuando esté programado y solo uno a la vez.(no se puede ejecutar el script simultáneamente)

Ahora la parte complicada es que tengo una tabla llamada "myhappyschedule" que tiene los datos que necesito y la hora programada.Esta tabla puede tener múltiples horarios programados incluso al mismo tiempo, cada uno ejecutaría este script.Básicamente, necesito una cola cada vez que se activa el script y todos deben esperar a que finalice cada uno de ellos.(a veces el script puede tardar solo un minuto en ejecutarse, a veces son muchos minutos)

Lo que estoy pensando en hacer es crear un script que verifique myhappyschedule cada 5 minutos y recopile los que están programados y los coloque en una cola donde otro script pueda ejecutar cada "trabajo" o ocurrencia en la cola en orden.Todo esto suena complicado.

Para alargar esto, debo decir que estoy permitiendo a los usuarios programar cosas en myhappyschedule y no editar crontab.

¿Qué se puede hacer con esto?¿Bloqueos de archivos y scripts que llaman scripts?

¿Fue útil?

Solución

agregar una columna exec_status a myhappytable (tal vez también time_started y time_finished, ver pseudocódigo)

ejecute el siguiente script cron cada x minutos

pseudocódigo del script 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)]

De esta manera, el script primero verifica si ninguno de los comandos se está ejecutando, luego ejecuta el primer comando que aún no se ha ejecutado, hasta que no haya más comandos para ejecutar en ese momento.Además, puede ver qué comando se está ejecutando consultando la base de datos.

Un peligro potencial: Si se cancela el script cron, una tarea programada permanecerá en estado "executing_now".Para eso está el bloqueo pid al principio y al final:para ver si el script cron terminó correctamente.pseudocódigo de crear/comprobar 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

Otros consejos

Puede usar el comando at (1) dentro de su script para programar su próxima ejecución. Antes de salir, puede verificar myhappyschedule para el próximo tiempo de ejecución. No necesitas cron en absoluto, realmente.

Me encontré con esta pregunta mientras buscaba una solución al problema de las colas.Para beneficio de cualquiera que busque aquí, está mi solución.

Combine esto con un cron que inicie los trabajos tal como están programados (incluso si están programados para ejecutarse al mismo tiempo) y eso también resuelve el problema que describió.

Problema


  • Como máximo debería estar ejecutándose una instancia del script.
  • Queremos preparar las solicitudes para procesarlas lo más rápido posible.

es decir.Necesitamos un canal hacia el guión.

Solución:


Cree una canalización para cualquier script.Hecho usando un pequeño script bash (más abajo).

El script se puede llamar como
./pipeline "<any command and arguments go here>"

Ejemplo:

./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

El guión crea un nuevo tubería con nombre para cada comando.Entonces lo anterior creará canalizaciones con nombre: sleep, shabugabu, y single_instance_script

En este caso, la llamada inicial iniciará un lector y ejecutará single_instance_script con some arguments como argumentos.Una vez que se completa la llamada, el lector tomará la siguiente solicitud del canal y la ejecutará con some other_arguments, completar, tomar el siguiente, etc.

Este script bloqueará los procesos solicitantes, así que llámelo como un trabajo en segundo plano (& al final) o como un proceso independiente con 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top