[prev] [thread] [next] [Date index for 2005/04/30]
Hi there !
I had a problem.
On some conditions my app (mod_perl1/mysql)
needed to apply complex update to a record and
make sure no one can read the record during the update.
I was using mysql's LOCK TABLES, but it seemed to be wrong solution.
In order to get things work, I needed to lock 10+ tables
(in different cases they are different, I'm bored with missing LOCKS),
some of them are searched/used heavily by the other parts of the app.
So I come to a app-side locking scheme,
which won't LOCK TABLES and queue locks in FIFO stack and
is suitable for mod_perl1.
Here are the questions
1. is it needed at all ? Should I better stick with lock tables ?
2. is there any (better) alternative. I failed to find one :(
3. is the SIGALRM the right signal to use in this situation (mostly to
break sleep)
4. Might anyone else need that code ? I could release it to CPAN,
if some of you consider it useful and point me to the right way of using
Test::More with the fork(). The code is quite generic and merely related
to Class::DBI,
so 'Class::DBI::Lock' isn't the right name I suppose.
Any thoughts are appreciated.
the code follows:
package Class::DBI::Lock;
use strict;
use warnings;
use base qw(Class::DBI);
use IPC::ShareLite qw(:lock);
use IPC::Signal qw(sig_num);
use Storable qw(freeze thaw);
our $VERSION = 0.01;
our $SHARE = new IPC::ShareLite(-key => 'CDBI',
-create => 1,
-destroy => 0);
__PACKAGE__->_store({});
sub lock {
my ($class, $id, $timeout) = @_;
die (__PACKAGE__ . "::lock() is a class method")
if ref $class;
my $sigalrm = 0;
local $SIG{ALRM} = sub { $sigalrm++ };
my $key = $class . $id;
# locking the share
$class->_lock();
my $data = $class->_fetch();
$data->{$key} ||= [];
push @{ $data->{$key} }, $$;
$class->_store($data);
# unlocking the share
# it is safe now to read from it
$class->_unlock();
# if it is first record in the queue
# then return immidiately
return 1 if @{ $data->{$key} } == 1;
# waiting for lock release
# which should come as SIGALRM
sleep $timeout;
# returning 1, since SIGALRM means lock is acquired
return 1 if $sigalrm;
# in case the lock timed out
# need to remove our pid from the queue
$class->_lock();
$data = $class->_fetch();
@{ $data->{$key} } = grep $_ != $$, @{ $data->{$key} };
$class->_store($data);
$class->_unlock();
return 0;
}
sub unlock {
my ($class, $id) = @_;
my $key = $class . $id;
$class->_lock();
my $data = $class->_fetch();
if ($data->{$key}[0] == $$) {
my $pid = shift @{ $data->{$key} };
$class->_store($data);
$class->_unlock();
kill(sig_num('ALRM'), $data->{$key}[0])
if $data->{$key}[0];
} else {
warn "Calling unlock() without lock()";
$class->_unlock();
}
}
sub _lock { $SHARE->lock( LOCK_EX ) }
sub _unlock { $SHARE->unlock() }
sub _store { $SHARE->store( freeze($_[1])) }
sub _fetch { thaw $SHARE->fetch() }
1;
|
Software row locking
|
Generated at 10:24 on 04 May 2005 by mariachi v0.52