Tuesday, March 9, 2010

Generating Arbitrary Numbers

Sometimes, "arbitrary" and "random" aren't synonyms. Here's an example of how to generate the former without their being the latter.

One nice thing about knowing people who make me think is that it gives me things to post about. For example, Hal Pomeranz, Ed Skoudis, Tim Medin, and Paul Asadoorian have a weekly blog, called Command Line Kung Fu, that compares and contrasts command-line tricks for different operating systems.

I only every use Linux, so I read Hal's stuff and skip the Windows and DOS stuff. Even with this, every week or two Hal's post makes me think, "Wait! Here's something he didn't mention!" (typically because it's slightly off-topic).

In this week's column they generate random time intervals.

Here's Hal's punchline:
[...] in larger enterprises you might have hundreds or thousands of machines that all need to do the same task at a regular interval. Often this task involves accessing some central server-- grabbing a config file or downloading virus updates for example. If a thousand machines all hit the server at exactly the same moment, you've got a big problem. So staggering the start times of these jobs across your enterprise by introducing a random delay is helpful. You could create a little shell script that just sleeps for a random time and then introduce it at the front of all your cron jobs like so:

0 * * * * /usr/local/bin/randsleeper; /path/to/regular/cronjob
(The column sketches how to implement 'randsleeper'.)

Yep. This works fine.

But as it stands, the cronjob could kick off one job at 9:59, and the next one at 10:00. What if I want to spread my machines across the hour, but want each machine to use a fixed timeslot, so the elapsed time between runs is a full hour for any given machine?

Here's one way:
  1. Pick an arbitrary machine-specific number, like the IPV6 address, or the MAC address of the ethernet card,
  2. Convert it to an integer.
  3. Take it mod the time interval.
  4. Use that number for the time to start the job.
Here's code to do that, which, as always, I grow, bit-by-bit, on the command line, by getting a little piece right, recalling that piece, and adding another step.

  • Step 1:
Get a unique, but arbitrary, machine-specific identifier (the MAC address of the first NIC).
$ ifconfig | awk '/HWaddr/ {print $NF; exit 0}'
  • Step 2:
Strip the colons
$ ifconfig | awk '/HWaddr/ {print $NF; exit 0}' | sed 's/://g'
And interpret the result as a hex number. (The shell requires hex numbers begin with "0x", so I'll just tack that on.)
$ echo $(( 0x$(ifconfig | awk '/HWaddr/ {print $NF; exit 0}' | sed 's/://g') ))
  • Step 3:
Mod it by the number of seconds in an hour, to get an arbitrary second.
$ echo $(( 0x$(ifconfig | awk '/HWaddr/ {print $NF; exit 0}' | sed 's/://g') % (60*60) ))
  • Step 4:
Always sleep until that many seconds after the hour, then kick off the job.
$ crontab -l > Cronjobs
$ echo "0 * * * * sleep \$(( 0x\$(ifconfig | awk '/HWaddr/ {print \$NF; exit 0}' | sed 's/://g') % (60*60) )); /path/to/regular/cronjob" >> Cronjobs
$ crontab Cronjobs

(For step four, I'd probably actually kick off cron -e and paste the line in; otherwise there are just too many ugly backslashes to get wrong.)

Warning: This will not work if your machines' MAC addresses cluster around the same value, mod 556. :-)


jed said...

For sure, crontab -e. One of the most annoying dimwit mistakes I've made was accidentally typing > instead of >>. There were a few profound quotations in that file that I shan't ever be able to recover.

hilltop_yodeler said...

Hi Jeff, Darrin Goodman here... I picked with you at one of your Sunday brunch jams several years ago -- I arrived with Tory Heinrich and brought my mandolin and banjo with me. Didn't know that you were also a Linux geek. Recently formed a stringband called Cottonwood Grove here in Fort Collins and we are trying to balance out the Bluegrass scene up here a bit. Not sure how to get in touch with you so I'm leaving you a comment on your blog. Would love to head out your way and pick a bit some time. Hope that you are well!

Generic Viagra Blog said...

Excellent thanks, I'm just starting to study programming and I don't really know to much about this.