Back in the days of the first Internet Bubble, Tom received a call from his colleague Steve. They were both contractors, working for the same elite contracting company, and Steve had run into a problem with a Perl script he couldn't figure out. Tom, being a bit more experienced in the arcane art of Perl, was confident he could help out.

The Perl script had started out as a quick hack by an in-house developer, and was little more than a simple CGI script that managed the company's website account database. Apparently, it had run "instantly" many months back, but as of late, it was seeing heavy use and taking a few minutes to run. Worse, it was "occasionally" losing data. And by occasionally, I mean most of the time: it'd typically took an a good three or four attempts to create an account or change a password.

The first thing Tom wanted to check on was the database settings. Steve couldn't find any database settings, but it was clearly using some kind of database, as the information was all in associative arrays, and there was no load routine to be found. Steve assumed that it was using a tied hash, to one of the hierarchical databases Perl has had available forever. Hearing it was using something that should be reasonable, Tom next suggested looking for nested loops, and there weren't any of those.

"Um, Tom," Steve said, "What's Data::Dumper?"

"That's a debugging module - it lets you output, in text format, pretty much any Perl data structures, regardless of how complex."

"Well, it looks like there may be some debugging code left in here, then - I see after it's finished with the job, it does a 'print STDERR Data::Dumper([\%hash], [qw(*hash)]);'"

"Wait, didn't you say he used %hash as the database tie?" Tom asked quickly. "That could be why it's so slow right there - he's dumping the entire password database as text after each execution. You have a test environment, right? Comment that out and see what it does."

"Damn it," Steve said, "Somebody else made a change to the test environment script. Oh well, I'm the only one who's supposed to be making changes to this right now, I'll just force my write. It's not the first time someone else has changed this file since I was given full ownership of it. They'll just have to learn they need to talk to me before changing it."

Steve tried the web page again after forcing his save, but the script still took nearly a minute to log him into their test environment server.

Tom asked Steve to look for a few other patterns, but they all turned up empty. Then, just as Tom was about to give up, and say he probably couldn't help over the phone, one of Steve's coworkers exclaimed loud enough for Tom to hear, "Wow! What did you do to the test server? That thing flies! It's refusing my login, but it's doing it so fast!"

Tom said, "Search for 'open.*STDERR'."

Steve found this:

sub init {
 ($TMPFILE = $0) =~ s#(.*)/(.*)#/$1/.$2.$$/;
 open(STDERR, ">$TMPFILE") or die "Cannot save state: $!\n";
 open(STDIN, "<$0");
 while (<>) {
  print STDERR;
  last if /sub BEGIN/;
 }
}

The script had a single BEGIN block, after all of the rest of the code, and the POD documentation. The BEGIN block contained a single statement:

%hash = (
   'admin' => "83fcb0032cfb59c0327401d4fab13ea7",
   'joeb' => "afc7090be5e7b01296850e5436a88872",
   'alexp' => "b385c5b6899594b3aa220f34e493ab39",
   ...snip tens of thousands of lines...
);

On the bright side, it didn't take long for Steve to convert the hash into a tied hash.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!