Hi,

Well, the problem was my fault.  :/  I had a bug in a generic base class
I use that makes it easier to build classes that work both inside and
outside mod_perl.

For those of you who are interested, this solutions works well.  By
using PerlChildInit handler to create a thread to maintain a shared
global hash of hashes with some small portion of a database, and some
information acquired with XML::RPC, my systems perform much better.

We were having problems with availability and performance of the
external data sources (eg MySQL or remote XML::RPC servers) that would
cause our apache instances to wait around timing out for each request.

Yay.

Here is a demonstration module for those of interest:

package TestPerlChildInit;

use strict;

use lib         '/opt/whenu/lib/whenu-perl-lib';

use threads;
use threads::shared;

use vars                qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
use vars                qw($DEBUG *DBG $CFG);
use vars                qw(%SHARED);

@ISA =                  qw(Exporter);
@EXPORT =               qw();
@EXPORT_OK =            qw();
%EXPORT_TAGS =          (':DEFAULT' => [qw()],
                         ':handler' => [qw()]);


BEGIN {
        use mod_perl;
        use Apache2;
        use Apache::Const       qw(:common :http);
        use Apache::RequestRec  qw();
        use Apache::RequestIO   qw();
        use Apache::Connection  qw();
        use Apache::ServerUtil  qw();
        use Apache::Module      qw();
        use Apache::Util        qw();
        use Apache::URI         qw();
        use Apache::Log         qw();
        use APR::OS             qw();
        use APR::Table          qw();
        share(%SHARED);
        $SHARED{'test'} = &share({});
        $SHARED{'test'}->{'count'} = 1;
        my $res = Apache->server->push_handlers(PerlChildInitHandler => 
\&mod_perl_ChildInitHandler);
        print STDERR "Testing[$$]: Installed ChildInitHandler result '$res'\n";
}

sub mod_perl_ChildInitHandler {
        print STDERR "mod_perl_ChildInitHandler\n";
        ##  Start a thread to restart other thread...
        threads->new( sub { 
        while(1) {
                my $ovs = threads->new(\&overseer);
                print STDERR "Testing[$$]: Started overseer thread\n";
                $ovs->join();
                print STDERR "Testing[$$]: Joined overseer thread (probably 
bad)\n";
                ## Add backoff for spawning too quickly etc.
        }
        })->detach;
        return &Apache::OK;
}

sub overseer {
        print STDERR "Testing[$$]->", threads->self->tid, " Overseer 
Startup...\n";
        while(sleep 2) {
                lock(%{$SHARED{'test'}});
                print STDERR "Testing[$$]->", threads->self->tid, ": 
\$SHARED{'test'}->{'count'} = $SHARED{'test'}->{'count'} \n";
                ## here is where you can do more interesting things such as
                ## get data from databases, external sources, or update them.
        }
}

sub handler : method {
        my $class = shift;
        my $r = shift;
        lock(%{$SHARED{'test'}});
        $SHARED{'test'}->{'count'}++;
        $r->no_cache();
        $r->err_headers_out->{"Expires"} =  "Sat, 1 Jan 2000 00:00:00 GMT";
        $r->err_headers_out->{"Pragma"} = "no-cache";
        $r->err_headers_out->{"Cache-Control"} = "no-cache";
        $r->err_headers_out->{"Location"} = 'http://www.google.com';
        $r->status(&Apache::REDIRECT);
        $r->rflush();
        return &Apache::OK;
        
}


On Mon, 2005-01-17 at 13:59 -0500, Richard F. Rebel wrote:
> Another good idea... :)
> 
> But I am transfixed by this problem...  I can't seem to get each forked
> apache server to have both a shared global hash between all cloned
> interpreters, *and* one thread in each process that runs in the
> background doing housekeeping.  I can think of numerous things that this
> would be useful for.
> 
> I know I am close, but I can't seem to quite grasp what I am missing.  I
> thought PerlChildInit's were called for each forked child from it's
> first/main interpreter (the one that all the others are cloned from).
> 
> 
> On Mon, 2005-01-17 at 13:59 -0500, Perrin Harkins wrote:
> > On Mon, 2005-01-17 at 11:25 -0500, Richard F. Rebel wrote:
> > > Unfortunately, it's high volume enough that it's no longer possible to
> > > keep these counters in the databases updated in real time.  (updates are
> > > to the order of 1000's per second).
> > 
> > I would just use BerkeleyDB for this, which can easilly keep up, rather
> > than messing with threads, but I'm interested in seeing if your
> > threading idea will work well.
> > 
> > > * A overseer/manager thread that wakes up once every so often and
> > > updates the MySQL database with the contents of the global shared hash.
> > 
> > Rather than doing that, why not just update it from a cleanup handler
> > every time the counter goes up by 10000 or so?  Seems much easier to me.
> > 
> > - Perrin
> > 
-- 
Richard F. Rebel

cat /dev/null > `tty`

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to