[interchange-cvs] interchange - pajamian modified 4 files
interchange-cvs at icdevgroup.org
interchange-cvs at icdevgroup.org
Fri Aug 10 04:42:09 EDT 2007
User: pajamian
Date: 2007-08-10 08:42:09 GMT
Modified: . WHATSNEW-5.5
Modified: lib/Vend Config.pm Dispatch.pm Server.pm
Log:
Applied patch from Mark Johnson <mark at endpoint.com> to fix problem with RPC
mode preforking too many children on server startup due to race condition.
Revision Changes Path
1.51 +3 -0 interchange/WHATSNEW-5.5
rev 1.51, prev_rev 1.50
Index: WHATSNEW-5.5
===================================================================
RCS file: /var/cvs/interchange/WHATSNEW-5.5,v
retrieving revision 1.50
retrieving revision 1.51
diff -u -r1.50 -r1.51
--- WHATSNEW-5.5 9 Aug 2007 18:16:39 -0000 1.50
+++ WHATSNEW-5.5 10 Aug 2007 08:42:08 -0000 1.51
@@ -100,6 +100,9 @@
* Remove catalog status files when removing catalog. Also call remove_catalog at
server stop -- would be nice for cleanup anyway (#95).
+* Fixed problem where RPC mode would fork too many processes at startup due to
+ race condition (#107).
+
UserDB
------
2.221 +3 -2 interchange/lib/Vend/Config.pm
rev 2.221, prev_rev 2.220
Index: Config.pm
===================================================================
RCS file: /var/cvs/interchange/lib/Vend/Config.pm,v
retrieving revision 2.220
retrieving revision 2.221
diff -u -r2.220 -r2.221
--- Config.pm 9 Aug 2007 09:42:46 -0000 2.220
+++ Config.pm 10 Aug 2007 08:42:09 -0000 2.221
@@ -1,6 +1,6 @@
# Vend::Config - Configure Interchange
#
-# $Id: Config.pm,v 2.220 2007/08/09 09:42:46 kwalsh Exp $
+# $Id: Config.pm,v 2.221 2007/08/10 08:42:09 pajamian Exp $
#
# Copyright (C) 2002-2007 Interchange Development Group
# Copyright (C) 1996-2002 Red Hat, Inc.
@@ -54,7 +54,7 @@
use Vend::Data;
use Vend::Cron;
-$VERSION = substr(q$Revision: 2.220 $, 10);
+$VERSION = substr(q$Revision: 2.221 $, 10);
my %CDname;
my %CPname;
@@ -443,6 +443,7 @@
['ChildLife', 'time', 0],
['StartServers', 'integer', 0],
['PreFork', 'yesno', 0],
+ ['PreForkSingleFork', 'yesno', 0],
['SOAP_MaxRequests', 'integer', 50],
['SOAP_StartServers', 'integer', 1],
['SOAP_Control', 'action', ''],
1.85 +12 -2 interchange/lib/Vend/Dispatch.pm
rev 1.85, prev_rev 1.84
Index: Dispatch.pm
===================================================================
RCS file: /var/cvs/interchange/lib/Vend/Dispatch.pm,v
retrieving revision 1.84
retrieving revision 1.85
diff -u -r1.84 -r1.85
--- Dispatch.pm 9 Aug 2007 11:08:36 -0000 1.84
+++ Dispatch.pm 10 Aug 2007 08:42:09 -0000 1.85
@@ -1,6 +1,6 @@
# Vend::Dispatch - Handle Interchange page requests
#
-# $Id: Dispatch.pm,v 1.84 2007/08/09 11:08:36 racke Exp $
+# $Id: Dispatch.pm,v 1.85 2007/08/10 08:42:09 pajamian Exp $
#
# Copyright (C) 2002-2007 Interchange Development Group
# Copyright (C) 2002 Mike Heins <mike at perusion.net>
@@ -26,7 +26,7 @@
package Vend::Dispatch;
use vars qw($VERSION);
-$VERSION = substr(q$Revision: 1.84 $, 10);
+$VERSION = substr(q$Revision: 1.85 $, 10);
use POSIX qw(strftime);
use Vend::Util;
@@ -1215,6 +1215,8 @@
## If returns false then was a 404 no catalog or a delivered image
open_cat() or return 1;
+ $0 = "interchange: $Vend::Cat $CGI::host";
+
$CGI::user = Vend::Util::check_authorization($CGI::authorization)
if defined $CGI::authorization;
@@ -1260,6 +1262,8 @@
$Vend::CookieID = $Vend::Cookie = 1;
}
+ $0 = "interchange: $Vend::Cat $CGI::host $sessionid";
+
$::Instance->{CookieName} = 'MV_SESSION_ID' if ! $::Instance->{CookieName};
$CGI::host = 'nobody' if $Vend::Cfg->{WideOpen};
@@ -1458,6 +1462,8 @@
$Vend::Session->{'user'} = $CGI::user;
+ $0 = "interchange: $Vend::Cat $CGI::host $sessionid " . $Vend::Session->{username} || '-';
+
$CGI::pragma = 'no-cache'
if delete $::Scratch->{mv_no_cache};
#show_times("end session get") if $Global::ShowTimes;
@@ -1474,6 +1480,8 @@
url_history($Vend::FinalPath) if $Vend::Cfg->{History};
+ $0 = "interchange: $Vend::Cat $CGI::host $sessionid " . ($Vend::Session->{username} || '-') . " $Vend::FinalPath";
+
# TRACK
$Vend::Track = new Vend::Track
if $Vend::Cfg->{UserTrack} and not ($Vend::admin and ! $::Variable->{MV_TRACK_ADMIN});
@@ -1703,6 +1711,8 @@
# END TRACK
close_cat();
+
+ $0 = 'interchange: done';
undef $H;
2.78 +221 -68 interchange/lib/Vend/Server.pm
rev 2.78, prev_rev 2.77
Index: Server.pm
===================================================================
RCS file: /var/cvs/interchange/lib/Vend/Server.pm,v
retrieving revision 2.77
retrieving revision 2.78
diff -u -r2.77 -r2.78
--- Server.pm 9 Aug 2007 15:10:10 -0000 2.77
+++ Server.pm 10 Aug 2007 08:42:09 -0000 2.78
@@ -1,6 +1,6 @@
# Vend::Server - Listen for Interchange CGI requests as a background server
#
-# $Id: Server.pm,v 2.77 2007/08/09 15:10:10 racke Exp $
+# $Id: Server.pm,v 2.78 2007/08/10 08:42:09 pajamian Exp $
#
# Copyright (C) 2002-2007 Interchange Development Group
# Copyright (C) 1996-2002 Red Hat, Inc.
@@ -26,7 +26,7 @@
package Vend::Server;
use vars qw($VERSION);
-$VERSION = substr(q$Revision: 2.77 $, 10);
+$VERSION = substr(q$Revision: 2.78 $, 10);
use Cwd;
use POSIX qw(setsid strftime);
@@ -872,6 +872,10 @@
sub connection {
my (%env, $entity);
+ my $show_in_ps = shift;
+
+ $0 = 'interchange: connection';
+
### This resets all $Vend::variable settings so we start
### completely initialized. It only affects the Vend package,
### not any Vend::XXX packages.
@@ -890,11 +894,18 @@
# Can log all CGI inputs
log_http_data($http) if $Global::Logging;
+ $0 = 'interchange: dispatch';
+
show_times("begin dispatch") if $Global::ShowTimes;
::dispatch($http) if $http;
show_times("end connection") if $Global::ShowTimes;
close $http->{rfh} if $http->{rfh};
undef $Vend::Cfg;
+
+ my $display = 'interchange: done';
+ $display .= "($show_in_ps)" if $show_in_ps;
+
+ $0 = $display;
}
## Signals
@@ -921,6 +932,9 @@
$Num_servers
$Page_servers
%Page_pids
+ %Starting_pids
+ $Starting_pids
+ @Termed_pids
$SOAP_servers
%SOAP_pids
$Job_servers
@@ -933,6 +947,7 @@
BEGIN {
$s_vector = '';
}
+$Starting_pids = 0;
$Num_servers = 0;
$SOAP_servers = 0;
$Job_servers = 0;
@@ -943,6 +958,38 @@
my ($Routine_USR1, $Routine_USR2, $Routine_HUP, $Routine_TERM, $Routine_INT);
my ($Sig_inc, $Sig_dec, $Counter);
+sub sig_int_or_term {
+ $Signal_Terminate = 1;
+
+ my $term_count = 0;
+ TERM: {
+ my %seen;
+ my @pids =
+ grep { !$seen{$_}++ }
+ (keys %Page_pids, keys %Starting_pids);
+
+ last TERM unless @pids;
+
+ kill TERM => $_ for @pids;
+ sleep 1;
+
+ redo TERM unless ++$term_count > 3;
+ }
+
+ KILL: {
+ my %seen;
+ my @pids =
+ grep { !$seen{$_}++ }
+ (keys %Page_pids, keys %Starting_pids);
+
+ last KILL unless @pids;
+
+ kill KILL => $_ for @pids;
+ }
+
+ return;
+}
+
unless ($Global::Windows) {
push @trapped_signals, qw(HUP USR1 USR2);
$Routine_USR1 = sub { $SIG{USR1} = $Routine_USR1; $Num_servers++};
@@ -983,18 +1030,20 @@
}
sub setup_signals {
- @orig_signal{@trapped_signals} =
- map(defined $_ ? $_ : 'DEFAULT', @SIG{@trapped_signals});
- $Signal_Terminate = '';
- $SIG{PIPE} = 'IGNORE';
+ @orig_signal{@trapped_signals} =
+ map(defined $_ ? $_ : 'DEFAULT', @SIG{@trapped_signals});
+ $Signal_Terminate = '';
+ $SIG{PIPE} = 'IGNORE';
+ $SIG{CHLD} = 'IGNORE'
+ if $Global::PreFork && $Global::PreForkSingleFork;
if ($Global::Windows) {
- $SIG{INT} = sub { $Signal_Terminate = 1; };
- $SIG{TERM} = sub { $Signal_Terminate = 1; };
+ $SIG{INT} = \&sig_int_or_term;
+ $SIG{TERM} = \&sig_int_or_term;
}
else {
- $SIG{INT} = sub { $Signal_Terminate = 1; };
- $SIG{TERM} = sub { $Signal_Terminate = 1; };
+ $SIG{INT} = \&sig_int_or_term;
+ $SIG{TERM} = \&sig_int_or_term;
$SIG{HUP} = sub { $Signal_Restart = 1; };
$SIG{USR1} = sub { $Num_servers++; };
$SIG{USR2} = sub { $Num_servers--; };
@@ -1045,45 +1094,84 @@
rand();
$Last_housekeeping = $now;
- my ($c, $num,$reconfig, $restart, $jobs, @files);
- my @pids;
+ my ($c, $num,$reconfig, $restart, $jobs, @files, @pidcheck_pids);
if($Global::PreFork) {
- my @bad_pids;
- my (@pids) = sort keys %Page_pids;
- my $count = scalar @pids;
- while ($count > ($Global::StartServers + 1) ) {
-#::logDebug("too many pids");
- my ($bad) = shift(@pids);
-#::logDebug("scheduling %s for death", $bad);
- push @bad_pids, $bad;
- $count--;
+ my @starting_pids = keys %Starting_pids;
+ my $starting_count = starting_pids('count');
+ my %bad_pids;
+ my @active_pids = keys %Page_pids;
+ my $active_count = scalar @active_pids;
+ my $check_time = time();
+ my $start_max_time = 30;
+
+ for my $pid (@starting_pids) {
+ my $time_taken = $check_time - $Starting_pids{$pid};
+ if ($time_taken > $start_max_time) {
+ ::logDebug("pid $pid took $time_taken seconds to start ($start_max_time allowed); scheduling for death");
+ $bad_pids{$pid} = undef;
+ delete $Starting_pids{$pid};
+ --$starting_count;
+ }
+ }
+
+ while ($active_count > ($Global::StartServers + 1) ) {
+::logDebug("too many pids ($active_count)");
+ my $bad = shift @active_pids;
+::logDebug("scheduling %s for death", $bad);
+ $bad_pids{$bad} = undef;
+ --$active_count;
}
- foreach my $pid (@pids) {
+ foreach my $pid (@active_pids) {
kill(0, $pid) and next;
-#::logDebug("Non-existent server at PID %s", $pid);
- push @bad_pids, $pid;
+::logDebug("Non-existent server at PID %s", $pid);
delete $Page_pids{$pid};
+ --$active_count;
}
- while($count < $Global::StartServers) {
-#::logDebug("Spawning page server to reach StartServers");
- start_page(undef,$Global::PreFork,1);
- $count++;
- }
- for my $pid (@bad_pids) {
-#::logDebug("Killing excess or unresponsive server at PID %s", $pid);
- next unless delete $Page_pids{$pid};
- if(kill 'TERM', $pid) {
-#::logDebug("Server at PID %s terminated OK", $pid);
- # This is OK
- }
- elsif (kill 'KILL', $pid) {
- ::logGlobal("page server pid %s required KILL", $pid);
+ if ($Global::PIDcheck) {
+ for my $pid (keys %Page_pids) {
+ my $last_use = $check_time - $Page_pids{$pid};
+ next unless $last_use > $Global::PIDcheck;
+::logDebug('pid %s last used %d seconds ago', $pid, $last_use);
+ $bad_pids{$pid} = undef;
+ delete $Page_pids{$pid};
+::logDebug('scheduling %s for death', $pid);
+ --$active_count;
}
- else {
- ::logGlobal("page server pid %s won't die!", $pid);
+ }
+
+ if ($active_count + $starting_count < $Global::StartServers) {
+ my $server_deficit =
+ $Global::StartServers
+ - $active_count
+ - $starting_count;
+ ::logGlobal("Spawning %d page server%s to reach %s StartServers", $server_deficit, $server_deficit == 1 ? '' : 's', $Global::StartServers);
+ start_page(undef, $Global::PreFork, $server_deficit);
+ }
+
+ for my $pid (@Termed_pids) {
+ kill (KILL => $pid)
+ and ::logDebug("Sent $pid a KILL");
+ }
+ ::logGlobal("page server pid %s won't die!", $_)
+ for grep { kill (0, $_) } @Termed_pids;
+ @Termed_pids = ();
+
+ if (%bad_pids) {
+::logDebug("Killing excess, old, or unresponsive servers");
+ delete @Page_pids{ keys %bad_pids };
+
+ for my $pid
+ ( grep
+ { kill (0, $_) or delete $bad_pids{$_} }
+ keys %bad_pids
+ )
+ {
+ kill (TERM => $pid);
+ ::logDebug("Sent $pid a TERM");
+ push (@Termed_pids, $pid);
}
}
}
@@ -1114,7 +1202,7 @@
if($Global::PIDcheck) {
$Num_servers = 0;
- @pids = grep /^pid\.\d+$/, @files;
+ @pidcheck_pids = grep /^pid\.\d+$/, @files;
}
my $respawn;
@@ -1335,8 +1423,8 @@
$_,
$!,
);
- start_page(undef,$Global::PreFork,1);
}
+ start_page(undef, $Global::PreFork, scalar @pids);
}
if($Global::SOAP) {
# We need to respawn all the SOAP servers to pick up the new config
@@ -1358,7 +1446,7 @@
}
}
- for (@pids) {
+ for (@pidcheck_pids) {
$Num_servers++;
my $fn = "$Global::RunDir/$_";
($Num_servers--, next) if ! -f $fn;
@@ -1588,33 +1676,58 @@
sub start_page {
my ($do_message, $no_fork, $number) = @_;
-#::logDebug("starting soap");
+#::logDebug("entering start_page");
- $number = $Global::StartServers if ! $number;
- if ($number > 50) {
+ my $current_servers =
+ starting_pids('count')
+ + scalar (keys %Page_pids);
+
+ my $server_deficit = $Global::StartServers - $current_servers;
+
+ # Bail immediately if we already have a slate of
+ # StartServers servers either pending or serving
+ return 1 if $server_deficit < 1;
+
+ # Shave number down to server_deficit if it's greater
+ $number = $server_deficit if $server_deficit < $number;
+
+ if ($number > 150) {
die ::errmsg(
"Ridiculously large number of StartServers: %s",
$number,
);
}
- for (1 .. $number) {
- my $pid;
- if(! defined ($pid = fork) ) {
- my $msg = ::errmsg("Can't fork: %s", $!);
- ::logGlobal({ level => 'crit' }, $msg );
- die ("$msg\n");
- }
- elsif (! $pid) {
- unless( $pid = fork ) {
+ my $dbl_fork_pid;
+
+ if (
+ $Global::PreForkSingleFork
+ or ! ($dbl_fork_pid = fork)
+ )
+ {
+
+ for (1 .. $number) {
+ my $pid;
+ if(! defined ($pid = fork) ) {
+ my $msg = ::errmsg("Can't fork: %s", $!);
+ ::logGlobal({ level => 'crit' }, $msg );
+ die ("$msg\n");
+ }
+ elsif (! $pid) {
$Global::Foreground = 1 if $no_fork;
- if($do_message) {
+ local $SIG{CHLD} = 'DEFAULT'
+ if $Global::PreForkSingleFork;
+
+ local $SIG{INT} = $Routine_INT;
+ local $SIG{TERM} = $Routine_TERM;
+
+ if ($do_message and ! $Vend::Quiet) {
::logGlobal(
{ level => 'info'},
server_start_message(
"Interchange page server started (process id %s)",
- ),
- ) unless $Vend::Quiet;
+ ),
+ );
}
send_ipc("register page $$");
@@ -1630,20 +1743,27 @@
if ($@) {
my $msg = ::errmsg("Server spawn error: %s", $@);
::logGlobal({ level => 'error' }, $msg);
- logError($msg)
+ ::logError($msg)
if defined $Vend::Cfg->{ErrorFile};
}
clean_up_after_fork();
- send_ipc("respawn page $$") if $next;
+ send_ipc("respawn page $$") if $next;
undef $::Instance;
exit(0);
}
- exit(0);
+ starting_pids('add',$pid)
+ if $Global::PreForkSingleFork;
}
+ $Global::PreForkSingleFork or exit(0);
+ }
+
+ if ($dbl_fork_pid) {
+ starting_pids('add',undef,$number);
wait;
}
+
return 1;
}
@@ -1710,6 +1830,32 @@
return 1;
}
+sub starting_pids {
+ my ($action,$pid,$n) = @_;
+
+ $n ||= 1;
+
+ if ( $action eq 'count' ) {
+ return $Global::PreForkSingleFork
+ ? scalar keys %Starting_pids
+ : $Starting_pids
+ ;
+ }
+ elsif ( $action eq 'add' ) {
+ $Global::PreForkSingleFork
+ ? ($Starting_pids{$pid} = time)
+ : ($Starting_pids += $n)
+ ;
+ }
+ elsif ( $action eq 'del' ) {
+ $Global::PreForkSingleFork
+ ? delete ($Starting_pids{$pid})
+ : ($Starting_pids -= $n)
+ ;
+ }
+ return;
+}
+
sub server_page {
my ($no_fork) = @_;
@@ -1812,9 +1958,12 @@
if($no_fork) {
### Careful, returns after MaxRequests or terminate signal
$::Instance = {};
- $handled++;
#::logDebug("begin non-forked ::connection()");
- connection();
+ send_ipc(sprintf ('lastused %s %s',$$,time))
+ if $Global::PIDcheck;
+ connection(++$handled);
+ send_ipc(sprintf ('lastused %s %s',$$,time))
+ if $Global::PIDcheck;
#::logDebug("end non-forked ::connection()");
undef $::Instance;
}
@@ -1923,8 +2072,6 @@
last;
}
-
-
};
last if $Signal_Terminate;
@@ -2040,8 +2187,13 @@
close $fh;
$Num_servers--;
}
+ elsif ($thing =~ /^lastused (\d+) (\d+)/) {
+#::logDebug("Page pid $1 last used at $2");
+ $Page_pids{$1} = $2;
+ }
elsif ($thing =~ /^register page (\d+)/) {
- $Page_pids{$1} = 1;
+ $Page_pids{$1} = time;
+ starting_pids('del',$1);
#::logDebug("registered Page pid $1");
$Page_servers++;
}
@@ -2089,7 +2241,7 @@
} while ( ! defined $ok and ! $!{EINTR});
print SOCK $msg;
-#::logDebug("pid $$: sent ipc $msg");
+::logDebug("pid $$: sent ipc $msg");
close SOCK;
}
@@ -2298,7 +2450,7 @@
if($Global::StartServers) {
$master_ipc = 1;
$p_vector = $vector ^ $ipc_vector;
- start_page(1,$Global::PreFork);
+ start_page(1, $Global::PreFork, $Global::StartServers);
}
my $c = 0;
@@ -2839,6 +2991,7 @@
}
}
}
+
1;
__END__
More information about the interchange-cvs
mailing list