IPC-SRLock
view release on metacpan or search on metacpan
lib/IPC/SRLock/Memcached.pm view on Meta::CPAN
package IPC::SRLock::Memcached;
use namespace::autoclean;
use Cache::Memcached;
use English qw( -no_match_vars );
use File::DataClass::Types qw( ArrayRef NonEmptySimpleStr Object );
use IPC::SRLock::Utils qw( Unspecified hash_from loop_until throw );
use Moo;
extends q(IPC::SRLock::Base);
# Public attributes
has 'lockfile' => is => 'ro', isa => NonEmptySimpleStr, default => '_lockfile';
has 'servers' => is => 'ro', isa => ArrayRef,
default => sub { [ 'localhost:11211' ] };
has 'shmfile' => is => 'ro', isa => NonEmptySimpleStr, default => '_shmfile';
# Private attributes
has '_memd' => is => 'lazy', isa => Object, reader => 'memd',
builder => sub { Cache::Memcached->new
( debug => $_[ 0 ]->debug,
namespace => $_[ 0 ]->name,
servers => $_[ 0 ]->servers ) };
# Private methods
my $_expire_lock = sub {
my ($self, $data, $key, @fields) = @_;
$self->log->error
( $self->_timeout_error
( $key, $fields[ 0 ], $fields[ 1 ], $fields[ 2 ] ) );
delete $data->{ $key };
return 0;
};
my $_unlock_share = sub {
my $self = shift; $self->memd->delete( $self->lockfile ); return 1;
};
my $_list = sub {
my $self = shift;
$self->memd->add( $self->lockfile, 1, $self->patience + 30 ) or return 0;
my $shm_content = $self->memd->get( $self->shmfile ) // {};
my $list = []; $self->$_unlock_share;
for my $key (sort keys %{ $shm_content }) {
my @fields = split m{ , }mx, $shm_content->{ $key };
push @{ $list }, { key => $key,
pid => $fields[ 0 ],
stime => $fields[ 1 ],
timeout => $fields[ 2 ] };
}
return $list;
};
my $_reset = sub {
my ($self, $args, $now) = @_; my $key = $args->{k}; my $pid = $args->{p};
$self->memd->add( $self->lockfile, 1, $self->patience + 30 ) or return 0;
my $shm_content = $self->memd->get( $self->shmfile ) // {};
my $lock; exists $shm_content->{ $key }
and $lock = $shm_content->{ $key }
and (split m{ , }mx, $lock)[ 0 ] != $pid
and $self->$_unlock_share
and throw 'Lock [_1] set by another process', [ $key ];
not delete $shm_content->{ $key } and $self->$_unlock_share
and throw 'Lock [_1] not set', [ $key ];
$self->memd->set( $self->shmfile, $shm_content ); $self->$_unlock_share;
return 1;
};
my $_set = sub {
my ($self, $args, $now) = @_;
my $key = $args->{k}; my $pid = $args->{p}; my $timeout = $args->{t};
$self->memd->add( $self->lockfile, 1, $self->patience + 30 ) or return 0;
my $shm_content = $self->memd->get( $self->shmfile ) // {}; my $lock;
if ($lock = $shm_content->{ $key }) {
my @fields = split m{ , }mx, $lock;
$fields[ 2 ] and $now > $fields[ 1 ] + $fields[ 2 ]
and $lock = $self->$_expire_lock( $shm_content, $key, @fields );
}
$lock and $self->$_unlock_share and return 0;
$shm_content->{ $key } = "${pid},${now},${timeout}";
$self->memd->set( $self->shmfile, $shm_content ); $self->$_unlock_share;
$self->log->debug( "Lock ${key} set by ${pid}" );
return 1;
};
# Public methods
sub list {
my $self = shift; return loop_until( $_list )->( $self, { k => 'dummy' } );
}
sub reset {
my ($self, @args) = @_; return loop_until( $_reset )->( $self, @args );
}
sub set {
my ($self, @args) = @_; return loop_until( $_set )->( $self, @args );
}
1;
__END__
=pod
=encoding utf-8
=head1 Name
IPC::SRLock::Memcached - Set / reset locks using libmemcache
=head1 Synopsis
use IPC::SRLock;
my $config = { type => q(memcached) };
my $lock_obj = IPC::SRLock->new( $config );
=head1 Description
Uses L<Cache::Memcached> to implement a distributed lock manager
=head1 Configuration and Environment
This class defines accessors for these attributes:
=over 3
=item C<lockfile>
Name of the key to the lock file record. Defaults to C<_lockfile>
( run in 0.469 second using v1.01-cache-2.11-cpan-71847e10f99 )