Diff for /loncom/loncnew between versions 1.25 and 1.34

version 1.25, 2003/09/23 11:22:14 version 1.34, 2003/12/11 23:18:37
Line 43 Line 43
 #    - Detect transmission timeouts.  #    - Detect transmission timeouts.
 #  #
   
 # Change log:  
 #    $Log$  
 #    Revision 1.25  2003/09/23 11:22:14  foxr  
 #    Tested ability to receive sigusr2  This is now logged and must be  
 #    properly implemented as a re-read of hosts and re-init of appropriate  
 #    children.  
 #  
 #    Revision 1.24  2003/09/16 09:46:42  foxr  
 #    Added skeletal infrastructure to support SIGUSR2 update hosts request.  
 #  
 #    Revision 1.23  2003/09/15 09:24:49  foxr  
 #    Add use strict and fix all the fallout from that.  
 #  
 #    Revision 1.22  2003/09/02 10:34:47  foxr  
 #    - Fix errors in host dead detection logic (too many cases where the  
 #      retries left were not getting incremented or just not checked).  
 #    - Added some additional status to the ps axuww display:  
 #      o Remaining retries on a host.  
 #      o >>> DEAD <<< indicator if I've given up on a host.  
 #    - Tested the SIGHUP will reset the retries remaining count (thanks to  
 #      the above status stuff, and get allow the loncnew to re-try again  
 #      on the host (thanks to the log).  
 #  
 #    Revision 1.21  2003/08/26 09:19:51  foxr  
 #    How embarrassing... put in the SocketTimeout function in loncnew and forgot  
 #    to actually hook it into the LondTransaction.  Added this to MakeLondConnection  
 #    where it belongs... hopefully transactions (not just connection attempts) will  
 #    timeout more speedily than the socket errors will catch it.  
 #  
 #    Revision 1.20  2003/08/25 18:48:11  albertel  
 #    - fixing a forgotten ;  
 #  
 #    Revision 1.19  2003/08/19 09:31:46  foxr  
 #    Get socket directory from configuration rather than the old hard coded test  
 #    way that I forgot to un-hard code.  
 #  
 #    Revision 1.18  2003/08/06 09:52:29  foxr  
 #    Also needed to remember to fail in-flight transactions if their sends fail.  
 #  
 #    Revision 1.17  2003/08/03 00:44:31  foxr  
 #    1. Correct handling of connection failure: Assume it means the host is  
 #       unreachable and fail all of the queued transactions.  Note that the  
 #       inflight transactions should fail on their own time due either to timeout  
 #       or send/receive failures.  
 #    2. Correct handling of logs for forced death signals.  Pull the signal  
 #       from the event watcher.  
 #  
 #    Revision 1.16  2003/07/29 02:33:05  foxr  
 #    Add SIGINT processing to child processes to toggle annoying trace mode  
 #    on/off.. will try to use this to isolate the compute boud process issue.  
 #  
 #    Revision 1.15  2003/07/15 02:07:05  foxr  
 #    Added code for lonc/lond transaction timeouts.  Who knows if it works right.  
 #    The intent is for a timeout to fail any transaction in progress and kill  
 #    off the sockt that timed out.  
 #  
 #    Revision 1.14  2003/07/03 02:10:18  foxr  
 #    Get all of the signals to work correctly.  
 #  
 #    Revision 1.13  2003/07/02 01:31:55  foxr  
 #    Added kill -HUP logic (restart).  
 #  
 #    Revision 1.11  2003/06/25 01:54:44  foxr  
 #    Fix more problems with transaction failure.  
 #  
 #    Revision 1.10  2003/06/24 02:46:04  foxr  
 #    Put a limit on  the number of times we'll retry a connection.  
 #    Start getting the signal stuff put in as well...note that need to get signals  
 #    going or else the client will permanently give up on dead servers.  
 #  
 #    Revision 1.9  2003/06/13 02:38:43  foxr  
 #    Add logging in 'expected format'  
 #  
 #    Revision 1.8  2003/06/11 02:04:35  foxr  
 #    Support delayed transactions... this is done uniformly by encapsulating  
 #    transactions in an object ... a LondTransaction that is implemented by  
 #    LondTransaction.pm  
 #  
 #    Revision 1.7  2003/06/03 01:59:39  foxr  
 #    complete coding to support deferred transactions.  
 #  
 #  
 use strict;  use strict;
 use lib "/home/httpd/lib/perl/";  use lib "/home/httpd/lib/perl/";
 use lib "/home/foxr/newloncapa/types";  
 use Event qw(:DEFAULT );  use Event qw(:DEFAULT );
 use POSIX qw(:signal_h);  use POSIX qw(:signal_h);
 use POSIX;  use POSIX;
