DX Application Performance Management

 View Only

Shell scription: handling program execution dependencies/semaphore?

By Jörg Mertin posted Apr 24, 2020 10:45 AM

  

Shell scription: handling program execution dependencies/semaphore?

The main issue in shell-scripts is to execute several programs, and make sure one program runs before spawning the next one. Usually one takes external programs to handle these (the SystemV Init/systemD subsystem does this very nicely under Linux, in Docker containers one would use the supervisor application). But one use case where these other solutions won't help is during the deployment of an application, and one needs to make sure the database is installed and usable.

The use case

Imagine one wants to deploy a web-application requiring a database. The database will be taken from the Operating system and needs to execute some scripts to prepare the database to receive data (for example, create the database, add users, grant privileges). Without these capabilities, the ongoing installation will fail as the required resource (here the database) is not ready.

In this specific case, one can only poll the database and check if it is running and in a ready state, ready to receive and answer commands.
Note that the below explained technique comes in very handy in docker environments.

The actual code would look like this:

1:   # Waiting for mysql to start
2:   RET=1
3:    while [[ RET -ne 0 ]]; do
4:        echo "=> Waiting for confirmation of MySQL service startup"
5:        sleep 2
6:        # Looking for the Uptime string of the Status message. As long as it won't show up, the DB
7:        # is not ready
8:        mysql --defaults-extra-file=/config.cnf $PCM_MYSQL_DB_NAME -e "status" | grep -q ^Uptime
9:        RET=$?
10:   done

It is actually just a loop using mysql to poll the status of the mysql database. As long as the DB does not return the Uptime in its response, we know it is not there.
So - what does this while loop do:

  • Line 2: We set the Return Value to be 1 / default. When the return code of line 8 (actually here the last command, from grep) find Uptime, it returns 0,m else it returns 1.
  • Line 3: We wait for the value of the variable RET to change to something else than 0, or else, loop through the code below
  • Line 4: Issue a warning message
  • Line 5: Wait 2 seconds (Unconditional, we just do it)
  • Line 8: We poll the mysql DB (credentials are inside the config.cnf file, as we don't want the password to show up in the command line) for its status, and grep the content to see if Uptime exists in the mysql output. If it does not exist, grep will return a error status of 1. In case it exists, an error status of 0 is returned and will exit the while loop to continue with whatever was requested.

Testing the code

When checking the function, we will use an existing database that has access.

jmertin@calypso:~/work/2020/Blogs/shellscripting$ ./sample.sh 


===============================================================================
== 2020-02-27 @ 12:03:36 Starting program sample ==============================
===============================================================================


=== Execution output below !  =================================================
================================================================================

=== WARNING: File does not exist !  ===========================================
 => File file.ba does not exist. Proceed [y/n]: y

=== Execution can continue after user choice !  ===============================
 => Please provide the user name for DB access: root
 => Please provide the password for the DB access: 
 => Please provide the Database name to use: pcm
 => Please provide host the database is hosted: 172.20.0.2


=== Waiting for Mysql to become ready !  ======================================
>>> sample[19867]: => Waiting for confirmation of MySQL service startup (2020-02-27 @ 12:03:56)
ERROR 2003 (HY000): Can't connect to MySQL server on '172.20.0.2' (111)
>>> sample[19397]: => Waiting for confirmation of MySQL service startup (2020-02-27 @ 12:04:02)

=== Program execution finished !  =============================================

Note, the first error message "Can't connect to MySQL server on '172.20.0.2'" happened when something is on the remote end of the communication channel, but does not yet respond. Perfect example on a not yet ready MySQL Database. When then the Database was ready, the script continued to run.

The Logfile shows the complete execution, with all the attempts. Could be fine tuned by setting the VERBOSE variable to true.

Feb 27 12:03:40 calypso sample[19867]: Username for accessing DB: root
Feb 27 12:03:48 calypso sample[19867]: Password for accessing DB: XXXXXXXX
Feb 27 12:03:51 calypso sample[19867]: Database name to be accessed: pcm
Feb 27 12:03:56 calypso sample[19867]: Database host: 172.20.0.2
Feb 27 12:03:56 calypso sample[19867]: => Waiting for confirmation of MySQL service startup (2020-02-27 @ 12:03:56)
Feb 27 12:03:58 calypso sample[19867]: => Waiting for confirmation of MySQL service startup (2020-02-27 @ 12:03:58)
Feb 27 12:04:00 calypso sample[19867]: => Waiting for confirmation of MySQL service startup (2020-02-27 @ 12:04:00)
Feb 27 12:04:02 calypso sample[19867]: => Waiting for confirmation of MySQL service startup (2020-02-27 @ 12:04:02)
Feb 27 12:04:14 calypso sample[19867]: Program execution finished​

Conclusion

One could also try using a semaphore. But a semaphore state would have to be updated and a check performed on the last status-check. Use an external program to validate/invalidate the state of the system. Over time, when starting up applications that rely on one another, best is to find out what the second application will require and perform a check on exactly that. It is the only way to actually make sure the second app will be able to run correctly.

0 comments
4 views

Permalink