Friday, April 10, 2009

Serialise Asynchronous Shell Scripts

You know that your shell script will break if more than one copy is running in the system. You also know that your script will be launched via a scheduler and you do not have control on when the job will be run. In such situation, you may want to have some form of locking mechanism feature in your script to ensure serialisation. First, your script has to start with acquiring a lock before doing any work. If not, it will have to go into a spin and check periodically to see whether such lock is still available, else exit if such lock cannot be acquired within a certain period. Removal of lockfile is part of the cleanup process when the script terminates.

Here is my sample template to implement what I described above. Hope it can help you to serialise your asynchronous jobs.

#! /bin/ksh


trap "lock_release" 0 1 2 3 5 9 15
LOCK_FILE="${TMPDIR:-/tmp}/lockfile-${0##*/}"
too_old=86400; # 1 day, 86400 seconds
verbose=1


debug()
{
        [ $verbose -eq 1 ] && echo $*
}
lock_acquire()
{
        debug "Acquiring lock ..."

        #
        # if lockfile is too old, remove it
        #
        if [ -f $LOCK_FILE ]; then
                tdiff=`perl -e '@a=stat("'$LOCK_FILE'");print time()-$a[9]'`
                if [ $tdiff -gt $too_old ]; then
                        debug "Lock file ($LOCK_FILE) is older than 1 day, remove it"
                        rm -f $LOCK_FILE
                fi
        fi

        spin=5
        timeout=30
        howlong=0
        while :
        do
                if [ ! -f $LOCK_FILE ]; then
                        touch $LOCK_FILE
                        LOCK_STATUS=0
                        break
                fi
                sleep $spin
                (( howlong = howlong + spin))
                if [ $howlong -gt $timeout ]; then
                        LOCK_STATUS=1
                        break
                fi
                debug "Waited ${howlong}s for lockfile ($LOCK_FILE) to be released"
        done

        if [ $LOCK_STATUS -eq 1 ]; then
                debug "ERROR. Unable to acquire lock. Exit."
                exit 1
        fi
        debug "OK. Lock ($LOCK_FILE) acquired."
        return $LOCK_STATUS
}
lock_release()
{
        if [ $LOCK_STATUS -eq 0 ]; then
                debug "Releasing lock ($LOCK_FILE)"
                rm -f $LOCK_FILE > /dev/null 2>&1
        fi
}
lock_acquire


# ----- main program ------
debug "Do something"
sleep 30

Try to run this script in a few terminals to have a feel yourself.

Labels:

0 Comments:

Post a Comment

<< Home