如何使用Rails防止重叠的cron作业?

时间:2012-10-30 00:31:40

标签: ruby-on-rails crontab race-condition whenever

我有一个cron作业设置,每5分钟运行一次任务。但有时任务需要>运行5分钟,因此该任务的另一个副本由cron同时运行。在运行另一个副本之前,我们是否有办法让它等待另一个工作完成?

4 个答案:

答案 0 :(得分:5)

AFAIK您无法随时使用,但您可以在脚本中处理此问题。 这可以通过以下解决方案之一来完成

  1. 使用在作业开始时设置并在作业结束时清除的标志(或某些信息,如开始时间,结束时间,成功状态)在数据库中处理此问题,并在每次作业开始时检查此标志如果以前的工作完成了;但请确保处理异常,就像在清除标记之前进程死亡一样,其他进程无法运行

  2. 您可以通过创建临时文件并为当前进程对其进行独占锁定来使操作系统作为标志工作,因此在当前进程之前没有其他进程可以对此文件进行独占锁定进程结束,然后当进程完成时它将释放锁并让其他进程工作。要做到这一点,包括这是你的cron工作的首要任务

    file = File.new("cron.lock", "a")
    can_lock = file.flock(File::LOCK_EX | File::LOCK_NB)
    
    if can_lock == false
      exit 1
    else
      #do whatever you want
    end
    
  3. 第二种方法的优点是即使进程意外终止,操作系统也会自动释放锁

    对我来说,我选择了第一种方法,因为如果上一个过程完成或花费的时间超过特定时间限制,我需要启动另一个过程

    有关详情,请查看this link

答案 1 :(得分:2)

使用文件系统或数据库锁

你无法阻止使用cron或类似的重叠 - 至少,不是直接 - 但你有很多选择。您可以在生成新任务之前检查正在运行的任务的进程列表,但这仍然容易受到竞争条件的影响。一些更好的选择是:

  1. 在shell脚本中使用信号量或文件锁。 flock lockfile 是用于此目的的优秀shell实用程序。
  2. 如果您的cron作业涉及对数据库的更改,请使用包含行级锁定或信号量列的表来防止在另一个进程正在运行时进行更改。
  3. 增加cron作业之间的间隔,以便您的进程有时间在下一次运行之前完成。即使您使用其他选项之一,这也许是一个好主意。
  4. 使您的脚本具有幂等性,以便并发操作不会相互影响。
  5. 查看队列或单例进程是否比cron作业更适合您。
  6. 对于这类问题,没有完美的答案。很大程度上取决于您的脚本正在做什么,以及系统的整体架构。你的里程会有所不同。

答案 2 :(得分:1)

我认为最好的选择是任何类型的锁(使用文件,数据库等),但是当你使用锁时,你需要在你的过程中非常巧妙地实现错误处理,如果你的锁没有被释放那么你的cron永远不会再次运行这个过程。

答案 3 :(得分:0)

使用script_with_lock'script_name',锁定:'lock_name'

job_type :script_with_lock, "cd :path && :environment_variable=:environment flock -n /var/lock/:lock.lock bundle exec script/:task :output"

使用runner_with_lock“红宝石代码”,锁定:“ lock_name”

job_type :runner_with_lock, "cd :path && flock -n /var/lock/:lock.lock script/rails runner -e :environment ':task' :output"