Solusi umum untuk mencegah cron job lama berjalan paralel?

27

Saya mencari solusi sederhana dan generik yang memungkinkan Anda untuk mengeksekusi skrip atau aplikasi apa pun di crontab dan mencegahnya berjalan dua kali.

Solusinya harus independen pada perintah yang dijalankan.

Saya menganggap itu akan terlihat seperti di lock && (command ; unlock)mana kunci akan kembali palsu jika ada kunci lain.

Bagian kedua akan seperti jika memperoleh kunci, menjalankan perintah dan membuka kunci setelah perintah dijalankan, bahkan jika itu mengembalikan kesalahan.

Sorin
sumber

Jawaban:

33

Lihatlah paket run-oneInstal run-one . Dari halaman manual untuk run-oneperintahIkon halaman manual :

run-one adalah skrip wrapper yang menjalankan tidak lebih dari satu instance unik dari beberapa perintah dengan serangkaian argumen unik.

Ini sering berguna dengan cronjobs, ketika Anda ingin tidak lebih dari satu salinan dijalankan pada satu waktu.

Suka timeatau sudo, Anda hanya perlu menambahkannya ke perintah. Jadi cronjob bisa terlihat seperti:

  */60 * * * *   run-one rsync -azP $HOME example.com:/srv/backup

Untuk informasi dan latar belakang lebih lanjut, lihat posting blog yang memperkenalkannya oleh Dustin Kirkland.

andrewsomething
sumber
5

Cara yang sangat sederhana untuk menyelesaikan kunci:

if mkdir /var/lock/mylock; then
  echo "Locking succeeded" >&2
else
  echo "Lock failed - exit" >&2
  exit 1
fi

Sebuah skrip yang ingin dijalankan perlu membuat kunci. Jika kunci ada, skrip lain sedang sibuk, sehingga skrip pertama tidak dapat berjalan. Jika file tidak ada, tidak ada skrip yang mendapatkan kunci. Jadi skrip saat ini mendapatkan kunci. Setelah skrip selesai, kunci harus di-realeased dengan menghapus kunci.

Untuk informasi lebih lanjut tentang kunci bash, periksa halaman ini

OrangeTux
sumber
1
Anda juga akan menginginkan perangkap EXIT yang menghilangkan kunci saat keluar. echo "Locking succeeded" >&2; trap 'rm -rf /var/lock/mylock' EXIT
geirha
1
Idealnya Anda ingin menggunakan kawanan penasihat dari proses yang menjalankan perintah yang Anda inginkan sebagai subtugas. Dengan cara itu jika mereka semua mati kawanan dilepaskan secara otomatis, yang menggunakan kehadiran dari file kunci tidak melakukan. Menggunakan port jaringan akan bekerja dengan cara yang sama - meskipun itu adalah cara namespace yang lebih kecil, yang merupakan masalah.
Alex North-Keys
3

Tidak perlu menginstal beberapa paket mewah:

#!/bin/bash
pgrep -xf "$*" > /dev/null || "$@"

Lebih cepat menulis skrip itu sendiri daripada menjalankan "apt-get install", bukan? Anda mungkin ingin menambahkan "-u $ (id -u)" ke pgrep untuk memeriksa instance yang dijalankan oleh pengguna saat ini saja.

Michael Kowhan
sumber
2
ini tidak menjamin satu contoh pun. dua skrip dapat berpindah ke sisi lain dari ||operator pada saat yang sama, sebelum keduanya memiliki kesempatan untuk memulai skrip.
Sedat Kapanoglu
@SedatKapanoglu Diberikan, skrip itu bukan racecondition-proof, tetapi pertanyaan aslinya adalah tentang cron-job yang berjalan lama (yang dijalankan paling banyak sekali dalam satu menit). Jika sistem Anda membutuhkan lebih dari satu menit untuk proses pembuatan, Anda memiliki beberapa masalah lain. Namun, jika diperlukan untuk beberapa alasan lain, Anda dapat menggunakan kawanan (1) untuk melindungi skrip di atas terhadap kondisi balapan.
Michael Kowhan
Saya menggunakan ini tetapi untuk skrip bash yang harus memeriksa sendiri. Kode ini adalah ini: v = $ (pgrep -xf "/ bin / bash $ 0 $ @") ["$ {v / $ BASHPID /}"! = ""] && keluar 2
ahofmann
3

