Bug 832893: changes jobqueue.pl to spawn worker processes to deliver bugmail to avoid memory leaks

r=dkl, a=LpSolit
trunk
Byron Jones 2013-02-15 13:57:04 +08:00
parent 5ca803ff3b
commit ef3caa58b2
4 changed files with 79 additions and 12 deletions

View File

@ -13,7 +13,10 @@ use strict;
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Install::Util qw(install_string);
use parent qw(TheSchwartz);
use File::Basename;
use File::Slurp;
use base qw(TheSchwartz);
use fields qw(_worker_pidfile);
# This maps job names for Bugzilla::JobQueue to the appropriate modules.
# If you add new types of jobs, you should add a mapping here.
@ -93,6 +96,57 @@ sub insert {
return $retval;
}
# To avoid memory leaks/fragmentation which tends to happen for long running
# perl processes; check for jobs, and spawn a new process to empty the queue.
sub subprocess_worker {
my $self = shift;
my $command = "$0 -d -p '" . $self->{_worker_pidfile} . "' onepass";
while (1) {
my $time = (time);
my @jobs = $self->list_jobs({
funcname => $self->{all_abilities},
run_after => $time,
grabbed_until => $time,
limit => 1,
});
if (@jobs) {
$self->debug("Spawning queue worker process");
# Run the worker as a daemon
system $command;
# And poll the PID to detect when the working has finished.
# We do this instead of system() to allow for the INT signal to
# interrup us and trigger kill_worker().
my $pid = read_file($self->{_worker_pidfile}, err_mode => 'quiet');
if ($pid) {
sleep(3) while(kill(0, $pid));
}
$self->debug("Queue worker process completed");
} else {
$self->debug("No jobs found");
}
sleep(5);
}
}
sub kill_worker {
my $self = Bugzilla->job_queue();
if ($self->{_worker_pidfile} && -e $self->{_worker_pidfile}) {
my $worker_pid = read_file($self->{_worker_pidfile});
if ($worker_pid && kill(0, $worker_pid)) {
$self->debug("Stopping worker process");
system "$0 -f -p '" . $self->{_worker_pidfile} . "' stop";
}
}
}
sub set_pidfile {
my ($self, $pidfile) = @_;
$self->{_worker_pidfile} = bz_locations->{'datadir'} .
'/worker-' . basename($pidfile);
}
# Clear the request cache at the start of each run.
sub work_once {
my $self = shift;
@ -136,4 +190,8 @@ be sent away to be done later.
=item job_map
=item set_pidfile
=item kill_worker
=back

View File

@ -38,6 +38,7 @@ our $initscript = "bugzilla-queue";
sub gd_preconfig {
my $self = shift;
$self->{_run_command} = 'subprocess_worker';
my $pidfile = $self->{gd_args}{pidfile};
if (!$pidfile) {
$pidfile = bz_locations()->{datadir} . '/' . $self->{gd_progname}
@ -136,6 +137,7 @@ sub gd_can_install {
print $config_fh <<END;
#!/bin/sh
BUGZILLA="$directory"
# This user must have write access to Bugzilla's data/ directory.
USER=$owner
END
close($config_fh);
@ -183,21 +185,25 @@ sub gd_setup_signals {
$SIG{TERM} = sub { $self->gd_quit_event(); }
}
sub gd_other_cmd {
my ($self) = shift;
if ($ARGV[0] eq "once") {
$self->_do_work("work_once");
sub gd_quit_event {
Bugzilla->job_queue->kill_worker();
exit(1);
}
exit(0);
sub gd_other_cmd {
my ($self, $do, $locked) = @_;
if ($do eq "once") {
$self->{_run_command} = 'work_once';
} elsif ($do eq "onepass") {
$self->{_run_command} = 'work_until_done';
} else {
$self->SUPER::gd_other_cmd($do, $locked);
}
$self->SUPER::gd_other_cmd();
}
sub gd_run {
my $self = shift;
$self->_do_work("work");
$self->_do_work($self->{_run_command});
}
sub _do_work {
@ -205,11 +211,11 @@ sub _do_work {
my $jq = Bugzilla->job_queue();
$jq->set_verbose($self->{debug});
$jq->set_pidfile($self->{gd_pidfile});
foreach my $module (values %{ Bugzilla::JobQueue->job_map() }) {
eval "use $module";
$jq->can_do($module);
}
$jq->$fn;
}
@ -242,6 +248,8 @@ to run the Bugzilla job queue.
=item gd_can_install
=item gd_quit_event
=item gd_other_cmd
=item gd_more_opt

View File

@ -46,6 +46,7 @@ jobqueue.pl - Runs jobs in the background for Bugzilla.
starts a new one.
once Checks the job queue once, executes the first item found (if
any) and then exits
onepass Checks the job queue, executes all items found, and then exits
check Report the current status of the daemon.
install On some *nix systems, this automatically installs and
configures jobqueue.pl as a system service so that it will

View File

@ -30,7 +30,7 @@ use constant DEFAULT_WHITELIST => qr/^(?:new|new_from_list|check|run_create_vali
use constant SUB_WHITELIST => (
'Bugzilla::Flag' => qr/^(?:(force_)?retarget|force_cleanup)$/,
'Bugzilla::FlagType' => qr/^sqlify_criteria$/,
'Bugzilla::JobQueue' => qr/^work_once$/,
'Bugzilla::JobQueue' => qr/(?:^work_once|subprocess_worker)$/,
);
# These modules do not need to be documented, generally because they