App-Base
view release on metacpan or search on metacpan
lib/App/Base/Daemon/Supervisor.pm view on Meta::CPAN
use Moose::Role;
with 'App::Base::Daemon';
our $VERSION = '0.08'; ## VERSION
=head1 NAME
App::Base::Daemon::Supervisor - supervise daemon process
=head1 SYNOPSIS
package App::Base::Daemon::Foo;
use Moose;
with 'App::Base::Daemon::Supervisor';
sub documentation { return 'foo'; }
sub options { ... }
sub supervised_process {
my $self = shift;
# the main daemon process
while(1) {
# check if supervisor is alive, exit otherwise
$self->ping_supervisor;
# do something
...
}
}
sub supervised_shutdown {
# this is called during shutdown
}
=head1 DESCRIPTION
App::Base::Daemon::Supervisor allows to run code under supervision, it also
provides support for zero downtime reloading. When you run Supervisor based
daemon, the first process becomes a supervisor, it forks a child process which
invokes I<supervised_process> method that should contain the main daemon code.
Supervisor and worker connected with a socketpair. If the worker exits for some
reason, the supervisor detects it and starts a new worker after a small delay.
Worker should periodically call I<ping_supervisor> method, so it would be able
to detect the case when supervisor has been killed and exit.
If module needs hot reloading feature, it should redefine I<can_do_hot_reload>
method to return true value. In this case supervisor process sets a handler for
I<SIGUSR2> signal. When I<SIGUSR2> signal is received, supervisor starts a new
copy of the script via fork/exec, so this new copy runs a new code. New
supervisor starts a worker process and waits a signal that this new worker is
ready to do its job. To send that signal worker should invoke
I<ready_to_take_over> method. Then the new supervisor receives that signal, it
sends I<SIGQUIT> to the old supervisor and waits for it to exit. After the old
supervisor exited (normally new supervisor detects that because it can flock
the pid file), the new supervisor sends signal to the worker,
I<ready_to_take_over> method in worker returns, and worker can start doing its
job. If supervisor receives I<SIGUSR2> when it is already in the process of
reloading, it ignores this signal. If supervisor didn't get I<SIGQUIT> in 60
seconds after starting hot reloading process, it sends I<SIGKILL> to the new
supervisor and resumes normal work.
=cut
use namespace::autoclean;
use Socket qw();
use POSIX qw(:errno_h);
use Time::HiRes;
use IO::Handle;
=head1 REQUIRED METHODS
Class consuming this role must implement the following methods:
=cut
=head2 supervised_process
The main daemon subroutine. Inside this subroutine you should periodically
check that supervisor is still alive using I<ping_supervisor> method. If
supervisor exited, daemon should also exit.
=cut
requires 'supervised_process';
=head2 supervised_shutdown
This subroutine is executed then daemon process is shutting down. Put cleanup
code inside.
=cut
requires 'supervised_shutdown';
=head1 ATTRIBUTES
=cut
=head2 is_supervisor
returns true inside supervisor process and false inside supervised daemon
=cut
has is_supervisor => (
is => 'rw',
default => 1,
);
=head2 delay_before_respawn
how long supervisor should wait after child process exited before starting a
new child. Default value is 5.
=cut
has delay_before_respawn => (
is => 'rw',
default => 5,
);
=head2 supervisor_pipe
File descriptor of the pipe to supervisor
( run in 1.157 second using v1.01-cache-2.11-cpan-39bf76dae61 )