File:  [LON-CAPA] / doc / build / Attic / loncapasqldatabase.html
Revision 1.2: download - view: text, annotated - select for diffs
Wed Feb 7 13:02:38 2001 UTC (23 years, 5 months ago) by harris41
Branches: MAIN
CVS tags: HEAD
a first draft of where things are at right now -Scott

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

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