File:  [LON-CAPA] / doc / Attic / mysql_note.txt
Revision 1.2: download - view: text, annotated - select for diffs
Thu Oct 4 14:13:01 2001 UTC (22 years, 6 months ago) by harris41
Branches: MAIN
CVS tags: version_0_4, stable_2002_spring, stable_2002_july, stable_2002_april, stable_2001_fall, STABLE, HEAD
minor sql command fix for latest version

    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>