Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I have a pernicious bug that completely baffles me: in order to optimize a particular piece of mod_perl code, we decided to fork off a part of the code and return the user to a status page while the child does the crunching in the background. So our code now looks something like this:

defined(my $pid = fork) or die "cannot fork. $!\n"; if ($pid){ warn "i'm the parent"; } else { &do_stuff(@args); CORE::exit(0); } return;

now, &do_stuff creates its own dbi handle (DBD::Oracle), and does a bunch of operations with that. the parent returns and the next page (the success/status page) does more operations with its own pre-declared dbi handles.

at this point what happens is that oracle throws one of numerous exceptions: "fatal two-task communication protocol error", "user requested cancel of current operation", "requested INSTANCE_NUMBER is busy", "end-of-file on communication channel", etc, etc.... there seems to be no consistency with which error you receive.

my theory is that a race condition exists between the parent and child processes, somehow fighting over control of the db handles, even though the child handle is created after the fork. How do i get around this?

The system:

i've tried adding Apache::DBI->connect_on_init to no avail, as well as POSIX::setsid. can anyone help me?

Replies are listed 'Best First'.
Re: forking w/ mod_perl and DBI
by andreychek (Parson) on Oct 13, 2001 at 05:18 UTC
    There was an excellent article on this very subject in Apache Today titled Improving mod_perl Driven Site's Performance -- Part VI: Forking and Executing Subprocesses from mod_perl.

    In the very first paragraph, they point out, using fairly strong language:

    It's desirable to avoid forking under mod_perl. Since when you do, you are forking the entire Apache server, lock, stock and barrel. Not only is your Perl code and Perl interpreter being duplicated, but so is mod_ssl, mod_rewrite, mod_log, mod_proxy, mod_speling (it's not a typo!) or whatever modules you have used in your server, all the core routines, etc.

    They also point out that forking a process might be trying to do the "wrong thing". In the case that you want to send information to the browser, and then do some processing afterwards (sort of sounds like your case), that there is a cleanup handler. You can do that with something like:
    my $r = shift; $r->register_cleanup(\&do_stuff); sub do_cleanup{ #some clean-up code here };
    In this case, this Apache process isn't freed up to handle another request, but it also isn't causing you the overhead of another fork.

    However, they do say that there are some cases where forking is necessary, and take you through proper steps to do so.

    Good luck!
    -Eric
Re: forking w/ mod_perl and DBI
by perrin (Chancellor) on Oct 13, 2001 at 04:22 UTC
    The problem is most likely that you are not really creating a new database connection. You have Apache::DBI in there, so there are persistent connections being carried across the fork and when you think you're opening a new one you are really just pulling an old one out of the Apache::DBI cache. You can test this theory by turning on Apache::DBI's debugging.

    You can force the creation of a new handle by adding an extra, useless parameter to your connect arguments hash. It will be ignored by DBI, but it will prevent your connection from matching the established one in the Apache::DBI cache.

Re: forking w/ mod_perl and DBI
by merlyn (Sage) on Oct 13, 2001 at 04:06 UTC