Gearman-WorkerSpawner
view release on metacpan or search on metacpan
lib/Gearman/WorkerSpawner.pm view on Meta::CPAN
for my $slot_num ($num_workers..$num_workers+$params{num_workers}-1) {
my $worker_id = sprintf '%d:%s/%s', $slot_num, $class, substr rand() . '0'x16, 2, 16;
push @slots, [$slot_num, $worker_id, \%params];
}
push @open_slots, @slots;
$num_workers += $params{num_workers};
my $success = 1;
local $SIG{CHLD} = 'IGNORE';
for (1 .. 10) {
my $cmd = '';
my $writer = $self->{supervisors}{$class};
if (!defined $writer) {
# don't have an existing child for this worker class
# logically, we want to call $self->_supervise, except in a separate
# process which has a reduced memory footprint after exec'ing. therefore we
# need to recreate $self and parameters in the "remote" _supervise
# procedure. create a pipe over which to do that.
pipe(my $reader, $writer) or die "pipe failed: $!\n";
$writer->autoflush(1);
$reader->autoflush(1);
# so exec doesn't close it
fcntl($reader, F_GETFD, my $flags = '');
vec($flags, FD_CLOEXEC, 1) = 0;
fcntl($reader, F_SETFD, $flags);
my $parent_pid = $$;
my $pid = fork;
die "failed to fork: $!\n" unless defined $pid;
if ($pid) {
# parent
$self->{supervisors}{$class} = $writer;
close $reader;
$self->{kids}{$pid}{action} = sub {
# supervisor shouldn't exit; compilation of worker class probably failed
my $code = shift;
if ($code != 0) {
die "supervisor died ($code)\n";
}
# invalidate cmd pipe "cache" when kid dies
delete $self->{supervisors}{$class};
};
# make a serializable copy of $self
my $storable_self = bless {
map { $_ => $self->{$_} }
grep {
$_ ne 'supervisors' && # globs aren't serializable
$_ ne 'kids' # so DESTROY doesn't kill them
}
keys %$self
}, __PACKAGE__;
$params{source} = (caller)[1] if $params{caller_source};
# first command is startup parameters
$cmd = _serialize({
spawner => $storable_self,
class => $class,
ppid => $parent_pid,
gearmand => gearman_servers(),
source => $params{source},
inc => \@INC,
});
}
else {
# child: start supervisor in a distinct process to manage the new jobs
exec $self->{perl}, $package_file, fileno $reader; # $self->_supervise
die "exec failed: $!\n";
}
}
# subsequent commands start new workers
$cmd .= _serialize(\@slots);
local $SIG{PIPE} = 'IGNORE';
return if print $writer $cmd;
# print failed, try again
delete $self->{supervisors}{$class} unless $success;
sleep 1;
}
die "failed to spawn workers";
}
=item $spawner->wait_until_all_ready()
Returns only once all worker are ready to accept jobs. This will only wait on
workers which have been started since the last call to wait_until_all_ready.
=cut
sub wait_until_all_ready {
my Gearman::WorkerSpawner $self = shift;
my $timeout = shift || 0.1;
my $client = Gearman::Client->new(job_servers => gearman_servers());
my $task_set = $client->new_task_set;
while (my $slot = shift @open_slots) {
$task_set->add_task(
_ping_name($slot->[SLOT_ID]),
undef,
{
timeout => $timeout,
retry_count => 1_000_000,
}
);
}
$task_set->wait;
}
=item $spawner->add_task($task)
( run in 1.287 second using v1.01-cache-2.11-cpan-302cb4679cc )