1

I am looking for best practice for ensuring a script executed by a cron job every minute only has one running instance. For e.g. if I have a cron that executed every minute and in case the process takes longer then one minute then do not execute another till done.

For now I have the below function. In essence I get the name of the current process and I do a ps grep to see if the count of the current process is listed. Kinda messy so I was looking for a more pythonic way.

I place the code on top of a file. It does work but again messy.

def doRunCount(stop=False,min_run=1):
    import inspect
    current_file = inspect.getfile( inspect.currentframe() )
    print current_file
    fn = current_file.split()
    run_check = os.popen('ps aux | grep python').read().strip().split('\n')
    run_count = 0
    for i in run_check:
        if i.find('/bin/sh')<0:
            if i.find(current_file)>=0:
                run_count = run_count + 1
    if run_count>min_run:
        print 'max proccess already running'
        exit()
    return run_count
Tampa
  • 75,446
  • 119
  • 278
  • 425
  • It sounds like you are trying to avoid a resource race condition. Why not solve the problem directly by making sure the cron jobs are atomic? Perhaps describe the nature of what they are doing. – HeyWatchThis Mar 06 '12 at 04:22
  • Just write a PID file and check to see if it exists. See http://stackoverflow.com/questions/788411/check-to-see-if-python-script-is-running –  Mar 06 '12 at 04:43
  • But what if the process crashes and there is an existing pid and the process did not have time to remove the pid? – Tampa Mar 06 '12 at 05:52

1 Answers1

2

I don't know if you could describe this as best practice, but I would use a pid file. Here's a snippet similar to what I have used several times to ensure only one instance of a specific app is running.

import os, sys

PID_FILE = '/path/to/somewhere.pid'

if os.path.exists( PID_FILE ):
    pid = int(open( PID_FILE,'rb').read().rstrip('\n'))
    if len(os.popen('ps %i' % pid).read().split('\n')) > 2:
        print "Already Running as pid: %i" % pid
        sys.exit(1)
# If we get here, we know that the app is not running so we can start a new one...
pf = open(PID_FILE,'wb')
pf.write('%i\n' % os.getpid())
pf.close()

if __name__ == '__main__':
    #Do something here!
    pass

Like I said this is similar to what I have used, but I just re-wrote this snippet to be a little more elegant. But it should get the general concept across! Hope this helps.

Here is a slight modification which should clear up any issues arising from a process crash. This code will not only validate that a pid file exists, but that the pid in the file is still alive and that the pid is still the same executable.

import os, sys

PID_FILE = '/path/to/somewhere.pid'

if os.path.exists( PID_FILE ):
    pid = int(open( PID_FILE,'rb').read().rstrip('\n'))
    pinfo = os.popen('ps %i' % pid).read().split('\n')
    if len( pinfo ) > 2:
        # You might need to modify this to your own usage...
        if pinfo[1].count( sys.argv[0] ):
            # Varify that the process found by 'ps' really is still running...
            print "Already Running as pid: %i" % pid
        sys.exit(1)
# If we get here, we know that the app is not running so we can start a new one...
pf = open(PID_FILE,'wb')
pf.write('%i\n' % os.getpid())
pf.close()

if __name__ == '__main__':
    #Do something here!
    pass

After that I just leave the pid file, since you don't really need to worry about a false positive. Note you might need to modify the second step of validation to your own specific usage!

john-charles
  • 1,417
  • 4
  • 17
  • 30
  • I do like the approach but now curious about a lingering pid that never got removed do to a crash – Tampa Mar 06 '12 at 05:54
  • I posted a second version which should answer some of your concerns. Basically I do everything I can to validate that the process in the pid file is really dead. After that, the existence of the pid file is a non-issue! – john-charles Mar 06 '12 at 06:14
  • Thnaks, when I run your code I get this error. pf.write('%i\n' % pid) NameError: name 'pid' is not defined – Tampa Mar 07 '12 at 01:25
  • Thnaks, when I run your code I get this error. pf.write('%i\n' % pid) NameError: name 'pid' is not defined – Tampa Mar 07 '12 at 01:25
  • the line `pf.write('%i\n' % pid)` should be `pf.write('%i\n' % os.getpid())` sorry about that! – john-charles Mar 07 '12 at 04:31