Perl Cookbook

Perl CookbookSearch this book
Previous: 14.4. Merging DBM FilesChapter 14
Database Access
Next: 14.6. Sorting Large DBM Files
 

14.5. Locking DBM Files

Problem

You need several concurrently running programs to have simultaneous access to a DBM file.

Solution

Either use the DBM implementation's locking mechanism if it has one, lock the file with flock, or use an auxiliary locking scheme as in Recipe 7.21.

Discussion

With SDBM or NDBM, you can't do much to lock the database itself. You must devise an auxiliary locking scheme using an extra lockfile.

GDBM uses the concept of readers and writers: either many readers or one solitary writer may have a GDBM file open at any given time. You specify whether you're a reader or a writer when you open it. This can be annoying.

Version 1 of Berkeley DB gives you access to the file descriptor of the open database, allowing you to flock it. The lock applies to the database as a whole, not to individual records. Version 2 implements its own full transaction system with locking.

Example 14.3 shows an example of locking a database using Berkeley DB. Run this repeatedly in the background to see locks granted in proper order.

Example 14.3: dblockdemo

#!/usr/bin/perl
# dblockdemo - demo locking dbm databases
use DB_File;
use strict;

sub LOCK_SH { 1 }                   # In case you don't have
sub LOCK_EX { 2 }                   # the standard Fcntl module.  You
sub LOCK_NB { 4 }                   # should, but who can tell
sub LOCK_UN { 8 }                   # how those chips fall?

my($oldval, $fd, $db, %db, $value, $key);

$key    = shift || 'default';
$value  = shift || 'magic';
$value .= " $$";

$db = tie(%db, 'DB_File', '/tmp/foo.db', O_CREAT|O_RDWR, 0666)
    or die "dbcreat /tmp/foo.db $!";
$fd = $db->fd;                      # need this for locking
print "$$: db fd is $fd\n";
open(DB_FH, "+<&=$fd")
    or die "dup $!";

unless (flock (DB_FH, LOCK_SH | LOCK_NB)) {
    print "$$: CONTENTION; can't read during write update!
                Waiting for read lock ($!) ....";
    unless (flock (DB_FH, LOCK_SH)) { die "flock: $!" }
}
print "$$: Read lock granted\n";

$oldval = $db{$key};
print "$$: Old value was $oldval\n";
flock(DB_FH, LOCK_UN);

unless (flock (DB_FH, LOCK_EX | LOCK_NB)) {
    print "$$: CONTENTION; must have exclusive lock!
                Waiting for write lock ($!) ....";
    unless (flock (DB_FH, LOCK_EX)) { die "flock: $!" }
}

print "$$: Write lock granted\n";
$db{$key} = $value;
$db->sync;  # to flush
sleep 10;

flock(DB_FH, LOCK_UN);
undef $db;
untie %db;
close(DB_FH);
print "$$: Updated db to $key=$value\n";

See Also

The documentation for the standard DB_File module, also in Chapter 7 of Programming Perl; Recipe 7.11; Recipe 16.12


Previous: 14.4. Merging DBM FilesPerl CookbookNext: 14.6. Sorting Large DBM Files
14.4. Merging DBM FilesBook Index14.6. Sorting Large DBM Files