Thursday, November 24, 2011

Simultaneous Background Process Monitoring in Bash using Multitail

I'm working on an installer for the development server environment we use at www.salsalabs.com

There are time-consuming tasks which are part of the installation process which can be done simultaneously: installation of program dependencies (with brew) and download of code repositories (using git).

I backgrounded the first task in my installation script using the usual & and let the script continue on and complete the second task "normally". After the second task completed, a wait command makes sure that the script does not proceed until the first task is also complete.

That's all well and good. It works just dandy... except that the output of these two processes is intermixed and due to each process providing feedback about the process of individual steps ... that output is very very very very very messy.

So I looked for a solution and I found MultiTail. Awesome! And available via brew! Awesome!

Except ... I don't have log files, and I don't want to generate log files. I want to have two different output streams and use multitail to view them simultaneously in one terminal window.

Two different output streams is easy enough, we've got file descriptors for that, right? (check out the exercise here, it makes my brain hurt). No, we need two different files that are going to act like streams. Oh! We want buffers!

(Cue the superhero music)

Here come FIFOs to the rescue! Use the handy-dandy mkfifo command and you can turn any output stream like stdout, stderr into a buffer that looks like a file to the operating system. Yay!!!!

So, our code, something like this:


install_dependencies.sh &
install_packages.sh
wait

install_something_else.sh


becomes:


mkdir /tmp/fifos
mkfifo "/tmp/fifos/Dependencies"
mkfifo "/tmp/fifos/Code Installation"

install_dependencies.sh > "/tmp/fifos/Dependencies" &
install_packages.sh > "/tmp/fifos/Code Installation"
wait

install_something_else.sh


And we add on multitail:


mkdir /tmp/fifos
mkfifo "/tmp/fifos/Dependencies"
mkfifo "/tmp/fifos/Code Installation"

install_dependencies.sh > "/tmp/fifos/Dependencies" &
install_packages.sh > "/tmp/fifos/Code Installation" &
multitail -ts --basename "/tmp/fifos/Dependencies" "/tmp/fifos/Code Installation"
wait

install_something_else.sh


The problem, now? multitail is never going to exit on it's own, we'll have to press "q" to end the process when the other stuff is done ... assuming we can tell for sure. So we never get to the wait command and we never get on to the next steps of our installation. Let's send an email to the developer and ask him for that feature ... but we'll make our own solution in the meantime.

So, here presented for you is my solution for monitoring a group of parallel background processes using multitail and ending the monitoring when all processes are complete.


PROCESS_COMPLETED_COUNT_TMP_FILE=$(mktemp -t "proc-count")
echo 0 > $PROCESS_COMPLETED_COUNT_TMP_FILE

function killtail {
    MUST_COMPLETE=$1
    PROCESS_COMPLETED=$(cat $PROCESS_COMPLETED_COUNT_TMPO_FILE)
    ((PROCESS_COMPLETED++))

    if [ PROCESS_COMPLETED -ge $MUST_COMPLETE ]; then
        # dangerous if there are multiple multitails running at the same time
        TAIL_PID=$(pidof multitail)
        kill $TAIL_PID
        rm -rf $PROCESS_COMPLETED_COUNT_TMP_FILE
    fi
}

mkdir /tmp/fifos
mkfifo "/tmp/fifos/Dependencies"
mkfifo "/tmp/fifos/Code Installation"

# subshell for everything involved with installing dependencies
(
    install_dependencies.sh > "/tmp/fifos/Dependencies"
    killtail 2
)> "/tmp/fifos/Dependencies" &

# subshell for everything involved with installing code
(
    install_packages.sh > "/tmp/fifos/Code Installation" &
    killtail 2
)> "/tmp/fifos/Code Installation" &

multitail -ts --basename "/tmp/fifos/Dependencies" "/tmp/fifos/Code Installation" 2> /dev/null
wait

rm "/tmp/fifos/Dependencies"
rm "/tmp/fifos/Code Installation"

