Ivan M Ivan M - 1 month ago 20
Bash Question

Bash linux force multiple instances to wait for running sqlplus command to finish

I have a bash script that allows multiple running instances at once. There is, however, a sqlplus connection to an Oracle database to retrieve a value. If this value is being fetched by multiple bash script instances at once, they might retrieve the same value, because while the first instance is still processing, the second instance is already validating the 'old data'.

I can only think of two option, which is locking the function by a PIDFILE. Downside is that if the user aborts the operation before the pidfile gets removed, it will cause problems. Another is locking the whole script based on declaring maximum processes counted with

ps -ef | grep 'scriptname' | wc -l
. But this would lock the whole script, and not just the database part.

So is there maybe another option? Is this sqlplus connection to the database somehow possible to catch so I can place the multiple running instances on pause while this function is being executed by one of the instances?

Answer

You could use a unique value in a tmp file (/tmp/pidfile for this example), and have it reset upon abort or completion of script.

Update: This seems solid, see tests down bottom.

#!/bin/bash


bashpid="$$"                     # Records PID of script.  Unused so far
pidfile=/tmp/pidfile             # pidfile var
pidlimit=2                       # pidlimit

cleanup () {                     # cleanup operations
        exit
    }

pidreset () {                    # trap operations (if reqd)
        pkill -a -P "$bashpid"
        kill -9 0
    }

trap "pidreset" SIGINT           # trap (if reqd)


countpid () {                    # This is the true PID count, only called
                                 # Once allowed in the database block
                                 # This includes self instance

     ps aux | grep "$(basename $0)" | grep -v grep > $pidfile

}

countloop () {                   # This counts PID while waiting for a spot
                                 # (-1 decrement since self is included but 
                                 # not yet granted access to database block)
    echo "(( ("$(ps aux | grep "$(basename $0)" | grep -v grep | wc -l )") - 1))" | bc > $pidfile

}
databasecall () {                # The database script calls

    touch $pidfile
    pidcount="$(cat $pidfile | wc -l)"
    until [[ "$pidcount" -le "$pidlimit" ]]; do
        echo "too many processes"
        countloop 
        pidcount="$(cat $pidfile | wc -l)"
        sleep 5
    done

    #   ================  Start of Database access Code ================
    #   Go ahead and do database stuff
    #   Write this script into the pidfile incrementing its count also

    countpid 
    pidcount="$(cat $pidfile | wc -l)"
    echo "We're in"
    echo "Scriptcount: $pidcount (Including this one)"
    sleep 5
    cleanup
    # return / whatever
    #   ================  End of Database access Code ================

}



echo "Main script block"          # Main script outside of Database block
sleep .1


databasecall                      # Call database function


cleanup                           # Call to housekeeping

Tests

It works fine but settles at running one more than pidlimit, though i'm not certain where this is occurring or if it's my test scenario yet.

bash>echo "imrunning & sleep .5 ; imrunning & sleep .5 ; imrunning & sleep .5 ; imrunning & sleep .5 ; imrunning" > /tmp/initrunning ; chmod +x /tmp/initrunning
bash>/tmp/./initrunning 
Main script block
We're in
Scriptcount:        1 (Including this one)
Main script block
We're in
Scriptcount:        2 (Including this one)
Main script block
too many processes
Main script block
too many processes
Main script block
too many processes
We're in
Scriptcount:        3 (Including this one)
We're in
Scriptcount:        3 (Including this one)
We're in
Scriptcount:        3 (Including this one)
bash