Line 161  my %perlvar    = %{$perlvarref}; Line 78  my %perlvar    = %{$perlvarref};
 #  parent and shared variables.  #  parent and shared variables.
   
 my %ChildHash; # by pid -> host.  my %ChildHash; # by pid -> host.
   my %HostToPid; # By host -> pid.
   my %HostHash; # by loncapaname -> IP.
   
   
 my $MaxConnectionCount = 10; # Will get from config later.  my $MaxConnectionCount = 10; # Will get from config later.
 my $ClientConnection = 0; # Uniquifier for client events.  my $ClientConnection = 0; # Uniquifier for client events.
   
 my $DebugLevel = 0;  my $DebugLevel = 0;
 my $NextDebugLevel= 10; # So Sigint can toggle this.  my $NextDebugLevel= 2; # So Sigint can toggle this.
 my $IdleTimeout= 3600; # Wait an hour before pruning connections.  my $IdleTimeout= 3600; # Wait an hour before pruning connections.
   
 #  #
Line 184  my $ConnectionCount = 0; Line 103  my $ConnectionCount = 0;
 my $IdleSeconds     = 0; # Number of seconds idle.  my $IdleSeconds     = 0; # Number of seconds idle.
 my $Status          = ""; # Current status string.  my $Status          = ""; # Current status string.
 my $RecentLogEntry  = "";  my $RecentLogEntry  = "";
 my $ConnectionRetries=5; # Number of connection retries allowed.  my $ConnectionRetries=2; # Number of connection retries allowed.
 my $ConnectionRetriesLeft=5; # Number of connection retries remaining.  my $ConnectionRetriesLeft=2; # Number of connection retries remaining.
   
 #  #
 #   The hash below gives the HTML format for log messages  #   The hash below gives the HTML format for log messages
Line 386  sub Tick { Line 305  sub Tick {
  my $Socket = $ActiveTransactions{$item}->getServer();   my $Socket = $ActiveTransactions{$item}->getServer();
  $Socket->Tick();   $Socket->Tick();
     }      }
       foreach my $item (keys %ActiveConnections) {
    my $State = $ActiveConnections{$item}->data->GetState();
    if ($State ne 'Idle' && $State ne 'SendingRequest' &&
       $State ne 'ReceivingReply') {
       Debug(5,"Ticking Socket $State $item");
       $ActiveConnections{$item}->data->Tick();
    }
       }
     # Do we have work in the queue, but no connections to service them?      # Do we have work in the queue, but no connections to service them?
     # If so, try to make some new connections to get things going again.      # If so, try to make some new connections to get things going again.
     #      #