install_something_else.sh


Now the subshells are backgrounded instead of the scripts themselves. Both "steps" are backgrounded, not just the first one. The command to increment the kill counter and check for the appropriate number of processes to have completed is added to the block of code. As well as backgrounding the subshells, we're directing stdout output from the subshells into our FIFOs, instead of directing the output of individual commands.

We pass the --basename option to multitail so that we see only the name of our FIFO in the multitail windows... and we've used nice human readable names for these temporary buffer files, so isn't that special?

When the kill counter hits the magic number, a SIGTERM will be sent to the multitail that we get back from pidof multitail. This makes multitail error, so we're throwing away the error output from multitail.

Finally, we still have a "wait" statement, just in case our multitail monitoring of the processes doesn't work for some reason. We still want to make sure they complete before moving forward with more steps. We remove the FIFOs and move on to the next steps in our installation.

Happy process monitoring!

Monday, December 21, 2009

Question: How do you convert a Java String object to a Javascript String in Rhino?

Convert any Java object with a toString() method into a JS string by calling String(object) in JS


var javaString = new java.lang.String("Java String");
var javascriptString = String(javaString);

Wednesday, December 16, 2009

Javascript for the Mad Scientist: advanced javascript for jQuery

I had a great time last night presenting a talk on advanced js for jQuery to the Frederick Web Tech. Meetup.

Christopher Thatcher of env.js and jQuery-Claypool fame and I hung out after the meeting and had drinks and lots of great conversation. Because of family and holidays and such-like many of the regulars couldn't come or couldn't stay for after-meeting socializing.

You can view or download/fork the presentation "slides" if you are interested in looking at the material.

Saturday, December 12, 2009

jQuery Namespacing / Child Plugins Update

I've updated the example namespacing plugin to use new arguments.callee() instead of extending the method and prototype individually.  This should allow for proper inheritance.

It does require that the method also wrap the plugin behavior in

if (this.jquery) { ... }

Tuesday, December 8, 2009

jQuery Namespacing / Child Plugins / Modularization

When writing jQuery plugins, you'll find that you want to group related functionality into a single "namespace" or "module", that acts as a parent for multiple child plugins.

There is not a lot of information readily accessible about the subject, from what I can tell.  So little in fact that only after I came up with a quick and dirty method for myself did I dive deep enough to find anything.

Friday, December 4, 2009

Splitting Directives - Modular "Molecule" Configurations of Apache httpd

At my 9 to 5, I am currently wearing a sys admin hat (this is something between a welder's mask and sherlock holmes' tweed cap, I think). I love process automation (the foundation of programming, I'd say) so everything is done with a bash script for maximum reuse.

My vision is to be able to rebuild a new version of my httpd configurations and deploy them to the target servers in one command (the testing process happens BEFORE the final build and deploy, silly!)

Why? One of the problems that I've seen in past projects is lack of configuration management and revision control for infrastructure applications like httpd or websphere. Worse, different tiers in the enterprise (boldly going, anyone?) and different servers might have different configurations because of lack of strictness in implementing changes methodically.

Sunday, November 29, 2009

Google Chrome Frame

Since my Web Worker shim relies on Google Gears plugin and the Google Chrome Frame plugin may make Gears obsolete (at least for this use case), I have temporarily suspended work on my html5 shims while I wait for that issue to shake out.

I may push harder for a non-Gears, native solution based on Statified JS. There is also a possibility to offer server-side worker implementations, which dovetails nicely with my recent research about the state of server-side JS.

People seem to be very excited about Node, but I prefer env.js and the Claypool JS framework from what I can see.

I think that server-side DOM emulation is the "BIG DEAL™" and that server-side JS should build on an existing server stack (which was one of the nicest points in Jaxer's favor when it was in active development).

Is anyone working on making any of the other server-side JS servers apache httpd modules? I haven't seen any indication that they are interested in this kind of integrated approach.