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