Thursday, August 2, 2012

Run only a single instance of a cron job and prevent overlap

When dealing with web applications often a cron process must be executed at most once. If a cron job overlaps, database queries can cause performance issues or at worse deadlocks and stale processes.

Here is a script that solves this problem. Explained with detail as there are a lot of things happening in 5 lines of code.

#!/bin/bash

# Installation
# mkdir /var/run/moodle
# chown root.apache /var/run/moodle
# chmod 775 /var/run/moodle
# copy this file and make it executable by the cron user. 
# chmod this file 744
# add it to crontab or crontab -e -u apache 
 
# Explanation
# 1. set -e tells a bash script to exit whenever a non zero value is i
#  returned (0 means function executed without error)
# 2. flock needs 200 or any int to label the file descriptor
# 3. The ( brackets ) execs each line of commands in order and check to 
#  see if they return 0. 
# 4. 200> tells the fd 200 to create the lock file if it doesn't already 
#  exist 
# 5. -n nonblock, will return 1 if the lock is taken. as cron is being 
#  ran every 5-10 mins we can wait for the next one
# 6. trap, if cntrl-c is called or a command is killed, it will execute 
#  the command and exit. Our case it removes the lock file.

# Note: This works great for cron and flock files. May be an issue with 
#  race conditions if something other than flock eg. echo 'busy' > file.pid.

LOCKFILE=/var/run/moodle/moodlecron.lock
set -e
(
    flock -n 200 
    trap "rm $LOCKFILE" EXIT 
    # Add commands to execute
    /usr/bin/php /var/www/html/moodle/admin/cli/cron.php  
) 200>$LOCKFILE