IPC-ConcurrencyLimit
view release on metacpan or search on metacpan
lib/IPC/ConcurrencyLimit/WithLatestStandby.pm view on Meta::CPAN
sub release_lock {
my $self = shift;
return $self->{locker}[0]->release_lock(@_);
}
sub lock_id {
my $self = shift;
return $self->{locker}[0]->lock_id(@_);
}
sub heartbeat {
my $self = shift;
return $self->{locker}[0]->heartbeat;
}
1;
__END__
=head1 NAME
IPC::ConcurrencyLimit::WithLatestStandby - IPC::ConcurrencyLimit with latest started working as standby
=head1 SYNOPSIS
use IPC::ConcurrencyLimit::WithLatestStandby;
sub run {
my $limit = IPC::ConcurrencyLimit::WithLatestStandby->new(
type => 'Flock', # default, and currently only supported type
path => '/var/run/myapp',
);
if ($limit->get_lock) {
# Got one of the worker locks (ie. number $id)
do_work();
} else {
print "Failed to get a lock, replaced by a newer standby worker.\n";
}
# lock released with $limit going out of scope here
}
run();
exit();
=head1 DESCRIPTION
This module behaves much the same as L<IPC::ConcurrencyLimit> when configured
for a single lock, with the exception of what happens when the lock is already
held by another process. Instead of simply returning false, the lock will block
and the worker will go into a "standby queue" waiting to acquire the master lock.
If the master lock is released then the next worker in the queue takes over,
and additionally workers in standby mode are managed such that older standby
workers "bow out" when they detect a newer standby worker is available to take
over waiting. When configured and used properly you are guaranteed to get the
most recent worker "taking over" processing every time.
When using this module at any one time there may be up to four workers alive.
One master process, one primary standby, one secondary standby, and one new
standby, each one corresponding to one of four locks, numbered 0 to 3. Each
new worker starts by acquiring the rightmost lock, #3, and then tries to
"move left" by also acquiring the preceding lock, which when successful leads
to it dropping its old lock. When the worker ends up holding the master lock #0
it can do work. At the same time the holder of the primary standby lock #1
also polls to see if the secondary standby lock #2 is held. If it is then the holder
of the lock #1 exits, allowing the secondary standby lock to "move left" and
take over standby responsibility.
So long as the polling time is sufficiently faster than the frequency with which
new processes are started you will generally see the most recently started worker
take over from the master. IOW, if you use the default poll time of 1 second
and you start new workers minutely you can be confident that the new worker will
be reasonably "fresh".
=head2 Options
C<IPC::ConcurrencyLimit::WithLatestStandby> does not accept all the regular
C<IPC::ConcurrencyLimit> options. Currently it is restricted to using
Flock internally, and max_procs may only be set to 1. There are also additional
parameters which may be supplied.
=over 4
=item poll_time
=item interval
This is the base amount of time that we should wait between checking if a lock
is still held by another process. It is not the actual time that we may wait,
which may be anything from 0 to 2 times the stated value, chosen randomly each
time we sleep. Fractional seconds may be specified.
Note that new standby workers, and secondary standby workers poll at a faster
rate than this value. The actual time is determined by $poll_time/$lock_id,
meaning that a secondary standby worker polls twice as often as a primary standby
worker. (For the lock algorithm to work properly we need to ensure that the
poll times associated with each lock differ, and are not synchronized).
=item retries
Specify the maximum number of times we will attempt to get a lock. Note that
due to the random sleep in our wait-and-retry loop this cannot be cleanly
mapped to time. For that you can use the C<timeout> setting, with or instead
of the retries logic.
Addtionally one can provide a code reference to control the retry logic.
The sub will be called with four arguments:
my $should_retry= $retry_sub->($tries, $lock_tries, $elapsed, $lock_elapsed);
$tries is an integer which is incremented every time we try to acquire a lock
internally, $lock_tries is similar but every time we successfully acquire a
lock and "move left" it is reset to 0. $elapsed is the amount of time in secs
(with fractional part) that has elapsed since we acquired the initial "new
standby worker" lock, and $lock_elapsed is the amount of time since we last
acquried a lock, with the timer being reset when we "move left". If the retry
sub returns true then we retry, if it returns false then we exit out of the
lock-wait loop.
( run in 3.014 seconds using v1.01-cache-2.11-cpan-437f7b0c052 )