--- loncom/lonsql 2002/08/06 13:48:47 1.51 +++ loncom/lonsql 2003/07/30 16:49:27 1.57 @@ -3,7 +3,7 @@ # The LearningOnline Network # lonsql - LON TCP-MySQL-Server Daemon for handling database requests. # -# $Id: lonsql,v 1.51 2002/08/06 13:48:47 matthew Exp $ +# $Id: lonsql,v 1.57 2003/07/30 16:49:27 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -39,10 +39,95 @@ lonsql - LON TCP-MySQL-Server Daemon for This script should be run as user=www. Note that a lonsql.pid file contains the pid of the parent process. -=head1 DESCRIPTION +=head1 OVERVIEW -lonsql is many things to many people. To me, it is a source file in need -of documentation. +The SQL database in LON-CAPA is used for catalog searches against +resource metadata only. The authoritative version of the resource +metadata is an XML-file on the normal file system (same file name as +resource plus ".meta"). The SQL-database is a cache of these files, +and can be reconstructed from the XML files at any time. + +The current database is implemented assuming a non-adjustable +architecture involving these data fields (specific to each version of +a resource). + +=over 4 + +=item * title + +=item * author + +=item * subject + +=item * notes + +=item * abstract + +=item * mime + +=item * language + +=item * creationdate + +=item * lastrevisiondate + +=item * owner + +=item * copyright + +=back + +=head2 Purpose within LON-CAPA + +LON-CAPA is meant to distribute A LOT of educational content to A LOT +of people. It is ineffective to directly rely on contents within the +ext2 filesystem to be speedily scanned for on-the-fly searches of +content descriptions. (Simply put, it takes a cumbersome amount of +time to open, read, analyze, and close thousands of files.) + +The solution is to index various data fields that are descriptive of +the educational resources on a LON-CAPA server machine in a +database. Descriptive data fields are referred to as "metadata". The +question then arises as to how this metadata is handled in terms of +the rest of the LON-CAPA network without burdening client and daemon +processes. + +The obvious solution, using lonc to send a query to a lond process, +doesn't work so well in general as you can see in the following +example: + + lonc= loncapa client process A-lonc= a lonc process on Server A + lond= loncapa daemon process + + database command + A-lonc --------TCP/IP----------------> B-lond + +The problem emerges that A-lonc and B-lond are kept waiting for the +MySQL server to "do its stuff", or in other words, perform the +conceivably sophisticated, data-intensive, time-sucking database +transaction. By tying up a lonc and lond process, this significantly +cripples the capabilities of LON-CAPA servers. + +The solution is to offload the work onto another process, and use +lonc and lond just for requests and notifications of completed +processing: + + database command + + A-lonc ---------TCP/IP-----------------> B-lond =====> B-lonsql + <---------------------------------/ | + "ok, I'll get back to you..." | + | + / + A-lond <------------------------------- B-lonc <====== + "Guess what? I have the result!" + +Of course, depending on success or failure, the messages may vary, but +the principle remains the same where a separate pool of children +processes (lonsql's) handle the MySQL database manipulations. + +Thus, lonc and lond spend effectively no time waiting on results from +the database. =head1 Internals @@ -116,76 +201,6 @@ my $MAX_CLIENTS_PER_CHILD = 5; # numb my %children = (); # keys are current child process IDs my $children = 0; # current number of children -######################################################## -######################################################## - -=pod - -=item Functions required for forking - -=over 4 - -=item REAPER - -REAPER takes care of dead children. - -=item HUNTSMAN - -Signal handler for SIGINT. - -=item HUPSMAN - -Signal handler for SIGHUP - -=item DISCONNECT - -Disconnects from database. - -=back - -=cut - -######################################################## -######################################################## -sub REAPER { # takes care of dead children - $SIG{CHLD} = \&REAPER; - my $pid = wait; - $children --; - &logthis("Child $pid died"); - delete $children{$pid}; -} - -sub HUNTSMAN { # signal handler for SIGINT - local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children - kill 'INT' => keys %children; - my $execdir=$perlvar{'lonDaemons'}; - unlink("$execdir/logs/lonsql.pid"); - &logthis("CRITICAL: Shutting down"); - $unixsock = "mysqlsock"; - my $port="$perlvar{'lonSockDir'}/$unixsock"; - unlink($port); - exit; # clean up with dignity -} - -sub HUPSMAN { # signal handler for SIGHUP - local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children - kill 'INT' => keys %children; - close($server); # free up socket - &logthis("CRITICAL: Restarting"); - my $execdir=$perlvar{'lonDaemons'}; - $unixsock = "mysqlsock"; - my $port="$perlvar{'lonSockDir'}/$unixsock"; - unlink($port); - exec("$execdir/lonsql"); # here we go again -} - -sub DISCONNECT { - $dbh->disconnect or - &logthis("WARNING: Couldn't disconnect from database ". - " $DBI::errstr : $@"); - exit; -} - ################################################################### ################################################################### @@ -222,8 +237,7 @@ my $run =0; # running count # # Read loncapa_apache.conf and loncapa.conf # -my $perlvarref=LONCAPA::Configuration::read_conf('loncapa_apache.conf', - 'loncapa.conf'); +my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf'); my %perlvar=%{$perlvarref}; # # Make sure that database can be accessed @@ -237,10 +251,16 @@ unless ($dbh = DBI->connect("DBI:mysql:l my $subj="LON: $perlvar{'lonHostID'} Cannot connect to database!"; system("echo 'Cannot connect to MySQL database!' |". " mailto $emailto -s '$subj' > /dev/null"); + + open(SMP,'>/home/httpd/html/lon-status/mysql.txt'); + print SMP 'time='.time.'&mysql=defunct'."\n"; + close(SMP); + exit 1; } else { $dbh->disconnect; } + # # Check if other instance running # @@ -251,6 +271,7 @@ if (-e $pidfile) { chomp($pide); if (kill 0 => $pide) { die "already running"; } } + # # Read hosts file # @@ -268,6 +289,7 @@ while (my $configline=) { close(CONFIG); # $PREFORK=int($PREFORK/4); + # # Create a socket to talk to lond # @@ -280,14 +302,15 @@ unless ($server=IO::Socket::UNIX->new(Lo Listen => 10)) { print "in socket error:$@\n"; } -######################################################## -######################################################## + # # Fork once and dissociate +# my $fpid=fork; exit if $fpid; die "Couldn't fork: $!" unless defined ($fpid); POSIX::setsid() or die "Can't start new session: $!"; + # # Write our PID on disk my $execdir=$perlvar{'lonDaemons'}; @@ -295,6 +318,7 @@ open (PIDSAVE,">$execdir/logs/lonsql.pid print PIDSAVE "$$\n"; close(PIDSAVE); &logthis("CRITICAL: ---------- Starting ----------"); + # # Ignore signals generated during initial startup $SIG{HUP}=$SIG{USR1}='IGNORE'; @@ -303,11 +327,13 @@ $SIG{HUP}=$SIG{USR1}='IGNORE'; for (1 .. $PREFORK) { make_new_child(); } + # # Install signal handlers. $SIG{CHLD} = \&REAPER; $SIG{INT} = $SIG{TERM} = \&HUNTSMAN; $SIG{HUP} = \&HUPSMAN; + # # And maintain the population. while (1) { @@ -586,7 +612,7 @@ Writes $message to the logfile. sub logthis { my $message=shift; my $execdir=$perlvar{'lonDaemons'}; - my $fh=IO::File->new(">>$execdir/logs/lonsqlfinal.log"); + my $fh=IO::File->new(">>$execdir/logs/lonsql.log"); my $now=time; my $local=localtime($now); print $fh "$local ($$): $message\n"; @@ -874,6 +900,76 @@ sub userlog { return join('&',sort(@results)); } +######################################################## +######################################################## + +=pod + +=item Functions required for forking + +=over 4 + +=item REAPER + +REAPER takes care of dead children. + +=item HUNTSMAN + +Signal handler for SIGINT. + +=item HUPSMAN + +Signal handler for SIGHUP + +=item DISCONNECT + +Disconnects from database. + +=back + +=cut + +######################################################## +######################################################## +sub REAPER { # takes care of dead children + $SIG{CHLD} = \&REAPER; + my $pid = wait; + $children --; + &logthis("Child $pid died"); + delete $children{$pid}; +} + +sub HUNTSMAN { # signal handler for SIGINT + local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children + kill 'INT' => keys %children; + my $execdir=$perlvar{'lonDaemons'}; + unlink("$execdir/logs/lonsql.pid"); + &logthis("CRITICAL: Shutting down"); + $unixsock = "mysqlsock"; + my $port="$perlvar{'lonSockDir'}/$unixsock"; + unlink($port); + exit; # clean up with dignity +} + +sub HUPSMAN { # signal handler for SIGHUP + local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children + kill 'INT' => keys %children; + close($server); # free up socket + &logthis("CRITICAL: Restarting"); + my $execdir=$perlvar{'lonDaemons'}; + $unixsock = "mysqlsock"; + my $port="$perlvar{'lonSockDir'}/$unixsock"; + unlink($port); + exec("$execdir/lonsql"); # here we go again +} + +sub DISCONNECT { + $dbh->disconnect or + &logthis("WARNING: Couldn't disconnect from database ". + " $DBI::errstr : $@"); + exit; +} +