Annotation of doc/mysql_note.txt, revision 1.1

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: 
        !            96: CREATE DATABASE 'loncapa';
        !            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>