Annotation of doc/mysql_note.txt, revision 1.2

1.1       harris41    1: August, 29 2000; Scott Harrison; LON-CAPA
                      2: 
                      3: These are notes related to a Perl interface and MySQL server installation
                      4: on Redhat 6.1 and 6.2 boxes.  (Guy Albertelli and Harsha Jagasia
                      5: contributed significantly to this.)
                      6: 
                      7: ********************
                      8: * MySQL COMPONENTS *
                      9: ********************
                     10: 
                     11: There are three components to an effective MySQL installation for the
                     12: LON-CAPA system.
                     13: 
                     14: Perl::DBI module- the API "front-end"...
                     15:                   database interface module for organizing generic
                     16:                   database commands which are independent of specific
                     17:                   database implementation (such as MySQL, mSQL, Postgres, etc).
                     18: 
                     19: Perl::MySQL module- the API "mid-section"...
                     20:                     the module to directly interface with the actual
                     21:                     MySQL database engine
                     22: 
                     23: MySQL database engine- the "back-end"...
                     24:                        the binary installation (compiled either from source
                     25:                        or pre-compiled file listings) which provides the
                     26:                        actual MySQL functionality on the system
                     27: 
                     28: RedHat Installation-
                     29: 
                     30: Initially done from source:
                     31: DBI-1.13.tar.gz  Msql-Mysql-modules-1.2209.tar.gz  mysql-3.22.32.tar.gz
                     32: 
                     33: I am now using pre-compiled file listings.
                     34: 
                     35: There were problems with using the RedHat packages since the three
                     36: different RedHat packages were somewhat noncompatible with each other
                     37: in terms of expected file locations. (The Debian linux distribution,
                     38: on the other hand, has a working set of these packages).
                     39: 
                     40: Regardless of how we install these three components, there still remain
                     41: certain things which need to happen for the configuration.
                     42: 
                     43: *****************
                     44: * CONFIGURATION *
                     45: *****************
                     46: 
                     47: (Note: SOMEPASSWORD is actually set to another text string on the current
                     48: LON-CAPA systems.)
                     49: 
                     50: Configuration is needed to generate the necessary functionality for the
                     51: MySQL system with LON-CAPA.
                     52: 
                     53: The functionality needed can be understood from this example line
                     54: of perl code from "lonsql".
                     55: 
                     56:    $dbh = DBI->connect(	"DBI:mysql:loncapa",
                     57: 			"www",
                     58: 			"SOMEPASSWORD",
                     59: 			{ RaiseError =>0,PrintError=>0});
                     60: 
                     61: There is an obvious need to CONNECT to the database, and in order to do
                     62: this, there must be:
                     63:   a RUNNING mysql daemon;
                     64:   a DATABASE named "loncapa";
                     65:   a USER named "www";
                     66:   and an ABILITY for LON-CAPA on one machine to access
                     67:        SQL database on another machine;
                     68:   
                     69: So, here are some notes on implementing these configurations.
                     70: 
                     71: ** RUNNING mysql daemon (safe_mysqld method)
                     72: 
                     73: The recommended way to run the MySQL daemon is as a non-root user
                     74: (probably www)...
                     75: 
                     76: so, 1) login as user www on the linux machine
                     77:     2) start the mysql daemon as /usr/local/bin/safe_mysqld &
                     78: 
                     79: safe_mysqld only works if the local installation of MySQL is set to the
                     80: right directory permissions which I found to be:
                     81: chown www:users /usr/local/var/mysql
                     82: chown www:users /usr/local/lib/mysql
                     83: chown -R www:users /usr/local/mysql
                     84: chown www:users /usr/local/include/mysql
                     85: chown www:users /usr/local/var
                     86: 
                     87: ** DATABASE named "loncapa"
                     88: 
                     89: As user www, run this command
                     90:     mysql -u root -p mysql
                     91: enter the password as SOMEPASSWORD
                     92: 
                     93: This allows you to manually enter MySQL commands.
                     94: The MySQL command to generate the loncapa DATABASE is:
                     95: 
