If you know that your code accesses the DBM file in read-only mode and you want to gain the maximum data-retrieval speed, you should tie the DBM file during server startup and register code in the child initialization stage that will tie the DBM file when the child process is spawned.

Consider the small test module in Example 19-2.

Example 19-2. Book/DBMCache.pm

package Book::DBMCache;

use DB_File;
use Fcntl qw(O_RDONLY O_CREAT);

use vars qw(%dbm);

sub init {
    my $filename = shift;
    tie %dbm, 'DB_File', $filename, O_RDONLY|O_CREAT,
        0660, $DB_BTREE or die "Can't tie $filename: $!";
}
1;

This module imports two symbols from the Fcntl package that we will use to tie the DBM file. The first one is O_RDONLY, as we want the file to be opened only for reading. It is important to note that in the case of the tie( ) interface, nothing prevents you from updating the DBM file, even if the file was tied with the O_RDONLY flag. The second flag, O_CREAT, is used just in case the DBM file wasn't found where it was expected—in this case, an empty file will be created instead, since otherwise tie( ) will fail and the code execution will be aborted.

The module specifies a global variable, %dbm, which we need to be global so that we can access it directly from outside of the Book::DBMCache module. Alternatively, we could define this variable as lexically scoped to this module and write an accessor (method), which would make the code cleaner. However, this accessor would be called every time we wanted to read some value.

When Book::DBMCache::init( ) is called with a path to the DBM file as its argument, the global variable %dbm is tied to this file. We want the tie operation to happen before the first request is made, so we do it in the ChildInitHandler code executed from startup.pl:

use Book::DBMCache;
Apache->push_handlers(PerlChildInitHandler => sub {
                        Book::DBMCache::init("/tmp/foo.db");
                    });

Assuming /tmp/foo.db is already populated with data, we can now write the test script shown in Example 19-3.

Example 19-3. test_dbm.pl

use Book::DBMCache;
use strict;

my $r = shift;
$r->send_http_header("text/plain");

my $foo = exists $Book::DBMCache::dbm{foo} ? $Book::DBMCache::dbm{foo} : '';
print "The value of foo: [$foo]";

When this is executed as an Apache::Registry script (assuming the DBM file was populated with the foo, bar key/value pair), we will see the following output:

The value of foo: [bar]

There's an easy way to guarantee that a tied hash is read-only: use a subclass of the tie module you're using that prevents writing. For example, you can subclass DB_File as follows:

package DB_File::ReadOnly;

use strict;
require DB_File;
$DB_File::ReadOnly::ISA = qw(DB_File);

sub STORE  {  }
sub DELETE {  }
sub CLEAR  {  }

1;

As you can see, the methods of the tie( ) interface that can alter the DBM file are overriden with methods that do nothing. Of course, you may want to use warn( ) or die( ) inside these methods, depending on how you want to flag writes. Any attempts to write probably should be considered serious problems.

Now you can use DB_File::ReadOnly just like you were using DB_File before, but you can be sure that the DBM file won't be modified through this interface.