version 1.55, 2003/02/03 13:43:38
|
version 1.68, 2005/11/07 15:43:03
|
Line 39 lonsql - LON TCP-MySQL-Server Daemon for
|
Line 39 lonsql - LON TCP-MySQL-Server Daemon for
|
This script should be run as user=www. |
This script should be run as user=www. |
Note that a lonsql.pid file contains the pid of the parent process. |
Note that a lonsql.pid file contains the pid of the parent process. |
|
|
=head1 DESCRIPTION |
=head1 OVERVIEW |
|
|
lonsql is |
=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 |
=head1 Internals |
|
|
Line 53 use strict;
|
Line 103 use strict;
|
|
|
use lib '/home/httpd/lib/perl/'; |
use lib '/home/httpd/lib/perl/'; |
use LONCAPA::Configuration; |
use LONCAPA::Configuration; |
|
use LONCAPA::lonmetadata(); |
|
|
use IO::Socket; |
use IO::Socket; |
use Symbol; |
use Symbol; |
Line 64 use Fcntl;
|
Line 115 use Fcntl;
|
use Tie::RefHash; |
use Tie::RefHash; |
use DBI; |
use DBI; |
use File::Find; |
use File::Find; |
|
use localenroll; |
|
|
######################################################## |
######################################################## |
######################################################## |
######################################################## |
Line 154 my $run =0; # running count
|
Line 206 my $run =0; # running count
|
my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf'); |
my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf'); |
my %perlvar=%{$perlvarref}; |
my %perlvar=%{$perlvarref}; |
# |
# |
|
# Write the /home/www/.my.cnf file |
|
my $conf_file = '/home/www/.my.cnf'; |
|
if (! -e $conf_file) { |
|
if (open MYCNF, ">$conf_file") { |
|
print MYCNF <<"ENDMYCNF"; |
|
[client] |
|
user=www |
|
password=$perlvar{'lonSqlAccess'} |
|
ENDMYCNF |
|
close MYCNF; |
|
} else { |
|
warn "Unable to write $conf_file, continuing"; |
|
} |
|
} |
|
|
|
|
|
# |
# Make sure that database can be accessed |
# Make sure that database can be accessed |
# |
# |
my $dbh; |
my $dbh; |
Line 165 unless ($dbh = DBI->connect("DBI:mysql:l
|
Line 234 unless ($dbh = DBI->connect("DBI:mysql:l
|
my $subj="LON: $perlvar{'lonHostID'} Cannot connect to database!"; |
my $subj="LON: $perlvar{'lonHostID'} Cannot connect to database!"; |
system("echo 'Cannot connect to MySQL database!' |". |
system("echo 'Cannot connect to MySQL database!' |". |
" mailto $emailto -s '$subj' > /dev/null"); |
" 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; |
exit 1; |
} else { |
} else { |
|
unlink('/home/httpd/html/lon-status/mysql.txt'); |
$dbh->disconnect; |
$dbh->disconnect; |
} |
} |
|
|
Line 184 if (-e $pidfile) {
|
Line 259 if (-e $pidfile) {
|
# |
# |
# Read hosts file |
# Read hosts file |
# |
# |
my %hostip; |
|
my $thisserver; |
my $thisserver; |
my $PREFORK=4; # number of children to maintain, at least four spare |
my $PREFORK=4; # number of children to maintain, at least four spare |
open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file"; |
open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file"; |
while (my $configline=<CONFIG>) { |
while (my $configline=<CONFIG>) { |
my ($id,$domain,$role,$name,$ip)=split(/:/,$configline); |
my ($id,$domain,$role,$name)=split(/:/,$configline); |
chomp($ip); |
$name=~s/\s//g; |
$hostip{$ip}=$id; |
|
$thisserver=$name if ($id eq $perlvar{'lonHostID'}); |
$thisserver=$name if ($id eq $perlvar{'lonHostID'}); |
$PREFORK++; |
#$PREFORK++; |
} |
} |
close(CONFIG); |
close(CONFIG); |
# |
# |
$PREFORK=int($PREFORK/4); |
#$PREFORK=int($PREFORK/4); |
|
|
# |
# |
# Create a socket to talk to lond |
# Create a socket to talk to lond |
Line 226 my $execdir=$perlvar{'lonDaemons'};
|
Line 299 my $execdir=$perlvar{'lonDaemons'};
|
open (PIDSAVE,">$execdir/logs/lonsql.pid"); |
open (PIDSAVE,">$execdir/logs/lonsql.pid"); |
print PIDSAVE "$$\n"; |
print PIDSAVE "$$\n"; |
close(PIDSAVE); |
close(PIDSAVE); |
&logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>"); |
&logthis("<font color='red'>CRITICAL: ---------- Starting ----------</font>"); |
|
|
# |
# |
# Ignore signals generated during initial startup |
# Ignore signals generated during initial startup |
Line 297 sub make_new_child {
|
Line 370 sub make_new_child {
|
$perlvar{'lonSqlAccess'}, |
$perlvar{'lonSqlAccess'}, |
{ RaiseError =>0,PrintError=>0})) { |
{ RaiseError =>0,PrintError=>0})) { |
sleep(10+int(rand(20))); |
sleep(10+int(rand(20))); |
&logthis("<font color=blue>WARNING: Couldn't connect to database". |
&logthis("<font color='blue'>WARNING: Couldn't connect to database". |
": $@</font>"); |
": $@</font>"); |
# "($st secs): $@</font>"); |
# "($st secs): $@</font>"); |
print "database handle error\n"; |
print "database handle error\n"; |
Line 325 sub make_new_child {
|
Line 398 sub make_new_child {
|
$queryid .= $run; |
$queryid .= $run; |
print $client "$queryid\n"; |
print $client "$queryid\n"; |
# |
# |
&logthis("QUERY: $query - $arg1 - $arg2 - $arg3"); |
# &logthis("QUERY: $query - $arg1 - $arg2 - $arg3"); |
sleep 1; |
sleep 1; |
# |
# |
my $result=''; |
my $result=''; |
Line 350 sub make_new_child {
|
Line 423 sub make_new_child {
|
$result='no_such_file'; |
$result='no_such_file'; |
} |
} |
# end of log query |
# end of log query |
|
} elsif ($query eq 'fetchenrollment') { |
|
# retrieve institutional class lists |
|
my $dom = &unescape($arg1); |
|
my %affiliates = (); |
|
my %replies = (); |
|
my $locresult = ''; |
|
my $querystr = &unescape($arg3); |
|
foreach (split/%%/,$querystr) { |
|
if (/^([^=]+)=([^=]+)$/) { |
|
@{$affiliates{$1}} = split/,/,$2; |
|
} |
|
} |
|
$locresult = &localenroll::fetch_enrollment($dom,\%affiliates,\%replies); |
|
$result = &escape($locresult.':'); |
|
if ($locresult) { |
|
$result .= &escape(join(':',map{$_.'='.$replies{$_}} keys %replies)); |
|
} |
|
} elsif ($query eq 'prepare activity log') { |
|
my ($cid,$domain) = map {&unescape($_);} ($arg1,$arg2); |
|
&logthis('preparing activity log tables for '.$cid); |
|
my $command = |
|
qq{$perlvar{'lonDaemons'}/parse_activity_log.pl -course=$cid -domain=$domain}; |
|
system($command); |
|
&logthis($command); |
|
my $returnvalue = $?>>8; |
|
if ($returnvalue) { |
|
$result = 'error: parse_activity_log.pl returned '. |
|
$returnvalue; |
|
} else { |
|
$result = 'success'; |
|
} |
} else { |
} else { |
# Do an sql query |
# Do an sql query |
$result = &do_sql_query($query,$arg1,$arg2); |
$result = &do_sql_query($query,$arg1,$arg2); |
Line 357 sub make_new_child {
|
Line 461 sub make_new_child {
|
# result does not need to be escaped because it has already been |
# result does not need to be escaped because it has already been |
# escaped. |
# escaped. |
#$result=&escape($result); |
#$result=&escape($result); |
# reply with result, append \n unless already there |
|
$result.="\n" unless ($result=~/\n$/); |
|
&reply("queryreply:$queryid:$result",$conserver); |
&reply("queryreply:$queryid:$result",$conserver); |
} |
} |
# tidy up gracefully and finish |
# tidy up gracefully and finish |
# |
# |
# close the database handle |
# close the database handle |
$dbh->disconnect |
$dbh->disconnect |
or &logthis("<font color=blue>WARNING: Couldn't disconnect". |
or &logthis("<font color='blue'>WARNING: Couldn't disconnect". |
" from database $DBI::errstr : $@</font>"); |
" from database $DBI::errstr : $@</font>"); |
# this exit is VERY important, otherwise the child will become |
# this exit is VERY important, otherwise the child will become |
# a producer of more and more children, forking yourself into |
# a producer of more and more children, forking yourself into |
Line 406 sub process_file {
|
Line 508 sub process_file {
|
|
|
sub do_sql_query { |
sub do_sql_query { |
my ($query,$custom,$customshow) = @_; |
my ($query,$custom,$customshow) = @_; |
|
&logthis('doing query '.$query); |
$custom = &unescape($custom); |
$custom = &unescape($custom); |
$customshow = &unescape($customshow); |
$customshow = &unescape($customshow); |
# |
# |
Line 420 sub do_sql_query {
|
Line 523 sub do_sql_query {
|
#prepare and execute the query |
#prepare and execute the query |
my $sth = $dbh->prepare($query); |
my $sth = $dbh->prepare($query); |
unless ($sth->execute()) { |
unless ($sth->execute()) { |
&logthis("<font color=blue>WARNING: ". |
&logthis('<font color="blue">'. |
"Could not retrieve from database: $@</font>"); |
'WARNING: Could not retrieve from database:'. |
|
$sth->errstr().'</font>'); |
} else { |
} else { |
my $aref=$sth->fetchall_arrayref; |
my $aref=$sth->fetchall_arrayref; |
foreach my $row (@$aref) { |
foreach my $row (@$aref) { |
Line 853 sub HUNTSMAN { # si
|
Line 957 sub HUNTSMAN { # si
|
kill 'INT' => keys %children; |
kill 'INT' => keys %children; |
my $execdir=$perlvar{'lonDaemons'}; |
my $execdir=$perlvar{'lonDaemons'}; |
unlink("$execdir/logs/lonsql.pid"); |
unlink("$execdir/logs/lonsql.pid"); |
&logthis("<font color=red>CRITICAL: Shutting down</font>"); |
&logthis("<font color='red'>CRITICAL: Shutting down</font>"); |
$unixsock = "mysqlsock"; |
$unixsock = "mysqlsock"; |
my $port="$perlvar{'lonSockDir'}/$unixsock"; |
my $port="$perlvar{'lonSockDir'}/$unixsock"; |
unlink($port); |
unlink($port); |
Line 864 sub HUPSMAN { # sig
|
Line 968 sub HUPSMAN { # sig
|
local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children |
local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children |
kill 'INT' => keys %children; |
kill 'INT' => keys %children; |
close($server); # free up socket |
close($server); # free up socket |
&logthis("<font color=red>CRITICAL: Restarting</font>"); |
&logthis("<font color='red'>CRITICAL: Restarting</font>"); |
my $execdir=$perlvar{'lonDaemons'}; |
my $execdir=$perlvar{'lonDaemons'}; |
$unixsock = "mysqlsock"; |
$unixsock = "mysqlsock"; |
my $port="$perlvar{'lonSockDir'}/$unixsock"; |
my $port="$perlvar{'lonSockDir'}/$unixsock"; |
Line 874 sub HUPSMAN { # sig
|
Line 978 sub HUPSMAN { # sig
|
|
|
sub DISCONNECT { |
sub DISCONNECT { |
$dbh->disconnect or |
$dbh->disconnect or |
&logthis("<font color=blue>WARNING: Couldn't disconnect from database ". |
&logthis("<font color='blue'>WARNING: Couldn't disconnect from database ". |
" $DBI::errstr : $@</font>"); |
" $DBI::errstr : $@</font>"); |
exit; |
exit; |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------- POD (plain old documentation, CPAN style) |
|
|
|
=pod |
=pod |
|
|
=back |
=back |