Is it possible somehow to execute rake task only if it is not running already, I want to use cron for executing some rake tasks but rake task shouldn't start if previous call is not finished Thanks

有帮助吗?

解决方案

I use lockrun to prevent cron tasks from running multiple times (this only works when invoking the command through the same lockrun invocation, so if you need to protect from various invocation paths, then you'll need to look for other methods).

In your crontab, you invoke it like this:

*/5 * * * * /usr/local/bin/lockrun --lockfile=/tmp/this_task.lockrun -- cd /my/path && RAILS_ENV=production bundle exec rake this:task

其他提示

Also, you can use a lockfile, but handle it in the task:

def lockfile
  # Assuming you're running Rails
  Rails.root.join('tmp', 'pids', 'leads_task.lock')
end

def running!
  `touch #{lockfile}`
end

def done!
  `rm #{lockfile}`
end

def running?
  File.exists?(lockfile)
end

task :long_running do
  unless running?
    running!
    # long running stuff
    done!
  end
end

This probably could be extracted into some kind of module, but you get the idea.

The general non-rake specific solution to this is to use a pid file as a lock. You would wrap the rake task in a script that creates this file when it runs rake, removes it when rake finishes running, and checks for it before beginning.

Not sure if rake has something built in, I don't know of anything.

If you simply want to skip this rake task, you could also use the database to store information about the task progress (e.g. update a field when you start and finish a given task) and check that information before running each task.

However, if you want a queue-like behavior, you might want to consider creating a daemon to handle the tasks. The process is very simple and gives you a lot more control. There is a very good railscast about this: Custom Daemon

The daemon approach will also prevent rails environment from being loaded again on each task. This is especially useful if your rake tasks are frequent.

Here's my variant with file lock for rails rake tasks.

Put this in your rake task file (under namespace, so it wont overlap with other rake tasks):

def cron_lock(name)
  path = Rails.root.join('tmp', 'cron', "#{name}.lock")
  mkdir_p path.dirname unless path.dirname.directory?
  file = path.open('w')
  return if file.flock(File::LOCK_EX | File::LOCK_NB) == false
  yield
end

usage:

cron_lock 'namespace_task_name' do
  # your code
end

full example:

namespace :service do
  def cron_lock(name)
    path = Rails.root.join('tmp', 'cron', "#{name}.lock")
    mkdir_p path.dirname unless path.dirname.directory?
    file = path.open('w')
    return if file.flock(File::LOCK_EX | File::LOCK_NB) == false
    yield
  end

  desc 'description'
  task cleaning: :environment do
    cron_lock 'service_cleaning' do
      # your code
    end
  end
end
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top