Lihat juga Tim Kay's solo, yang melakukan penguncian dengan mengikat port pada alamat loopback yang unik bagi pengguna:

http://timkay.com/solo/

Jika situsnya turun:

Pemakaian:

solo -port=PORT COMMAND

where
    PORT        some arbitrary port number to be used for locking
    COMMAND     shell command to run

options
    -verbose    be verbose
    -silent     be silent

Gunakan seperti ini:

* * * * * solo -port=3801 ./job.pl blah blah

Naskah:

#!/usr/bin/perl -s
#
# solo v1.7
# Prevents multiple cron instances from running simultaneously.
#
# Copyright 2007-2016 Timothy Kay
# http://timkay.com/solo/
#
# It is free software; you can redistribute it and/or modify it under the terms of either:
#
# a) the GNU General Public License as published by the Free Software Foundation;
#    either version 1 (http://dev.perl.org/licenses/gpl1.html), or (at your option)
#    any later version (http://www.fsf.org/licenses/licenses.html#GNUGPL), or
#
# b) the "Artistic License" (http://dev.perl.org/licenses/artistic.html), or
#
# c) the MIT License (http://opensource.org/licenses/MIT)
#

use Socket;

alarm $timeout                              if $timeout;

$port =~ /^\d+$/ or $noport                     or die "Usage: $0 -port=PORT COMMAND\n";

if ($port)
{
    # To work with OpenBSD: change to
    # $addr = pack(CnC, 127, 0, 1);
    # but make sure to use different ports across different users.
    # (Thanks to  www.gotati.com .)
    $addr = pack(CnC, 127, $<, 1);
    print "solo: bind ", join(".", unpack(C4, $addr)), ":$port\n"   if $verbose;

    $^F = 10;           # unset close-on-exec

    socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname('tcp'))       or die "socket: $!";
    bind(SOLO, sockaddr_in($port, $addr))               or $silent? exit: die "solo($port): $!\n";
}

sleep $sleep if $sleep;

exec @ARGV;
DNA
sumber
Untuk Mac OSX, ini akan gagal dengan Kesalahan solo(3801): Can't assign requested addresskecuali Anda memaksa a 0untuk parameter metode ke-3 pack. Apa yang baik untuk BSD juga bagus untuk Mac.
Eric Leschinski
1

Anda butuh kunci. run-onemelakukan pekerjaan, tetapi Anda mungkin juga ingin melihat flockdari util-linuxpaket.

Ini adalah paket standar yang disediakan oleh pengembang kernel, memungkinkan lebih banyak penyesuaian daripada run-onedan masih sangat sederhana.

styrofoam terbang
sumber
0

Solusi sederhana dari bash-hackers.org yang bekerja untuk saya adalah menggunakan mkdir . Ini adalah cara mudah untuk memastikan bahwa hanya satu instance dari program Anda yang berjalan. Buat direktori dengan mkdir .lock yang mengembalikan

  • benar jika kreasi itu berhasil dan
  • false jika file kunci ada, menunjukkan bahwa saat ini ada satu instance yang sedang berjalan.

Jadi fungsi sederhana ini melakukan semua logika penguncian file:

if mkdir .lock; then
    echo "Locking succeeded"
    eval startYourProgram.sh ;
else
    echo "Lock file exists. Program already running? Exit. "
    exit 1
fi


echo "Program finished, Removing lock."
rm -r .lock
domih
sumber
0

Solusi ini adalah untuk skrip bash yang perlu memeriksa sendiri

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
ahofmann
sumber