Reloading Perl Modules in a Server ( using Perl )
Just this week I have been working on a problem on how to reload a Perl module
in a continuous process eg. when you have changed the code in a loaded module,
you want the parent process to detect this, and then dump the old copy in
favour of the new.
What I didn’t know was that I would end up deep in Perl Guts, and that it
isn’t as bad as it sounds.
Scenario: we have an Application server that processes business functions.
This server is the back office for web servers that are possibly operational 24/7.
When we deploy new code we want to be able to do so with minimal down time
( hopefully none ), so just like with Apache you can use
Apache::Reload
to refresh mod_perl Modules, We wanted to be able to do the same.
This boiled down to a couple of things:
- Register a Perl package with some controling agent to determine its baseline modification date
- Test the current timestamp of the modules file before each incarnation/execution, and reload if necessary.
Baseline
The first thing I needed was the filename of the package, to do this I had to turn the package name into a file name and then find that file in the available paths in @INC. when looping at @INC you have to be very careful not to modify it, so I copied it first.
$pkg =~ s/::/\//g;
$pkg .= '.pm';
if ( -f $pkg ){
$files->{$mod} = $pkg;
return $pkg;
} else {
my @incy = ( @INC );
foreach ( @incy ){
if ( -f $_.'/'.$mod.'.pm' ){
$files->{$mod} = abs_path($_).'/'.$mod.'.pm';
return $files->{$mod};
}
}
return undef;
}
After that I need the to create a cache of the timestamp of the file
return (stat($file) )[9];
Test and Reload
Now that I can locate the module and have a baseline to check against, I new to check the timestamp repeatedly ( as above ), and then remove the module from the symbol table, and reload it if the timestamp has been updated.
delete $INC{$file};
my $pkg = $mod;
# expand to full symbol table name if needed
$pkg .= '::' unless $pkg =~ /::$/;
no strict "refs";
my $symtab = *{$pkg}{HASH};
return unless defined $symtab;
# free all the symbols in the package
foreach my $name (keys %$symtab) {
debug("undef: $pkg$name");
undef %{$pkg . $name};
}
# delete the symbol table
undef %{"$mod"};
As is often the case with Perl - here in lies the little bit of magic that
makes it all happen. I delete the entry in the %INC hash that holds the directory
of loaded modules. Then I figure out the symbol table entry for the package.
With this name I can lookup the symbol table and find all the related entries
to the package ie. the names of all methods of the package. these are:
undef %{$pkg . $name}; undefined, and then the
package as a whole is undefined.
And that’s it! The code can be found in Reload.pm
All the credit goes to the respective authors of Apache::Session, and Symbol,
where I shamelessly plaigarised all the ideas from. :-)
Posted by PiersHarding at May 3, 2002 8:33 PM