tags:

views:

83

answers:

5

I have couple of php scripts which are called by cron. The cron job runs every 20 mins. Sometimes script finishes the job in few mins and sometimes it takes more than 20 mins.

I want to prevent the race condition here. One way I have in my mind is to use the database set the flag when page is running and remove flag when the job is done. But I really don't want to use database.

Is there any other solution?

+1  A: 

You could lock a pre-defined "flag" file using flock() and check whether that file is still locked before starting the script.

Pekka
I would not recommend this, what if the script dies ?
Stephen
Yes for that reason I did not use the file locking
jason
True - you could use some sort of timeout, but it may not work in this frequent a task.
Pekka
@Stephen All locks are automatically released if the script terminates... It's a feature of PHP (that it cleans up resources at script termination)...
ircmaxell
@ircmaxell have you tested this? Because when i did this for my previous employer the file's were not unlocked. Yes php has automatic garbage collection, but that does not mean it will unlock the files if the script dies. Test it, make the script crash and see if the file is unlocked. I bet it's still locked ;)
Stephen
Well, according to http://www.php.net/manual/en/function.flock.php it was removed in `5.3.2`... So I guess that's why it always worked for me (I was using `5.2`)...
ircmaxell
A: 

Sure, temporary file =]

or fork the process. See more on forking here: http://php.net/manual/en/function.pcntl-fork.php

Suggestion: http://www.saynotoflash.com/archives/multi-process-php-execution/

Stephen
+2  A: 

You could use a marker file, or a shared memory area, or any of the other normal forms of inter-process communication. The gold-plate solution would be to implement a little batch-queue process.

Brian Hooper
Can you provide me an example?
jason
create a file when the job is running, and remove it after the job is finished. Than if the job runs again, while another job is running, check for this file before starting.
jigfox
Of a batch-queue process? Sorry, I can't. I'm a VMS man really and VMS includes batch processing. What you want is a little program that reads a file containing a list of jobs, and when it finds one, executes it and then deletes it from the file. This involves a bit of process synchronisation. Cron jobs don't then execute the job; they append a request to the file. Somebody must have done this before; perhaps a little trawl of the web might turn up something.
Brian Hooper
Have a look at this; it might be what you are looking for. http://www.cs.ait.ac.th/~on/O/oreilly/unix/upt/ch40_06.htm
Brian Hooper
+1  A: 

If you make a pid file to indicate a process is running be sure to check when the file was created and have a threshold in place for how long is too long. That way if your job dies before it has an opportunity to delete it's pid file your jobs don't screech to a halt.

blcArmadillo
+1  A: 

The best method (that I've used) involves saving the process id in a "lock file".

function getLock() {
    //First, check to see if the script is already running
    $data = @file_get_contents('path/to/process.pid');
    if ($data && posix_kill($data, 0)) {
        throw new RuntimeException(
            'Unable To Attain Lock, Another Process Is Still Running'
        );
    }
    file_put_contents('path/to/process.pid', posix_getpid());
}

Basically, it reads the file, and then tries to posix_kill the pid contained inside. Note, that when you use a signal of 0 to posix_kill, it merely tells you if the call will succeed (meaning that the process is running).

Doing it this way has the advantage that if the process dies without cleaning up the lock file, it'll be detected and will run again.

ircmaxell
So when work is done I need to just unlink pid file right? What if I kill the php process what will happen?
jason
You can unlink it. The beauty of this approach is that you don't have to. All the file does is store the `pid`. So even if you don't unlink it, it should still work fine (so long as another process didn't start on the same `pid`, which isn't too likely)... If the file still exists, it checks to see if a process with the stored `pid` is still running. If it's not (because it died, you killed it, or it finished), it will get the lock and continue execution...
ircmaxell
working perfectly!!
jason
I tried this from web page it worked fine but when I use this code from shell it does not work. What could be the reason?
jason
ummm... Perhaps the file path? I've used it many times (from the shell), and it has never failed me. I'd manually check that the lock file exists while the other is running first, and go from there...
ircmaxell
No path is fine. Have you tried this from shell and web together? Try the first instance from shell, second from web page and third one from another shell.You will notice the first and last will work (either run both from shell or web) fine but second one will stuck (opposite to first and last)
jason
Ahhh, I know what's going on. You're using mod_php or fastcgi for the web interface. Then it's taking the webserver's (or persistent process) pid. So it will always remain running so long as the web server is. If you NEED it to be runnable from both, you'll need to remove the file at the end of execution (I'd suggest `register_shutdown_function`). But beware that a fatal error will leave it hanging. If you really really need it to run from the web as well as shell, I'd suggest going with an `flock` as well (since the `flock` will be released form a fatal error)...
ircmaxell
do you mean first I should open file and get the lock and then check pid? If this is the case checking PID is not useful. Some one here commented flock is not good option when script dies.
jason
When the script dies, `PHP` will automatically release any locks that it had (Unless there's a seg-fault IIRC). And you are correct, checking the PID is redundant if you're using `flock`
ircmaxell