tags:

views:

369

answers:

6

I have a process that runs on cron every five minutes. Usually, it takes only a few seconds to run, but sometimes it takes several minutes. I want to ensure that only one version of this is running at a time.

I tried an obvious way...

File.open("/tmp/indexer_lock.tmp",'w') do |f|
  exit unless f.flock(File::LOCK_EX)
end

...but it's not testing to see if it can get the lock, it's blocking until the lock is released.

Any idea what I'm missing? I'd rather not hack something using ps, but that's an alternative.

Thanks for all your help, Jason

+2  A: 

You could create and delete a temporary file and check for existence of this file. Please check the answer to this question : one instance shell script

shodanex
This would be the simplest solution that still works.
Geo
+1  A: 

Ok, working off notes from @shodanex's pointer, here's what I have. I rubied it up a little bit (though I don't know of a touch analogue in Ruby).

tmp_file = File.expand_path(File.dirname(__FILE__)) +  "/indexer.lock"
if File.exists?(tmp_file)
  puts "quitting"
  exit
else
  `touch #{tmp_file}`
end

.. do stuff ..

File.delete(tmp_file)
Jason Butler
+3  A: 

Although this isn't directly answering your question, if I were you I'd probably write a daemon script (you could use http://daemons.rubyforge.org/)

You could have your indexer (assuming its indexer.rb) be run through a wrapper script named script/index for example:

require 'rubygems'
require 'daemons'

Daemons.run('indexer.rb')

And your indexer can do almost the same thing, except you specify a sleep interval

loop do
   # code executing your indexing 

   sleep INDEXING_INTERVAL
end

This is how job processors in tandem with a queue server usually function.

ucron
+2  A: 

If your using cron it might be easier to do something like this in the shell script that cron calls:

#!/usr/local/bin/bash
#

if ps -C $PROGRAM_NAME &> /dev/null ; then
   : #Program is already running.. appropriate action can be performed here (kill it?)
else
   #Program is not running.. launch it.
   $PROGRAM_NAME
fi
pisswillis
A: 

Can you not add File::LOCK_NB to your lock, to make it non-blocking (i.e. it fails if it can't get the lock)

That would work in C, Perl etc.

MarkR
+1  A: 

There's a lockfile gem for exactly this situation. I've used it before and it's dead simple.

Sarah Mei
This is awesome. As simple as: Lockfile("name_of_lock_file", :retries => 0) { your_logic_here }
Lee