| home / programming / perl / mod_perl / chap6 / 1 | [previous] [next] |
|
|
Let's return to our original example and proceed with the second mystery we noticed. Why have we seen inconsistent results over numerous reloads?
What happens is that each time the parent process gets a request
for the page, it hands the request over to a child process. Each child process
runs its own copy of the script. This means that each child process has its
own copy of $counter, which will increment independently
of all the others. So not only does the value of each $counter
increase independently with each invocation, but because different children
handle the requests at different times, the increment seems to grow inconsistently.
For example, if there are 10 httpd children, the first
10 reloads might be correct (if each request went to a different child). But
once reloads start reinvoking the script from the child processes, strange results
will appear.
Moreover, requests can appear at random since child processes don't always run the same requests. At any given moment, one of the children could have served the same script more times than any other, while another child may never have run it.
Stas and his boss didn't discover the aforementioned problem with the user registration system before going into production because the error_log file was too crowded with warnings continuously logged by multiple child processes.
To immediately recognize the problem visually (so you can see incorrect results), you need to run the server as a single process. You can do this by invoking the server with the -X option:
panic% httpd -X
Since there are no other servers (children) running, you will get the problem report on the second reload.
Enabling the warnings mode (as explained
earlier in this chapter) and monitoring the error_log
file will help you detect most of the possible errors. Some warnings can become
errors, as we have just seen. You should check every reported warning and eliminate
it, so it won't appear in error_log again. If your
error_log file is filled up with hundreds of lines
on every script invocation, you will have difficulty noticing and locating real
problems, and on a production server you'll soon run out of disk space if your
site is popular.
If your service consists of a single script, you will probably
have no namespace problems. But web services usually are built from many scripts
and handlers. In the following sections, we will investigate possible namespace
problems and their solutions. But first we will refresh our understanding of
two special Perl variables, @INC and %INC.
Perl's @INC array is like the PATH
environment variable for the shell program. Whereas PATH
contains a list of directories to search for executable programs, @INC
contains a list of directories from which Perl modules and libraries can be
loaded.
When you use( ), require(
), or do( ) a filename or a module, Perl
gets a list of directories from the @INC variable
and searches them for the file it was requested to load. If the file that you
want to load is not located in one of the listed directories, you must tell
Perl where to find the file. You can either provide a path relative to one of
the directories in @INC or provide the absolute
path to the file.
Perl's %INC hash is used to cache
the names of the files and modules that were loaded and compiled by use(
), require( ), or do(
) statements. Every time a file or module is successfully loaded, a new
key-value pair is added to %INC. The key is the
name of the file or module as it was passed to one of the three functions we
have just mentioned. If the file or module was found in any of the @INC
directories (except "."), the filenames
include the full path. Each Perl interpreter, and hence each process under mod_perl,
has its own private %INC hash, which is used to
store information about its compiled modules.
Before attempting to load a file or a module with use(
) or require( ), Perl checks whether it's
already in the %INC hash. If it's there, the loading
and compiling are not performed. Otherwise, the file is loaded into memory and
an attempt is made to compile it. Note that do( )
loads the file or module unconditionally--it does not check the %INC
hash. We'll look at how this works in practice in the following examples.
First, let's examine the contents of @INC
on our system:
panic% perl -le 'print join "\n", @INC'/usr/lib/perl5/5.6.1/i386-linux/usr/lib/perl5/5.6.1/usr/lib/perl5/site_perl/5.6.1/i386-linux/usr/lib/perl5/site_perl/5.6.1/usr/lib/perl5/site_perl.
Notice . (the current directory) as the last directory in the list.
Let's load the module strict.pm and
see the contents of %INC:
panic% perl -le 'use strict; print map {"$_ => $INC{$_}"} keys %INC'strict.pm => /usr/lib/perl5/5.6.1/strict.pm
Since strict.pm was found in the
/usr/lib/perl5/5.6.1/ directory and /usr/lib/perl5/5.6.1/
is a part of @INC, %INC
includes the full path as the value for the key strict.pm.
Let's create the simplest possible module in /tmp/test.pm:
1;
This does absolutely nothing, but it returns a true value when loaded, which is enough to satisfy Perl that it loaded correctly. Let's load it in different ways:
panic% cd /tmppanic% perl -e 'use test; \print map { "$_ => $INC{$_}\n" } keys %INC'test.pm => test.pm
Since the file was found in . (the directory the code was executed
from), the relative path is used as the value. Now let's alter @INC
by appending /tmp:
panic% cd /tmppanic% perl -e 'BEGIN { push @INC, "/tmp" } use test; \print map { "$_ => $INC{$_}\n" } keys %INC'test.pm => test.pm
Here we still get the relative path, since the module was found first relative to ".". The directory /tmp was placed after . in the list. If we execute the same code from a different directory, the "." directory won't match:
panic% cd /panic% perl -e 'BEGIN { push @INC, "/tmp" } use test; \print map { "$_ => $INC{$_}\n" } keys %INC'test.pm => /tmp/test.pm
so we get the full path. We can also prepend the path with unshift(
), so that it will be used for matching before ".".
We will get the full path here as well:
panic% cd /tmppanic% perl -e 'BEGIN { unshift @INC, "/tmp" } use test; \print map { "$_ => $INC{$_}\n" } keys %INC'test.pm => /tmp/test.pm
The code:
BEGIN { unshift @INC, "/tmp" }
can be replaced with the more elegant:
use lib "/tmp";
This is almost equivalent to our BEGIN
block and is the recommended approach.
These approaches to modifying @INC
can be labor intensive: moving the script around in the filesystem might require
modifying the path.
| home / programming / perl / mod_perl / chap6 / 1 | [previous] [next] |
Created: March 27 2003
Revised: July 23, 2003
URL: http://webreference.com/programming/perl/mod_perl/chap6/1