1.2     ! harris41   96: CREATE DATABASE loncapa;
1.1       harris41   97: 
                     98: ** USER named "www"
                     99: 
                    100: As user www, run this command
                    101:     mysql -u root -p mysql
                    102: enter the password as SOMEPASSWORD
                    103: 
                    104: To add the user www to the MySQL server, and grant all
                    105: privileges on *.* to www@localhost identified by 'SOMEPASSWORD'
                    106: with grant option;
                    107: 
                    108: INSERT INTO user (Host, User, Password)
                    109: VALUES ('localhost','www',password('SOMEPASSWORD'));
                    110: 
                    111: GRANT ALL PRIVILEGES ON *.* TO www@localhost;
                    112: 
                    113: FLUSH PRIVILEGES;
                    114: 
                    115: ** ABILITY for LON-CAPA machines to communicate with SQL databases on
                    116:    other LON-CAPA machines
                    117: 
                    118: This is a little more intricate than might first be expected (and I probably
                    119: won't do a perfect job reciting everything in this short synopsis).  Because
                    120: LON-CAPA machines will likely be handling many SQL requests at a time,
                    121: there were some problems with current MySQL capabilities.
                    122: 
                    123: PROBLEM SITUATION:
                    124: 
                    125:   If Server A wants data from Server B, Server A uses a lonc process to
                    126:   send a database command to a Server B lond process.
                    127:     lonc= loncapa client process    A-lonc= a lonc process on Server A
                    128:     lond= loncapa daemon process
                    129: 
                    130:                  database command
                    131:     A-lonc  --------TCP/IP----------------> B-lond
                    132: 
                    133:   The problem emerges that A-lonc and B-lond are kept waiting for the
                    134:   MySQL server to "do its stuff", or in other words, perform the conceivably
                    135:   sophisticated, data-intensive, time-sucking database transaction.  By tying
                    136:   up a lonc and lond process, this significantly cripples the capabilities
                    137:   of LON-CAPA servers. 
                    138: 
                    139:   While commercial databases have a variety of features that ATTEMPT to
                    140:   deal with this, freeware databases are still experimenting and exploring
                    141:   with different schemes with varying degrees of performance stability.
                    142: 
                    143: THE SOLUTION:
                    144: 
                    145:   A separate daemon process was created that B-lond works with to
                    146:   handle database requests.  This daemon process is called "lonsql".
                    147: 
                    148:   So,
                    149:                 database command
                    150:   A-lonc  ---------TCP/IP-----------------> B-lond =====> B-lonsql
                    151:          <---------------------------------/                |
                    152:            "ok, I'll get back to you..."                    |
                    153:                                                             |
                    154:                                                             /
                    155:   A-lond  <-------------------------------  B-lonc   <======
                    156:            "Guess what? I have the result!"
                    157: 
                    158:   Of course, depending on success or failure, the messages may vary,
                    159:   but the principle remains the same where a separate pool of children
                    160:   processes (lonsql's) handle the MySQL database manipulations.
                    161: 
                    162: Here are excerpts of code which implement the above handling:
                    163: 
                    164: **LONSQL
                    165: 
                    166: A subroutine from "lonsql" which establishes a child process for handling
                    167: database interactions.
                    168: 
                    169: sub make_new_child {
                    170:     my $pid;
                    171:     my $sigset;
                    172:     
                    173:     # block signal for fork
                    174:     $sigset = POSIX::SigSet->new(SIGINT);
                    175:     sigprocmask(SIG_BLOCK, $sigset)
                    176:         or die "Can't block SIGINT for fork: $!\n";
                    177:     
                    178:     die "fork: $!" unless defined ($pid = fork);
                    179:     
                    180:     if ($pid) {
                    181:         # Parent records the child's birth and returns.
                    182:         sigprocmask(SIG_UNBLOCK, $sigset)
                    183:             or die "Can't unblock SIGINT for fork: $!\n";
                    184:         $children{$pid} = 1;
                    185:         $children++;
                    186:         return;
                    187:     } else {
                    188:         # Child can *not* return from this subroutine.
                    189:         $SIG{INT} = 'DEFAULT';      # make SIGINT kill us as it did before
                    190:     
                    191:         # unblock signals
                    192:         sigprocmask(SIG_UNBLOCK, $sigset)
                    193:             or die "Can't unblock SIGINT for fork: $!\n";
                    194: 	
                    195: 	
                    196:         #open database handle
                    197: 	# making dbh global to avoid garbage collector
                    198: 	unless (
                    199: 		$dbh = DBI->connect("DBI:mysql:loncapa","www","SOMEPASSWORD",{ RaiseError =>0,PrintError=>0})
                    200: 		) { 
                    201: 	            my $st=120+int(rand(240));
                    202: 		    &logthis("<font color=blue>WARNING: Couldn't connect to database  ($st secs): $@</font>");
                    203: 		    print "database handle error\n";
                    204: 		    sleep($st);
                    205: 		    exit;
                    206: 
                    207: 	  };
                    208: 	# make sure that a database disconnection occurs with ending kill signals
                    209: 	$SIG{TERM}=$SIG{INT}=$SIG{QUIT}=$SIG{__DIE__}=\&DISCONNECT;
                    210: 
                    211:         # handle connections until we've reached $MAX_CLIENTS_PER_CHILD
                    212:         for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {
                    213:             $client = $server->accept()     or last;
                    214:             
                    215:             # do something with the connection
                    216: 	    $run = $run+1;
                    217: 	    my $userinput = <$client>;
                    218: 	    chomp($userinput);
                    219: 	    	    
                    220: 	    my ($conserver,$querytmp)=split(/&/,$userinput);
                    221: 	    my $query=unescape($querytmp);
                    222: 
                    223:             #send query id which is pid_unixdatetime_runningcounter
                    224: 	    $queryid = $thisserver;
                    225: 	    $queryid .="_".($$)."_";
                    226: 	    $queryid .= time."_";
                    227: 	    $queryid .= $run;
                    228: 	    print $client "$queryid\n";
                    229: 	    
                    230:             #prepare and execute the query
                    231: 	    my $sth = $dbh->prepare($query);
                    232: 	    my $result;
                    233: 	    unless ($sth->execute())
                    234: 	    {
                    235: 		&logthis("<font color=blue>WARNING: Could not retrieve from database: $@</font>");
                    236: 		$result="";
                    237: 	    }
                    238: 	    else {
                    239: 		my $r1=$sth->fetchall_arrayref;
                    240: 		my @r2; map {my $a=$_; my @b=map {escape($_)} @$a; push @r2,join(",", @b)} (@$r1);
                    241: 		$result=join("&",@r2) . "\n";
                    242: 	    }
                    243:             &reply("queryreply:$queryid:$result",$conserver);
                    244: 
                    245:         }
                    246:     
                    247:         # tidy up gracefully and finish
                    248: 	
                    249:         #close the database handle
                    250: 	$dbh->disconnect
                    251: 	   or &logthis("<font color=blue>WARNING: Couldn't disconnect from database  $DBI::errstr ($st secs): $@</font>");
                    252:     
                    253:         # this exit is VERY important, otherwise the child will become
                    254:         # a producer of more and more children, forking yourself into
                    255:         # process death.
                    256:         exit;
                    257:     }
                    258: }
                    259: 
                    260: ** LOND enabling of MySQL requestsw
                    261: 
                    262:   This code is part of every lond child process in the way that it parses command request syntax
                    263:   sent to it from lonc processes.  querysend corresponds to B-lonc sending the result of the query.
                    264:   queryreply corresponds to B-lond indicating that it has received the request and will start the
                    265:   database transaction (it returns "ok" to A-lonc ($client)).
                    266: 
                    267: # ------------------------------------------------------------------- querysend
                    268:                    } elsif ($userinput =~ /^querysend/) {
                    269:                        my ($cmd,$query)=split(/:/,$userinput);
                    270: 		       $query=~s/\n*$//g;
                    271:                      print $client sqlreply("$hostid{$clientip}\&$query")."\n";
                    272: # ------------------------------------------------------------------ queryreply
                    273:                    } elsif ($userinput =~ /^queryreply/) {
                    274:                        my ($cmd,$id,$reply)=split(/:/,$userinput); 
                    275: 		       my $store;
                    276:                        my $execdir=$perlvar{'lonDaemons'};
                    277:                        if ($store=IO::File->new(">$execdir/tmp/$id")) {
                    278: 			   print $store $reply;
                    279: 			   close $store;
                    280: 			   print $client "ok\n";
                    281: 		       }
                    282: 		       else {
                    283: 			   print $client "error:$!\n";
                    284: 		       }
                    285: 
                    286: 
                    287: 
                    288: ** TEST the database connection with my current tester.pl code which mimics what command will eventually be
                    289:    sent through lonc.
                    290: 
                    291: $reply=reply(
                    292:     "querysend:SELECT * FROM general_information WHERE Id='AAAAA'",$lonID);
                    293: 

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