perlapachehandlermod-perlmod-perl2

use methods in different modules in mod_perl handler


I want to share a variable between different perl modules. So I created a perl module called MyCache.pm which saves the variable (in my case a hash variable):

package PerlModules::MyCache;
my %cache = ();
sub set {
  my ($key, $value) = @_;
  $cache{$key} = $value;
}
sub get {
  my ($key) = @_;
  return $cache{$key};
}

Now I have two handlers. The one handler will call the set method and the other one will call the get method to access the information.

package PerlModules::MyCacheSetter;
use Apache2::RequestRec();
use Apache2::RequestIO();
use Apache2::Const -compile => qw(OK);
use PerlModules::MyCache;
sub handler {
  my $r = shift;
  PerlModules::MyCache::set('test1', "true");
  PerlModules::MyCache::set('test2', "false");
  PerlModules::MyCache::set('test3', "true");
  return Apache2::Const::OK;
}

And here is the getter handler:

package PerlModules::MyCacheGetter;
use Apache2::RequestRec();
use Apache2::RequestIO();
use Apache2::Const -compile => qw(OK);
use PerlModules::MyCache;
sub handler {
  my $r = shift;
  $r->print(PerlModules::MyCache::get('test1'));
  $r->print(PerlModules::MyCache::get('test2'));
  $r->print(PerlModules::MyCache::get('test3'));
  return Apache2::Const::OK;
}

Now I've configured apache (via http.conf) to access these perl modules. I run the setter handler and then the getter, but there was no output.

In the error.log there are now some entries:

Use of uninitialized value in subroutine entry at ../MyCacheGetter.pm line 14.
Use of uninitialized value in subroutine entry at ../MyCacheGetter.pm line 15.
Use of uninitialized value in subroutine entry at ../MyCacheGetter.pm line 16.

This lines are the three calls of the get method. So what am I doing wrong? How can I fix the problem and share my cache variable between different handlers?


Solution

  • Your cache will only exist for the lifetime of a given Apache child process. If you want other processes to see it, you'll need to store it somewhere they can all get at it.

    This is untested, but you can get the general idea: (Now tested). EDIT: OK, it seems like you can get some issues with Storable depending on what perl version and Storable version you're running. I've replaced Storable with Data::Serialize in my example. I've also added a line to the get/set methods so that either the -> or :: syntax can be used.

    package PerlModules::MyCache;
    
    use IPC::ShareLite qw/:lock/;
    use Data::Serializer;
    use 5.10.0;
    
    my $key = 1234; # Your shared memory key (you set this!)
    
    my $ipc = IPC::ShareLite->new(
        -key     => $key,
        -create  => 'yes',
        -destroy => 'no'
    );
    
    my $ser = Data::Serializer->new(
        serializer => 'Data::Dumper'
    );
    
    sub set {
        shift @_ if $_[0] eq __PACKAGE__;
        my ($key, $value) = @_;
        $ipc->lock(LOCK_EX);
        my $frozen; eval { $frozen = $ipc->fetch; };
        my $cache = defined($frozen) ? $ser->thaw($frozen) : {};
        $cache->{$key} = $value;
        $ipc->store($ser->freeze($cache));
        $ipc->unlock;
        return $value;
    }
    
    sub get {
        shift @_ if $_[0] eq __PACKAGE__;
        my ($key) = @_;
        my $frozen; eval { $frozen = $ipc->fetch; };
        my $cache = defined($frozen) ? $ser->thaw($frozen) : {};
        return $cache->{$key};
    }
    
    sub clear {
        shift @_ if $_[0] eq __PACKAGE__;
        $ipc->store($ser->freeze({}));
        return {};
    }
    
    1;
    

    You might want to run PerlModules::MyCache->clear once before you test to ensure the correct structure of the cache storage.