Last active
January 11, 2021 10:51
-
-
Save simonecesano/5da478e8c9f52dfa70d5e5cfe101eb10 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package Mojolicious::Plugin::Minion::Starter; | |
use Mojo::Base 'Mojolicious::Plugin', -signatures; | |
use Mojo::File qw/path/; | |
use Mojo::Util qw/dumper/; | |
use Mojo::IOLoop::Subprocess; | |
use Proc::ProcessTable; | |
has processes => sub { Proc::ProcessTable->new } ; | |
has table => sub { [] }; | |
has app => has app => sub { Mojo::Server->new->build_app('Mojo::HelloWorld') }; | |
use Time::HiRes qw/time/; | |
sub register { | |
my ($self, $app, $config) = (@_); | |
$self->app($app); | |
$self->start_reaper; | |
$app->hook(before_server_start => $self->before_server_start_hook($config)); | |
} | |
sub start_reaper { | |
my $self = shift; | |
my $config = shift; | |
my $tick = $config->{timeout} || 5; | |
$self->app->log->info(sprintf("Will reap zombie workers every %.2f seconds", $tick)); | |
Mojo::IOLoop->recurring($tick => $self->reaper($config)); | |
} | |
sub reaper { | |
my $self = shift; | |
my $config = shift; | |
sub { | |
my $start = time; | |
$self->check; | |
my $reaped = $self->reap; | |
if ($reaped && $config->{log}) { | |
$self->app->log->info(sprintf "Reaped %d zombies in %.4f seconds", $reaped, time - $start); | |
} | |
} | |
} | |
sub before_server_start_hook { | |
my $self = shift; | |
my $spawn = (shift() || {})->{spawn}; | |
$spawn //= 1; $spawn = $spawn <= 0 ? 1 : $spawn; | |
sub { | |
my $ppid = $$; | |
my $server_pid = 0; | |
$self->app->log->info(sprintf("Preparing to spawn %d processes", $spawn)); | |
for (0..($spawn - 1)) { | |
my $subprocess = Mojo::IOLoop::Subprocess->new; | |
$subprocess->run( | |
sub ($subprocess) { | |
$self->app->minion->worker->run; | |
}, | |
sub ($subprocess, $err, @results) { | |
$self->app->log->info($err); | |
} | |
); | |
$subprocess->on(spawn => sub ($subprocess) { | |
my $pid = $subprocess->pid; | |
$self->app->log->info(sprintf("Spawned Minion worker with pid %d and ppid %d", $subprocess->pid, $$)); | |
}); | |
$subprocess->on(cleanup => sub ($subprocess) { $self->app->log->info("Process $$ is about to exit") }); | |
} | |
} | |
} | |
sub check { | |
my $self = shift; | |
my $all = shift; | |
$self->table( [ map { $_ } grep { path($_->cmndline)->basename eq $0 } @{$self->processes->table} ]); | |
} | |
sub chain { | |
my $self = shift; | |
my %chain; | |
for (@{$self->table}) { | |
$chain{$_->ppid} = [] unless $chain{$_->ppid}; | |
push @{$chain{$_->ppid}}, $_->pid; | |
} | |
return \%chain; | |
} | |
sub descendants { | |
my $self = shift; | |
my $pid = shift || $$; | |
my $desc = shift; | |
my $chain = $self->chain; | |
push @$desc, getppid if $pid == $$; | |
push @$desc, $pid if $pid == $$; | |
for (@{$chain->{$pid}}) { | |
push @$desc, $_; | |
$self->descendants($_, $desc); | |
} | |
return $desc; | |
} | |
sub zombies { | |
my $self = shift; | |
my @processes = @{$self->table}; | |
my %legit; | |
@legit{@{$self->descendants}} = @{$self->descendants}; | |
return [ map { $_->pid } grep { !$legit{$_->pid} } @processes ]; | |
} | |
sub reap { | |
my $self = shift; | |
my @zombies = @{$self->zombies}; | |
return kill 'HUP', @zombies; | |
} | |
1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is an attempt at having Minion worker processes start automatically when the Mojolicious app starts.It works by starting worker subprocesses, and regularly reaping those that aren't connected to the app anymore.
I haven't it tested thoroughly so use at your own risk, but it seems to work ok both with morbo and hypnotoad.