NAME
MooX::Role::RunAlone - prevent multiple instances of a script from running
VERSION
Version v0.0.0_01
SYNOPSIS
# normal mode
package My::Script;
use strict;
use warnings;
use Moo;
with 'MooX::Role::RunAlone';
...
__END__ # or __DATA__
# deferred mode
package My::DeferedScript;
BEGIN {
$ENV{RUNALONE_DEFER_LOCK} = 1;
}
use strict;
use warnings;
use Moo;
with 'MooX::Role::RunAlone';
...
# exit immediately if we are not alone
__PACKAGE__->runalone_lock;
# do work
...
__END__ # or __DATA__
DESCRIPTION
This Role provides a simple way for a command line script that uses Moo
to ensure that only a single instance of said script is able to run at one time. This is accomplished by trying to obtain an exlusive lock on the sctript's __DATA__
or __END__
section.
The Role will send a message to STDERR
indicating a fatal error and then call exit(2)
if neither of those tags are present. This behavior can not be disabled and occurs when the Role is composed.
Normal Locking
If one of the aforementioned tags are present, an attempt is made (via runalone_lock()
) to obtain an exclusive lock on the tag's file handle using flock
with the LOCK_EX
and LOCK_NB
flags set. A failure to obtain an exclusive lock means that another instance of the composing script is already executing. A message will be sent to STDERR
indicating a fatal condition and the Role will call exit(1)
.
The Role does a void return if the call to flock
is successful.
Deferred Locking
The composing script can tell the Role that it should not immediately call runalone_lock()
but should defer this action to the script. This is done like this:
BEGIN {
$ENV{RUNALONE_DEFER_LOCK} = 1;
}
The Role will return immediately after checking to see whether or not one of the tags are present instead of trying to get the lock.
Note: It is the responsibility of the composing script to call runalone_lock()
at an appropriate time.
Fatal Messages
There are two messages that are sent to STDERR
that cannot usually be suppressed during normal startup:
- "FATAL: No __DATA__ or __END__ tag found"
- "FATAL: A copy of '$0' is already running"
-
Note: this can be suppressed in deferred locking mode. See the
noexit
argument torunalone_lock
.
METHODS
Only one method is currently exposed, but it is the workhorse when deferred mode is used.
runalone_lock
This method attempts to get an exclusive lock on the __END__
or __DATA__
handle that was located during the Role's startup. A composing script may immulate normal operation by simply calling this method with no arguments at the desired time. It will either return a Boolean true
if successful, or call exit
with a status code of 1
upon failure.
The method's behavior can be modified by four arguments. This allows the composing script to enable lock retries or perform custom operations as needed. (Note: the method is implemented as a class method
and may be called with either a class name or a composing object.
Examples:
# basic call with retries and progress messages enabled
my $locked = __PACKAGE__->runalone_lock(
attemtps => 3,
interval => 2,
verbose => 1,
);
# basic call with retries enabled, but silent
my $locked = __PACKAGE__->runalone_lock(
attemtps => 3,
interval => 2,
);
# make a single (silent) attempt, but return to the caller instead of
# exiting if the attempt fails. also suppresses any failure message.
my $locked = __PACKAGE__->runalone_lock(
noexit => 1,
);
Arguments
Invalid values will cause an exception to be thrown via croak
so the offending caller might be more easily identified.
- noexit (Boolean, default: 0)
-
Controls whetern the method will call
exit( 1 )
or return a Booleanfalse
upon failure. Settin ittrue
allows the composing script to take additional/different actions.Note: if set, it will also suppress the fatal error message associated with failure to obtain a lock.
- attempts (Integer, must satisfy 0 < N < 10; default: 1)
-
Set how many attempts will be made to get a lock on the handle in question.
- interval (Integer, must satisfy 0 < N < 10, default: 1)
-
Sets how long to
sleep
between attempts ifattempts
is greater than one. - verbose (Boolean, default: 0)
-
Enables progress messages on STDERR if set. The following messages can appear:
"attemting to lock <data pkg> ... failed. Retrying <N> more time(s)" "attemting to lock <data pkg> ... SUCCESS"
Returns
1
if the lock was obtained.
The method will either call exit(1)
or return a Boolean false
depending upon the value of the noexit
argument.
PRIVATE METHODS
There are a few internal methods that are not documented here. All such methods begin with the string _runalone_
in an attempt to avoid namespace collision.
CAVEATS
[NB: This section has been copied from Sys::RunAlone
]
Symlinks
Execution of scripts that are (sym)linked to another script, will all be seen as execution of the same script, even though the error message will only show the specified script name. This could be considered a bug or a feature.
Changing a Running Script
If you change the script while it is running, the script will effectively lose its lock on the file. causing any subsequent run of the same script to be successful, therefor causing two instances of the same script to run at the same time (which is what you wanted to prevent by using Sys::RunAlone in the first place). Therefore, make sure that no instances of the script are running (and won't be started by cronjobs while making changes) if you really want to be 100% sure that only one instance of the script is running at the same time.
ACKNOWLEDGMENTS
This Role relies upon a principle that was first proposed (so far as this author knows) by Randal L. Schwartz (MERLYN), and first implemented by Elizibeth Mattijsen (ELIZABETH) in Sys::RonAlone. That module has been extended by PERLANCAR in Sys::RunAlone::Flexible with suggestions by this author.
SEE ALSO
Sys::RunAlone, Sys::RunAlone::Flexible
AUTHOR
Jim Bacon, <boftx at cpan.org>
BUGS
Please report any bugs or feature requests to bug-moox-role-runalone at rt.cpan.org
, or through the web interface at https://rt.cpan.org/NoAuth/ReportBug.html?Queue=MooX-Role-RunAlone. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc MooX::Role::RunAlone
You can also look for information at:
RT: CPAN's request tracker (report bugs here)
https://rt.cpan.org/NoAuth/Bugs.html?Dist=MooX-Role-RunAlone
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
Search CPAN
LICENSE AND COPYRIGHT
This software is Copyright (c) 2020 by Jim Bacon.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)