File:  [LON-CAPA] / doc / build / Attic / loncapasqldatabase.html
Revision 1.4: download - view: text, annotated - select for diffs
Sat Feb 10 18:38:37 2001 UTC (23 years, 4 months ago) by harris41
Branches: MAIN
CVS tags: HEAD
document is almost "complete" at least for current purposes.. -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/10/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: <UL>
   20: <LI>Current status of documentation</LI>
   21: <LI>Current status of implementation</LI>
   22: <LI>Purpose within LON-CAPA</LI>
   23: <LI>Installation</LI>
   24: <LI>Installation from source</LI>
   25: <LI>Configuration (automated)</LI>
   26: <LI>Manual configuration</LI>
   27: <LI>Testing</LI>
   28: <LI>Example sections of code relevant to LON-CAPA</LI>
   29: </UL>
   30: </P>
   31: <H3>Current status of documentation</H3>
   32: <P>
   33: I am going to begin documentation by inserting what notes
   34: I have into this file.  I will be subsequently rearranging
   35: them and editing them based on the tests that I conduct.
   36: I am trying to make sure that documentation, installation,
   37: and run-time issues are all consistent and correct.  The
   38: current status of everything is that it works and has
   39: been minimally tested, but things need to be cleaned up
   40: and checked again!
   41: </P>
   42: <H3>Current status of implementation</H3>
   43: <P>
   44: Right now, a lot of "feasibility" work has been done.
   45: Recipes for manual installation and configuration have
   46: been gathered.  Network connectivity of lond->lonsql->lond->lonc
   47: type tests have been performed.  A binary installation
   48: has been compiled in an RPM (LON-CAPA-mysql).
   49: The most lacking test in terms of feasibility has
   50: been looking at benchmarks to analyze the load at which
   51: the SQL database can efficiently allow many users to
   52: make simultaneous requests of the metadata database.
   53: </P>
   54: <P>
   55: Documentation has been pieced together over time.  But,
   56: as mentioned in the previous section, it needs an
   57: overhaul.
   58: </P>
   59: <P>
   60: The binary installation has some quirks associated with it.
   61: Some of the user permissions are wrong, although this is
   62: benign.  Also, other options of binary installation (such
   63: as using binary RPMs put together by others) were dismissed
   64: given the difficulty of getting differing combinations of
   65: these external RPMs to work together.
   66: </P>
   67: <P>
   68: Most configuration questions have been initially worked out
   69: to the point of getting this SQL software component working,
   70: however there may be more optimal approaches than currently
   71: exist.
   72: </P>
   73: <H3>Purpose within LON-CAPA</H3>
   74: <P>
   75: LON-CAPA is meant to distribute A LOT of educational content
   76: to A LOT of people.  It is ineffective to directly rely on contents
   77: within the ext2 filesystem to be speedily scanned for 
   78: on-the-fly searches of content descriptions.  (Simply put,
   79: it takes a cumbersome amount of time to open, read, analyze, and
   80: close thousands of files.)
   81: </P>
   82: <P>
   83: The solution is to hash-index various data fields that are
   84: descriptive of the educational resources on a LON-CAPA server
   85: machine.  Descriptive data fields are referred to as
   86: "metadata".  The question then arises as to how this metadata
   87: is handled in terms of the rest of the LON-CAPA network
   88: without burdening client and daemon processes.  I now
   89: answer this question in the format of Problem and Solution
   90: below.
   91: </P>
   92: <P>
   93: <PRE>
   94: PROBLEM SITUATION:
   95: 
   96:   If Server A wants data from Server B, Server A uses a lonc process to
   97:   send a database command to a Server B lond process.
   98:     lonc= loncapa client process    A-lonc= a lonc process on Server A
   99:     lond= loncapa daemon process
  100: 
  101:                  database command
  102:     A-lonc  --------TCP/IP----------------> B-lond
  103: 
  104:   The problem emerges that A-lonc and B-lond are kept waiting for the
  105:   MySQL server to "do its stuff", or in other words, perform the conceivably
  106:   sophisticated, data-intensive, time-sucking database transaction.  By tying
  107:   up a lonc and lond process, this significantly cripples the capabilities
  108:   of LON-CAPA servers. 
  109: 
  110:   While commercial databases have a variety of features that ATTEMPT to
  111:   deal with this, freeware databases are still experimenting and exploring
  112:   with different schemes with varying degrees of performance stability.
  113: 
  114: THE SOLUTION:
  115: 
  116:   A separate daemon process was created that B-lond works with to
  117:   handle database requests.  This daemon process is called "lonsql".
  118: 
  119:   So,
  120:                 database command
  121:   A-lonc  ---------TCP/IP-----------------> B-lond =====> B-lonsql
  122:          <---------------------------------/                |
  123:            "ok, I'll get back to you..."                    |
  124:                                                             |
  125:                                                             /
  126:   A-lond  <-------------------------------  B-lonc   <======
  127:            "Guess what? I have the result!"
  128: 
  129:   Of course, depending on success or failure, the messages may vary,
  130:   but the principle remains the same where a separate pool of children
  131:   processes (lonsql's) handle the MySQL database manipulations.
  132: </PRE>
  133: </P>
  134: <H3>Installation</H3>
  135: <P>
  136: Installation of the LON-CAPA SQL database normally occurs
  137: by default when using the LON-CAPA installation CD
  138: (see http://install.lon-capa.org).  It is installed
  139: as the LON-CAPA-mysql RPM.  This RPM encodes for the MySQL
  140: engine and related perl interfaces (Perl::DBI, Perl::Msql-Mysql).
  141: </P>
  142: <P>
  143: The three components of a MySQL installation for the
  144: LON-CAPA system are further described immediately below.
  145: <TABLE BORDER="0">
  146: <TR><TD COLSPAN="2"><STRONG>Perl::DBI module</STRONG>-
  147: the API "front-end"...</TD></TR>
  148: <TR><TD WIDTH="10%"></TD><TD>database interface module for organizing generic
  149: database commands which are independent of specific
  150: database implementation (such as MySQL, mSQL, Postgres, etc).
  151: </TD></TR>
  152: <TR><TD COLSPAN="2"><STRONG>Perl::MySQL module</STRONG>-
  153: the API "mid-section"...</TD></TR>
  154: <TR><TD WIDTH="10%"></TD><TD>the module to directly interface with the actual
  155: MySQL database engine</TD></TR>
  156: <TR><TD COLSPAN="2"><STRONG>MySQL database engine</STRONG>-
  157: the "back-end"...</TD></TR>
  158: <TR><TD WIDTH="10%"></TD><TD>the binary installation (compiled either
  159: from source or pre-compiled file listings) which provides the
  160: actual MySQL functionality on the system</TD></TR>
  161: </TABLE>
  162: </P>
  163: <H3>Installation from source</H3>
  164: <P>
  165: The following set of tarballs was found to work together
  166: properly on a LON-CAPA RedHat 6.2 system:
  167: <UL>
  168: <LI>DBI-1.13.tar.gz
  169: <LI>Msql-Mysql-modules-1.2209.tar.gz
  170: <LI>mysql-3.22.32.tar.gz
  171: </UL>
  172: </P>
  173: <P>
  174: Installation was simply a matter of following the instructions
  175: and typing the several "make" commands for each 
  176: </P>
  177: <H3>Configuration (automated)</H3>
  178: <P>
  179: Not yet developed.  This will be part of an interface
  180: present on LON-CAPA systems that can be launched by
  181: entering the command <TT>/usr/sbin/loncapaconfig</TT>.
  182: </P>
  183: <H3>Manual configuration</H3>
  184: <P>
  185: This is not complete.
  186: </P>
  187: <P>
  188: <STRONG>Starting the mysql daemon</STRONG>: Login on the Linux
  189: system as user 'www'.  Enter the command
  190: <TT>/usr/local/bin/safe_mysqld &</TT>
  191: </P>
  192: <P>
  193: <STRONG>Set a password for 'root'</STRONG>:
  194: <TT>/usr/local/bin/mysqladmin -u root password 'new-password'</TT>
  195: </P>
  196: <P>
  197: <STRONG>Adding a user</STRONG>:  Start the mysql daemon.  Login to the
  198: mysql system as root (<TT>mysql -u root -p mysql</TT>)
  199: and enter the right password (for instance 'newmysql').  Add the user
  200: www
  201: <PRE>
  202: INSERT INTO user (Host, User, Password)
  203: VALUES ('localhost','www',password('newmysql'));
  204: </PRE>
  205: </P>
  206: <P>
  207: <STRONG>Granting privileges to user 'www'</STRONG>:
  208: <PRE>
  209: GRANT ALL PRIVILEGES ON *.* TO www@localhost;
  210: FLUSH PRIVILEGES;
  211: </PRE>
  212: </P>
  213: <P>
  214: <STRONG>Set the SQL server to start upon system startup</STRONG>:
  215: Copy support-files/mysql.server to the right place on the system
  216: (/etc/rc.d/...).
  217: </P>
  218: <H3>Testing</H3>
  219: <P>
  220: Not yet documented or formalized.
  221: </P>
  222: <H3>Example sections of code relevant to LON-CAPA</H3>
  223: <P>
  224: </P>
  225: 
  226: <H1>Old notes</H1>
  227: 
  228: <H3>How to add a user to the SQL database</H3>
  229: <P>
  230: <PRE>
  231: start the mysql daemon as /usr/local/bin/safe_mysqld &
  232: Login as root: mysql -u root -p mysql
  233: enter the password as newmysql
  234: add the user www: grant all priveleges on *.* to www@localhost identified by 'newmysql' with grant option;
  235: 
  236: INSERT INTO user (Host, User, Password)
  237: VALUES ('localhost','www',password('newmysql'));
  238: 
  239: GRANT ALL PRIVILEGES ON *.* TO www@localhost;
  240: 
  241: FLUSH PRIVILEGES;
  242: 
  243: Here the user www has the right to grant privileges to other users.
  244: This can be changed if required with a simple update command on the grant tables
  245: 
  246: 
  247: /home/httpd/perl/perlsql/lonsql
  248: /usr/local/mysql/fakeclient
  249: </PRE>
  250: </P>
  251: <H3>To do</H3>
  252: <P>
  253: <PRE>
  254: This is the output from scripts/mysql_install_db...
  255: still some todo things (like support-files/mysql.server)
  256: 
  257: Creating db table
  258: Creating host table
  259: Creating user table
  260: Creating func table
  261: Creating tables_priv table
  262: Creating columns_priv table
  263: 
  264: To start mysqld at boot time you have to copy support-files/mysql.server
  265: to the right place for your system
  266: 
  267: PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
  268: This is done with:
  269: /usr/local/bin/mysqladmin -u root password 'new-password'
  270: See the manual for more instructions.
  271: 
  272: Please report any problems with the /usr/local/bin/mysqlbug script!
  273: 
  274: The latest information about MySQL is available on the web at http://www.mysql.com
  275: Support MySQL by buying support/licenses at http://www.tcx.se/license.htmy.
  276: </PRE>
  277: </P>
  278: <H3>Source Installation and Manual Configuration</H3>
  279: <P>
  280: <PRE>
  281: August, 29 2000; Scott Harrison; LON-CAPA
  282: 
  283: These are notes related to a Perl interface and MySQL server installation
  284: on Redhat 6.1 and 6.2 boxes.  (Guy Albertelli and Harsha Jagasia
  285: contributed significantly to this.)
  286: 
  287: ********************
  288: * MySQL COMPONENTS *
  289: ********************
  290: 
  291: There are three components to an effective MySQL installation for the
  292: LON-CAPA system.
  293: 
  294: Perl::DBI module- the API "front-end"...
  295:                   database interface module for organizing generic
  296:                   database commands which are independent of specific
  297:                   database implementation (such as MySQL, mSQL, Postgres, etc).
  298: 
  299: Perl::MySQL module- the API "mid-section"...
  300:                     the module to directly interface with the actual
  301:                     MySQL database engine
  302: 
  303: MySQL database engine- the "back-end"...
  304:                        the binary installation (compiled either from source
  305:                        or pre-compiled file listings) which provides the
  306:                        actual MySQL functionality on the system
  307: 
  308: RedHat Installation-
  309: 
  310: Initially done from source:
  311: DBI-1.13.tar.gz  Msql-Mysql-modules-1.2209.tar.gz  mysql-3.22.32.tar.gz
  312: 
  313: I am now using pre-compiled file listings.
  314: 
  315: There were problems with using the RedHat packages since the three
  316: different RedHat packages were somewhat noncompatible with each other
  317: in terms of expected file locations. (The Debian linux distribution,
  318: on the other hand, has a working set of these packages).
  319: 
  320: Regardless of how we install these three components, there still remain
  321: certain things which need to happen for the configuration.
  322: 
  323: *****************
  324: * CONFIGURATION *
  325: *****************
  326: 
  327: (Note: SOMEPASSWORD is actually set to another text string on the current
  328: LON-CAPA systems.)
  329: 
  330: Configuration is needed to generate the necessary functionality for the
  331: MySQL system with LON-CAPA.
  332: 
  333: The functionality needed can be understood from this example line
  334: of perl code from "lonsql".
  335: 
  336:    $dbh = DBI->connect(	"DBI:mysql:loncapa",
  337: 			"www",
  338: 			"SOMEPASSWORD",
  339: 			{ RaiseError =>0,PrintError=>0});
  340: 
  341: There is an obvious need to CONNECT to the database, and in order to do
  342: this, there must be:
  343:   a RUNNING mysql daemon;
  344:   a DATABASE named "loncapa";
  345:   a USER named "www";
  346:   and an ABILITY for LON-CAPA on one machine to access
  347:        SQL database on another machine;
  348:   
  349: So, here are some notes on implementing these configurations.
  350: 
  351: ** RUNNING mysql daemon (safe_mysqld method)
  352: 
  353: The recommended way to run the MySQL daemon is as a non-root user
  354: (probably www)...
  355: 
  356: so, 1) login as user www on the linux machine
  357:     2) start the mysql daemon as /usr/local/bin/safe_mysqld &
  358: 
  359: safe_mysqld only works if the local installation of MySQL is set to the
  360: right directory permissions which I found to be:
  361: chown www:users /usr/local/var/mysql
  362: chown www:users /usr/local/lib/mysql
  363: chown -R www:users /usr/local/mysql
  364: chown www:users /usr/local/include/mysql
  365: chown www:users /usr/local/var
  366: 
  367: ** DATABASE named "loncapa"
  368: 
  369: As user www, run this command
  370:     mysql -u root -p mysql
  371: enter the password as SOMEPASSWORD
  372: 
  373: This allows you to manually enter MySQL commands.
  374: The MySQL command to generate the loncapa DATABASE is:
  375: 
  376: CREATE DATABASE 'loncapa';
  377: 
  378: ** USER named "www"
  379: 
  380: As user www, run this command
  381:     mysql -u root -p mysql
  382: enter the password as SOMEPASSWORD
  383: 
  384: To add the user www to the MySQL server, and grant all
  385: privileges on *.* to www@localhost identified by 'SOMEPASSWORD'
  386: with grant option;
  387: 
  388: INSERT INTO user (Host, User, Password)
  389: VALUES ('localhost','www',password('SOMEPASSWORD'));
  390: 
  391: GRANT ALL PRIVILEGES ON *.* TO www@localhost;
  392: 
  393: FLUSH PRIVILEGES;
  394: 
  395: ** ABILITY for LON-CAPA machines to communicate with SQL databases on
  396:    other LON-CAPA machines
  397: 
  398: This is a little more intricate than might first be expected (and I probably
  399: won't do a perfect job reciting everything in this short synopsis).  Because
  400: LON-CAPA machines will likely be handling many SQL requests at a time,
  401: there were some problems with current MySQL capabilities.
  402: 
  403: PROBLEM SITUATION:
  404: 
  405:   If Server A wants data from Server B, Server A uses a lonc process to
  406:   send a database command to a Server B lond process.
  407:     lonc= loncapa client process    A-lonc= a lonc process on Server A
  408:     lond= loncapa daemon process
  409: 
  410:                  database command
  411:     A-lonc  --------TCP/IP----------------> B-lond
  412: 
  413:   The problem emerges that A-lonc and B-lond are kept waiting for the
  414:   MySQL server to "do its stuff", or in other words, perform the conceivably
  415:   sophisticated, data-intensive, time-sucking database transaction.  By tying
  416:   up a lonc and lond process, this significantly cripples the capabilities
  417:   of LON-CAPA servers. 
  418: 
  419:   While commercial databases have a variety of features that ATTEMPT to
  420:   deal with this, freeware databases are still experimenting and exploring
  421:   with different schemes with varying degrees of performance stability.
  422: 
  423: THE SOLUTION:
  424: 
  425:   A separate daemon process was created that B-lond works with to
  426:   handle database requests.  This daemon process is called "lonsql".
  427: 
  428:   So,
  429:                 database command
  430:   A-lonc  ---------TCP/IP-----------------> B-lond =====> B-lonsql
  431:          <---------------------------------/                |
  432:            "ok, I'll get back to you..."                    |
  433:                                                             |
  434:                                                             /
  435:   A-lond  <-------------------------------  B-lonc   <======
  436:            "Guess what? I have the result!"
  437: 
  438:   Of course, depending on success or failure, the messages may vary,
  439:   but the principle remains the same where a separate pool of children
  440:   processes (lonsql's) handle the MySQL database manipulations.
  441: 
  442: Here are excerpts of code which implement the above handling:
  443: 
  444: **LONSQL
  445: 
  446: A subroutine from "lonsql" which establishes a child process for handling
  447: database interactions.
  448: 
  449: sub make_new_child {
  450:     my $pid;
  451:     my $sigset;
  452:     
  453:     # block signal for fork
  454:     $sigset = POSIX::SigSet->new(SIGINT);
  455:     sigprocmask(SIG_BLOCK, $sigset)
  456:         or die "Can't block SIGINT for fork: $!\n";
  457:     
  458:     die "fork: $!" unless defined ($pid = fork);
  459:     
  460:     if ($pid) {
  461:         # Parent records the child's birth and returns.
  462:         sigprocmask(SIG_UNBLOCK, $sigset)
  463:             or die "Can't unblock SIGINT for fork: $!\n";
  464:         $children{$pid} = 1;
  465:         $children++;
  466:         return;
  467:     } else {
  468:         # Child can *not* return from this subroutine.
  469:         $SIG{INT} = 'DEFAULT';      # make SIGINT kill us as it did before
  470:     
  471:         # unblock signals
  472:         sigprocmask(SIG_UNBLOCK, $sigset)
  473:             or die "Can't unblock SIGINT for fork: $!\n";
  474: 	
  475: 	
  476:         #open database handle
  477: 	# making dbh global to avoid garbage collector
  478: 	unless (
  479: 		$dbh = DBI->connect("DBI:mysql:loncapa","www","SOMEPASSWORD",{ RaiseError =>0,PrintError=>0})
  480: 		) { 
  481: 	            my $st=120+int(rand(240));
  482: 		    &logthis("<font color=blue>WARNING: Couldn't connect to database  ($st secs): $@</font>");
  483: 		    print "database handle error\n";
  484: 		    sleep($st);
  485: 		    exit;
  486: 
  487: 	  };
  488: 	# make sure that a database disconnection occurs with ending kill signals
  489: 	$SIG{TERM}=$SIG{INT}=$SIG{QUIT}=$SIG{__DIE__}=\&DISCONNECT;
  490: 
  491:         # handle connections until we've reached $MAX_CLIENTS_PER_CHILD
  492:         for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {
  493:             $client = $server->accept()     or last;
  494:             
  495:             # do something with the connection
  496: 	    $run = $run+1;
  497: 	    my $userinput = <$client>;
  498: 	    chomp($userinput);
  499: 	    	    
  500: 	    my ($conserver,$querytmp)=split(/&/,$userinput);
  501: 	    my $query=unescape($querytmp);
  502: 
  503:             #send query id which is pid_unixdatetime_runningcounter
  504: 	    $queryid = $thisserver;
  505: 	    $queryid .="_".($$)."_";
  506: 	    $queryid .= time."_";
  507: 	    $queryid .= $run;
  508: 	    print $client "$queryid\n";
  509: 	    
  510:             #prepare and execute the query
  511: 	    my $sth = $dbh->prepare($query);
  512: 	    my $result;
  513: 	    unless ($sth->execute())
  514: 	    {
  515: 		&logthis("<font color=blue>WARNING: Could not retrieve from database: $@</font>");
  516: 		$result="";
  517: 	    }
  518: 	    else {
  519: 		my $r1=$sth->fetchall_arrayref;
  520: 		my @r2; map {my $a=$_; my @b=map {escape($_)} @$a; push @r2,join(",", @b)} (@$r1);
  521: 		$result=join("&",@r2) . "\n";
  522: 	    }
  523:             &reply("queryreply:$queryid:$result",$conserver);
  524: 
  525:         }
  526:     
  527:         # tidy up gracefully and finish
  528: 	
  529:         #close the database handle
  530: 	$dbh->disconnect
  531: 	   or &logthis("<font color=blue>WARNING: Couldn't disconnect from database  $DBI::errstr ($st secs): $@</font>");
  532:     
  533:         # this exit is VERY important, otherwise the child will become
  534:         # a producer of more and more children, forking yourself into
  535:         # process death.
  536:         exit;
  537:     }
  538: }
  539: 
  540: ** LOND enabling of MySQL requestsw
  541: 
  542:   This code is part of every lond child process in the way that it parses command request syntax
  543:   sent to it from lonc processes.  querysend corresponds to B-lonc sending the result of the query.
  544:   queryreply corresponds to B-lond indicating that it has received the request and will start the
  545:   database transaction (it returns "ok" to A-lonc ($client)).
  546: 
  547: # ------------------------------------------------------------------- querysend
  548:                    } elsif ($userinput =~ /^querysend/) {
  549:                        my ($cmd,$query)=split(/:/,$userinput);
  550: 		       $query=~s/\n*$//g;
  551:                      print $client sqlreply("$hostid{$clientip}\&$query")."\n";
  552: # ------------------------------------------------------------------ queryreply
  553:                    } elsif ($userinput =~ /^queryreply/) {
  554:                        my ($cmd,$id,$reply)=split(/:/,$userinput); 
  555: 		       my $store;
  556:                        my $execdir=$perlvar{'lonDaemons'};
  557:                        if ($store=IO::File->new(">$execdir/tmp/$id")) {
  558: 			   print $store $reply;
  559: 			   close $store;
  560: 			   print $client "ok\n";
  561: 		       }
  562: 		       else {
  563: 			   print $client "error:$!\n";
  564: 		       }
  565: 
  566: 
  567: 
  568: ** TEST the database connection with my current tester.pl code which mimics what command will eventually be
  569:    sent through lonc.
  570: 
  571: $reply=reply(
  572:     "querysend:SELECT * FROM general_information WHERE Id='AAAAA'",$lonID);
  573: </PRE>
  574: </P>
  575: </BODY>
  576: </HTML>

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