Line 395  sub Tick { Line 322  sub Tick {
  if ($ConnectionRetriesLeft > 0) {   if ($ConnectionRetriesLeft > 0) {
     my $Connections = ($Requests <= $MaxConnectionCount) ?      my $Connections = ($Requests <= $MaxConnectionCount) ?
  $Requests : $MaxConnectionCount;   $Requests : $MaxConnectionCount;
     Debug(1,"Work but no connections, start ".$Connections." of them");      Debug(5,"Work but no connections, start ".$Connections." of them");
     my $successCount = 0;      my $successCount = 0;
     for (my $i =0; $i < $Connections; $i++) {      for (my $i =0; $i < $Connections; $i++) {
  $successCount += MakeLondConnection();   $successCount += MakeLondConnection();
     }      }
     if($successCount == 0) { # All connections failed:      if($successCount == 0) { # All connections failed:
  Debug(1,"Work in queue failed to make any connectiouns\n");   Debug(5,"Work in queue failed to make any connectiouns\n");
  EmptyQueue(); # Fail pending transactions with con_lost.   EmptyQueue(); # Fail pending transactions with con_lost.
     }      }
  } else {   } else {
     ShowStatus(GetServerHost()." >>> DEAD!!! <<<");      ShowStatus(GetServerHost()." >>> DEAD!!! <<<");
     Debug(1,"Work in queue, but gave up on connections..flushing\n");      Debug(5,"Work in queue, but gave up on connections..flushing\n");
     EmptyQueue(); # Connections can't be established.      EmptyQueue(); # Connections can't be established.
  }   }
                 
Line 431  Trigger disconnections of idle sockets. Line 358  Trigger disconnections of idle sockets.
   
 sub SetupTimer {  sub SetupTimer {
     Debug(6, "SetupTimer");      Debug(6, "SetupTimer");
     Event->timer(interval => 1, debug => 1, cb => \&Tick );      Event->timer(interval => 1, cb => \&Tick );
 }  }
   
 =pod  =pod
Line 453  sub ServerToIdle { Line 380  sub ServerToIdle {
     my $Socket   = shift; # Get the socket.      my $Socket   = shift; # Get the socket.
     delete($ActiveTransactions{$Socket}); # Server has no transaction      delete($ActiveTransactions{$Socket}); # Server has no transaction
   
     &Debug(6, "Server to idle");      &Debug(5, "Server to idle");
   
     #  If there's work to do, start the transaction:      #  If there's work to do, start the transaction:
   
     my $reqdata = $WorkQueue->dequeue(); # This is a LondTransaction      my $reqdata = $WorkQueue->dequeue(); # This is a LondTransaction
     unless($reqdata eq undef)  {      if ($reqdata ne undef)  {
  Debug(9, "Queue gave request data: ".$reqdata->getRequest());   Debug(5, "Queue gave request data: ".$reqdata->getRequest());
  &StartRequest($Socket,  $reqdata);   &StartRequest($Socket,  $reqdata);
   
     } else {      } else {
   
     #  There's no work waiting, so push the server to idle list.      #  There's no work waiting, so push the server to idle list.
  &Debug(8, "No new work requests, server connection going idle");   &Debug(5, "No new work requests, server connection going idle");
  $IdleConnections->push($Socket);   $IdleConnections->push($Socket);
     }      }
 }  }
Line 511  sub ClientWritable { Line 438  sub ClientWritable {
  # request.   # request.
   
  &Debug(9,"Send result is ".$result." Defined: ".defined($result));   &Debug(9,"Send result is ".$result." Defined: ".defined($result));
  if(defined($result)) {   if($result ne undef) {
     &Debug(9, "send result was defined");      &Debug(9, "send result was defined");
     if($result == length($Data)) { # Entire string sent.      if($result == length($Data)) { # Entire string sent.
  &Debug(9, "ClientWritable data all written");   &Debug(9, "ClientWritable data all written");
Line 582  The transaction that is being completed. Line 509  The transaction that is being completed.
 =cut  =cut
   
 sub CompleteTransaction {  sub CompleteTransaction {
     &Debug(6,"Complete transaction");      &Debug(5,"Complete transaction");
     my $Socket = shift;      my $Socket = shift;
     my $Transaction = shift;      my $Transaction = shift;
   
Line 621  sub StartClientReply { Line 548  sub StartClientReply {
     &Debug(8," Reply was: ".$data);      &Debug(8," Reply was: ".$data);
     my $Serial         = $ActiveClients{$Client};      my $Serial         = $ActiveClients{$Client};
     my $desc           = sprintf("Connection to lonc client %d",      my $desc           = sprintf("Connection to lonc client %d",
   
  $Serial);   $Serial);
     Event->io(fd       => $Client,      Event->io(fd       => $Client,
       poll     => "w",        poll     => "w",
Line 870  sub LondReadable { Line 796  sub LondReadable {
  $ConnectionRetriesLeft = $ConnectionRetries; # success resets the count   $ConnectionRetriesLeft = $ConnectionRetries; # success resets the count
  $Watcher->cancel();   $Watcher->cancel();
  if(exists($ActiveTransactions{$Socket})) {   if(exists($ActiveTransactions{$Socket})) {
     Debug(8,"Completing transaction!!");      Debug(5,"Completing transaction!!");
     CompleteTransaction($Socket,       CompleteTransaction($Socket, 
  $ActiveTransactions{$Socket});   $ActiveTransactions{$Socket});
  } else {   } else {
Line 1117  sub MakeLondConnection { Line 1043  sub MakeLondConnection {
     my $Connection = LondConnection->new(&GetServerHost(),      my $Connection = LondConnection->new(&GetServerHost(),
  &GetServerPort());   &GetServerPort());
   
     if($Connection == undef) { # Needs to be more robust later.      if($Connection eq undef) { # Needs to be more robust later.
  Log("CRITICAL","Failed to make a connection with lond.");   Log("CRITICAL","Failed to make a connection with lond.");
  $ConnectionRetriesLeft--;   $ConnectionRetriesLeft--;
  return 0; # Failure.   return 0; # Failure.
Line 1129  sub MakeLondConnection { Line 1055  sub MakeLondConnection {
  # exchange underway.   # exchange underway.
  #   #
  my $Socket = $Connection->GetSocket();   my $Socket = $Connection->GetSocket();
  if($Socket == undef) {   if($Socket eq undef) {
     die "did not get a socket from the connection";      die "did not get a socket from the connection";
  } else {   } else {
     &Debug(9,"MakeLondConnection got socket: ".$Socket);      &Debug(9,"MakeLondConnection got socket: ".$Socket);
Line 1232  sub QueueTransaction { Line 1158  sub QueueTransaction {
   
     my $LondSocket    = $IdleConnections->pop();      my $LondSocket    = $IdleConnections->pop();
     if(!defined $LondSocket) { # Need to queue request.      if(!defined $LondSocket) { # Need to queue request.
  Debug(8,"Must queue...");   Debug(5,"Must queue...");
  $WorkQueue->enqueue($requestData);   $WorkQueue->enqueue($requestData);
  if($ConnectionCount < $MaxConnectionCount) {   if($ConnectionCount < $MaxConnectionCount) {
     if($ConnectionRetriesLeft > 0) {      if($ConnectionRetriesLeft > 0) {
  Debug(4,"Starting additional lond connection");   Debug(5,"Starting additional lond connection");
  if(MakeLondConnection() == 0) {   if(MakeLondConnection() == 0) {
     EmptyQueue(); # Fail transactions, can't make connection.      EmptyQueue(); # Fail transactions, can't make connection.
  }   }
Line 1274  sub ClientRequest { Line 1200  sub ClientRequest {
     my $rv = $socket->recv($thisread, POSIX::BUFSIZ, 0);      my $rv = $socket->recv($thisread, POSIX::BUFSIZ, 0);
     Debug(8, "rcv:  data length = ".length($thisread)      Debug(8, "rcv:  data length = ".length($thisread)
   ." read =".$thisread);    ." read =".$thisread);
     unless (defined $rv && length($thisread)) {      unless (defined $rv  && length($thisread)) {
  # Likely eof on socket.   # Likely eof on socket.
  Debug(5,"Client Socket closed on lonc for ".$RemoteHost);   Debug(5,"Client Socket closed on lonc for ".$RemoteHost);
  close($socket);   close($socket);
Line 1519  sub CreateChild { Line 1445  sub CreateChild {
     my $pid          = fork;      my $pid          = fork;
     if($pid) { # Parent      if($pid) { # Parent
  $RemoteHost = "Parent";   $RemoteHost = "Parent";
  $ChildHash{$pid} = $RemoteHost;   $ChildHash{$pid} = $host;
    $HostToPid{$host}= $pid;
  sigprocmask(SIG_UNBLOCK, $sigset);   sigprocmask(SIG_UNBLOCK, $sigset);
   
     } else { # child.      } else { # child.
Line 1577  ShowStatus("Forking node servers"); Line 1504  ShowStatus("Forking node servers");
   
 Log("CRITICAL", "--------------- Starting children ---------------");  Log("CRITICAL", "--------------- Starting children ---------------");
   
   LondConnection::ReadConfig;               # Read standard config files.
 my $HostIterator = LondConnection::GetHostIterator;  my $HostIterator = LondConnection::GetHostIterator;
 while (! $HostIterator->end()) {  while (! $HostIterator->end()) {
   
     my $hostentryref = $HostIterator->get();      my $hostentryref = $HostIterator->get();
     CreateChild($hostentryref->[0]);      CreateChild($hostentryref->[0]);
       $HostHash{$hostentryref->[0]} = $hostentryref->[4];
     $HostIterator->next();      $HostIterator->next();
 }  }
 $RemoteHost = "Parent Server";  $RemoteHost = "Parent Server";
Line 1604  while(1) { Line 1533  while(1) {
     my $deadchild = wait();      my $deadchild = wait();
     if(exists $ChildHash{$deadchild}) { # need to restart.      if(exists $ChildHash{$deadchild}) { # need to restart.
  my $deadhost = $ChildHash{$deadchild};   my $deadhost = $ChildHash{$deadchild};
    delete($HostToPid{$deadhost});
  delete($ChildHash{$deadchild});   delete($ChildHash{$deadchild});
  Log("WARNING","Lost child pid= ".$deadchild.   Log("WARNING","Lost child pid= ".$deadchild.
       "Connected to host ".$deadhost);        "Connected to host ".$deadhost);
Line 1656  Kills off (via sigint) children for host Line 1586  Kills off (via sigint) children for host
   
 =item  =item
   
 HUP's children for hosts that already exist (this just forces a status display  QUITs  children for hosts that already exist (this just forces a status display
 and resets the connection retry count for that host.  and resets the connection retry count for that host.
   
 =item  =item
Line 1667  the start of the master program and main Line 1597  the start of the master program and main
 =cut  =cut
   
 sub UpdateKids {  sub UpdateKids {
   
     Log("INFO", "Updating connections via SIGUSR2");      Log("INFO", "Updating connections via SIGUSR2");
   
       #  Just in case we need to kill our own lonc, we wait a few seconds to
       #  give it a chance to receive and relay lond's response to the 
       #  re-init command.
       #
   
       sleep(2); # Wait a couple of seconds.
   
       my %hosts;                   # Indexed by loncapa hostname, value=ip.
       
       # Need to re-read  the host table:
       
       
       LondConnection::ReadConfig();
       my $I = LondConnection::GetHostIterator;
       while (! $I->end()) {
    my $item = $I->get();
    $hosts{$item->[0]} = $item->[4];
    $I->next();
       }
   
       #  The logic below is written for clarity not for efficiency.
       #  Since I anticipate that this function is only rarely called, that's
       #  appropriate.  There are certainly ways to combine the loops below,
       #  and anyone wishing to obscure the logic is welcome to go for it.
       #  Note that we don't re-direct sigchild.  Instead we do what's needed
       #  to the data structures that keep track of children to ensure that
       #  when sigchild is honored, no new child is born.
       #
   
       #  For each existing child; if it's host doesn't exist, kill the child.
   
       foreach my $child (keys %ChildHash) {
    my $oldhost = $ChildHash{$child};
    if (!(exists $hosts{$oldhost})) {
       Log("CRITICAL", "Killing child for $oldhost  host no longer exists");
       delete $ChildHash{$child};
       delete $HostToPid{$oldhost};
       kill 'QUIT' => $child;
    }
       }
       # For each remaining existing child; if it's host's ip has changed,
       # Restart the child on the new IP.
   
       foreach my $child (keys %ChildHash) {
    my $oldhost = $ChildHash{$child};
    my $oldip   = $HostHash{$oldhost};
    if ($hosts{$oldhost} ne $oldip) {
   
       # kill the old child.
   
       Log("CRITICAL", "Killing child for $oldhost host ip has changed...");
       delete $ChildHash{$child};
       delete $HostToPid{$oldhost};
       kill 'QUIT' => $child;
   
       # Do the book-keeping needed to start a new child on the
       # new ip.
   
       $HostHash{$oldhost} = $hosts{$oldhost};
       CreateChild($oldhost);
    }
       }
       # Finally, for each new host, not in the host hash, create a
       # enter the host and create a new child.
       # Force a status display of any existing process.
   
       foreach my $host (keys %hosts) {
    if(!(exists $HostHash{$host})) {
       Log("INFO", "New host $host discovered in hosts.tab...");
       $HostHash{$host} = $hosts{$host};
       CreateChild($host);
    } else {
       kill 'HUP' => $HostToPid{$host};    # status display.
    }
       }
 }  }
   
   
Line 1686  sub Restart { Line 1693  sub Restart {
     Log("CRITICAL", "Restarting");      Log("CRITICAL", "Restarting");
     my $execdir = $perlvar{'lonDaemons'};      my $execdir = $perlvar{'lonDaemons'};
     unlink("$execdir/logs/lonc.pid");      unlink("$execdir/logs/lonc.pid");
     exec("$execdir/lonc");      exec("$execdir/loncnew");
 }  }
   
 =pod  =pod

Removed from v.1.25  
changed lines
  Added in v.1.34


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>