Diff for /doc/build/Attic/loncapasqldatabase.html between versions 1.1 and 1.4

version 1.1, 2001/02/07 12:49:17 version 1.4, 2001/02/10 18:38:37
Line 8 Line 8
 Scott Harrison  Scott Harrison
 </P>  </P>
 <P>  <P>
 Last updated: 02/07/2001  Last updated: 02/10/2001
 </P>  </P>
   <P>
 This file describes issues associated with LON-CAPA  This file describes issues associated with LON-CAPA
 and a SQL database.  and a SQL database.
 </P>  </P>
 <P>  
 <H3>Latest HOWTO</H3>  <H3>Latest HOWTO</H3>
   <P>
   <UL>
   <LI>Current status of documentation</LI>
   <LI>Current status of implementation</LI>
   <LI>Purpose within LON-CAPA</LI>
   <LI>Installation</LI>
   <LI>Installation from source</LI>
   <LI>Configuration (automated)</LI>
   <LI>Manual configuration</LI>
   <LI>Testing</LI>
   <LI>Example sections of code relevant to LON-CAPA</LI>
   </UL>
   </P>
   <H3>Current status of documentation</H3>
   <P>
   I am going to begin documentation by inserting what notes
   I have into this file.  I will be subsequently rearranging
   them and editing them based on the tests that I conduct.
   I am trying to make sure that documentation, installation,
   and run-time issues are all consistent and correct.  The
   current status of everything is that it works and has
   been minimally tested, but things need to be cleaned up
   and checked again!
   </P>
   <H3>Current status of implementation</H3>
   <P>
   Right now, a lot of "feasibility" work has been done.
   Recipes for manual installation and configuration have
   been gathered.  Network connectivity of lond->lonsql->lond->lonc
   type tests have been performed.  A binary installation
   has been compiled in an RPM (LON-CAPA-mysql).
   The most lacking test in terms of feasibility has
   been looking at benchmarks to analyze the load at which
   the SQL database can efficiently allow many users to
   make simultaneous requests of the metadata database.
   </P>
   <P>
   Documentation has been pieced together over time.  But,
   as mentioned in the previous section, it needs an
   overhaul.
   </P>
   <P>
   The binary installation has some quirks associated with it.
   Some of the user permissions are wrong, although this is
   benign.  Also, other options of binary installation (such
   as using binary RPMs put together by others) were dismissed
   given the difficulty of getting differing combinations of
   these external RPMs to work together.
   </P>
   <P>
   Most configuration questions have been initially worked out
   to the point of getting this SQL software component working,
   however there may be more optimal approaches than currently
   exist.
   </P>
   <H3>Purpose within LON-CAPA</H3>
   <P>
   LON-CAPA is meant to distribute A LOT of educational content
   to A LOT of people.  It is ineffective to directly rely on contents
   within the ext2 filesystem to be speedily scanned for 
   on-the-fly searches of content descriptions.  (Simply put,
   it takes a cumbersome amount of time to open, read, analyze, and
   close thousands of files.)
   </P>
   <P>
   The solution is to hash-index various data fields that are
   descriptive of the educational resources on a LON-CAPA server
   machine.  Descriptive data fields are referred to as
   "metadata".  The question then arises as to how this metadata
   is handled in terms of the rest of the LON-CAPA network
   without burdening client and daemon processes.  I now
   answer this question in the format of Problem and Solution
   below.
   </P>
   <P>
   <PRE>
   PROBLEM SITUATION:
   
     If Server A wants data from Server B, Server A uses a lonc process to
     send a database command to a Server B lond process.
       lonc= loncapa client process    A-lonc= a lonc process on Server A
       lond= loncapa daemon process
   
                    database command
       A-lonc  --------TCP/IP----------------> B-lond
   
     The problem emerges that A-lonc and B-lond are kept waiting for the
     MySQL server to "do its stuff", or in other words, perform the conceivably
     sophisticated, data-intensive, time-sucking database transaction.  By tying
     up a lonc and lond process, this significantly cripples the capabilities
     of LON-CAPA servers. 
   
     While commercial databases have a variety of features that ATTEMPT to
     deal with this, freeware databases are still experimenting and exploring
     with different schemes with varying degrees of performance stability.
   
   THE SOLUTION:
   
     A separate daemon process was created that B-lond works with to
     handle database requests.  This daemon process is called "lonsql".
   
     So,
                   database command
     A-lonc  ---------TCP/IP-----------------> B-lond =====> B-lonsql
            <---------------------------------/                |
              "ok, I'll get back to you..."                    |
                                                               |
                                                               /
     A-lond  <-------------------------------  B-lonc   <======
              "Guess what? I have the result!"
   
     Of course, depending on success or failure, the messages may vary,
     but the principle remains the same where a separate pool of children
     processes (lonsql's) handle the MySQL database manipulations.
   </PRE>
   </P>
   <H3>Installation</H3>
   <P>
   Installation of the LON-CAPA SQL database normally occurs
   by default when using the LON-CAPA installation CD
   (see http://install.lon-capa.org).  It is installed
   as the LON-CAPA-mysql RPM.  This RPM encodes for the MySQL
   engine and related perl interfaces (Perl::DBI, Perl::Msql-Mysql).
   </P>
   <P>
   The three components of a MySQL installation for the
   LON-CAPA system are further described immediately below.
   <TABLE BORDER="0">
   <TR><TD COLSPAN="2"><STRONG>Perl::DBI module</STRONG>-
   the API "front-end"...</TD></TR>
   <TR><TD WIDTH="10%"></TD><TD>database interface module for organizing generic
   database commands which are independent of specific
   database implementation (such as MySQL, mSQL, Postgres, etc).
   </TD></TR>
   <TR><TD COLSPAN="2"><STRONG>Perl::MySQL module</STRONG>-
   the API "mid-section"...</TD></TR>
   <TR><TD WIDTH="10%"></TD><TD>the module to directly interface with the actual
   MySQL database engine</TD></TR>
   <TR><TD COLSPAN="2"><STRONG>MySQL database engine</STRONG>-
   the "back-end"...</TD></TR>
   <TR><TD WIDTH="10%"></TD><TD>the binary installation (compiled either
   from source or pre-compiled file listings) which provides the
   actual MySQL functionality on the system</TD></TR>
   </TABLE>
   </P>
   <H3>Installation from source</H3>
   <P>
   The following set of tarballs was found to work together
   properly on a LON-CAPA RedHat 6.2 system:
   <UL>
   <LI>DBI-1.13.tar.gz
   <LI>Msql-Mysql-modules-1.2209.tar.gz
   <LI>mysql-3.22.32.tar.gz
   </UL>
   </P>
   <P>
   Installation was simply a matter of following the instructions
   and typing the several "make" commands for each 
   </P>
   <H3>Configuration (automated)</H3>
   <P>
   Not yet developed.  This will be part of an interface
   present on LON-CAPA systems that can be launched by
   entering the command <TT>/usr/sbin/loncapaconfig</TT>.
   </P>
   <H3>Manual configuration</H3>
   <P>
   This is not complete.
   </P>
   <P>
   <STRONG>Starting the mysql daemon</STRONG>: Login on the Linux
   system as user 'www'.  Enter the command
   <TT>/usr/local/bin/safe_mysqld &</TT>
   </P>
   <P>
   <STRONG>Set a password for 'root'</STRONG>:
   <TT>/usr/local/bin/mysqladmin -u root password 'new-password'</TT>
   </P>
   <P>
   <STRONG>Adding a user</STRONG>:  Start the mysql daemon.  Login to the
   mysql system as root (<TT>mysql -u root -p mysql</TT>)
   and enter the right password (for instance 'newmysql').  Add the user
   www
   <PRE>
   INSERT INTO user (Host, User, Password)
   VALUES ('localhost','www',password('newmysql'));
   </PRE>
   </P>
   <P>
   <STRONG>Granting privileges to user 'www'</STRONG>:
   <PRE>
   GRANT ALL PRIVILEGES ON *.* TO www@localhost;
   FLUSH PRIVILEGES;
   </PRE>
   </P>
   <P>
   <STRONG>Set the SQL server to start upon system startup</STRONG>:
   Copy support-files/mysql.server to the right place on the system
   (/etc/rc.d/...).
   </P>
   <H3>Testing</H3>
   <P>
   Not yet documented or formalized.
   </P>
   <H3>Example sections of code relevant to LON-CAPA</H3>
   <P>
   </P>
   
   <H1>Old notes</H1>
   
   <H3>How to add a user to the SQL database</H3>
   <P>
   <PRE>
   start the mysql daemon as /usr/local/bin/safe_mysqld &
   Login as root: mysql -u root -p mysql
   enter the password as newmysql
   add the user www: grant all priveleges on *.* to www@localhost identified by 'newmysql' with grant option;
   
   INSERT INTO user (Host, User, Password)
   VALUES ('localhost','www',password('newmysql'));
   
   GRANT ALL PRIVILEGES ON *.* TO www@localhost;
   
   FLUSH PRIVILEGES;
   
   Here the user www has the right to grant privileges to other users.
   This can be changed if required with a simple update command on the grant tables
   
   
   /home/httpd/perl/perlsql/lonsql
   /usr/local/mysql/fakeclient
   </PRE>
   </P>
   <H3>To do</H3>
   <P>
   <PRE>
   This is the output from scripts/mysql_install_db...
   still some todo things (like support-files/mysql.server)
   
   Creating db table
   Creating host table
   Creating user table
   Creating func table
   Creating tables_priv table
   Creating columns_priv table
   
   To start mysqld at boot time you have to copy support-files/mysql.server
   to the right place for your system
   
   PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
   This is done with:
   /usr/local/bin/mysqladmin -u root password 'new-password'
   See the manual for more instructions.
   
   Please report any problems with the /usr/local/bin/mysqlbug script!
   
   The latest information about MySQL is available on the web at http://www.mysql.com
   Support MySQL by buying support/licenses at http://www.tcx.se/license.htmy.
   </PRE>
   </P>
   <H3>Source Installation and Manual Configuration</H3>
   <P>
   <PRE>
   August, 29 2000; Scott Harrison; LON-CAPA
   
   These are notes related to a Perl interface and MySQL server installation
   on Redhat 6.1 and 6.2 boxes.  (Guy Albertelli and Harsha Jagasia
   contributed significantly to this.)
   
   ********************
   * MySQL COMPONENTS *
   ********************
   
   There are three components to an effective MySQL installation for the
   LON-CAPA system.
   
   Perl::DBI module- the API "front-end"...
                     database interface module for organizing generic
                     database commands which are independent of specific
                     database implementation (such as MySQL, mSQL, Postgres, etc).
   
   Perl::MySQL module- the API "mid-section"...
                       the module to directly interface with the actual
                       MySQL database engine
   
   MySQL database engine- the "back-end"...
                          the binary installation (compiled either from source
                          or pre-compiled file listings) which provides the
                          actual MySQL functionality on the system
   
   RedHat Installation-
   
   Initially done from source:
   DBI-1.13.tar.gz  Msql-Mysql-modules-1.2209.tar.gz  mysql-3.22.32.tar.gz
   
   I am now using pre-compiled file listings.
   
   There were problems with using the RedHat packages since the three
   different RedHat packages were somewhat noncompatible with each other
   in terms of expected file locations. (The Debian linux distribution,
   on the other hand, has a working set of these packages).
   
   Regardless of how we install these three components, there still remain
   certain things which need to happen for the configuration.
   
   *****************
   * CONFIGURATION *
   *****************
   
   (Note: SOMEPASSWORD is actually set to another text string on the current
   LON-CAPA systems.)
   
   Configuration is needed to generate the necessary functionality for the
   MySQL system with LON-CAPA.
   
   The functionality needed can be understood from this example line
   of perl code from "lonsql".
   
      $dbh = DBI->connect( "DBI:mysql:loncapa",
    "www",
    "SOMEPASSWORD",
    { RaiseError =>0,PrintError=>0});
   
   There is an obvious need to CONNECT to the database, and in order to do
   this, there must be:
     a RUNNING mysql daemon;
     a DATABASE named "loncapa";
     a USER named "www";
     and an ABILITY for LON-CAPA on one machine to access
          SQL database on another machine;
     
   So, here are some notes on implementing these configurations.
   
   ** RUNNING mysql daemon (safe_mysqld method)
   
   The recommended way to run the MySQL daemon is as a non-root user
   (probably www)...
   
   so, 1) login as user www on the linux machine
       2) start the mysql daemon as /usr/local/bin/safe_mysqld &
   
   safe_mysqld only works if the local installation of MySQL is set to the
   right directory permissions which I found to be:
   chown www:users /usr/local/var/mysql
   chown www:users /usr/local/lib/mysql
   chown -R www:users /usr/local/mysql
   chown www:users /usr/local/include/mysql
   chown www:users /usr/local/var
   
   ** DATABASE named "loncapa"
   
   As user www, run this command
       mysql -u root -p mysql
   enter the password as SOMEPASSWORD
   
   This allows you to manually enter MySQL commands.
   The MySQL command to generate the loncapa DATABASE is:
   
   CREATE DATABASE 'loncapa';
   
   ** USER named "www"
   
   As user www, run this command
       mysql -u root -p mysql
   enter the password as SOMEPASSWORD
   
   To add the user www to the MySQL server, and grant all
   privileges on *.* to www@localhost identified by 'SOMEPASSWORD'
   with grant option;
   
   INSERT INTO user (Host, User, Password)
   VALUES ('localhost','www',password('SOMEPASSWORD'));
   
   GRANT ALL PRIVILEGES ON *.* TO www@localhost;
   
   FLUSH PRIVILEGES;
   
   ** ABILITY for LON-CAPA machines to communicate with SQL databases on
      other LON-CAPA machines
   
   This is a little more intricate than might first be expected (and I probably
   won't do a perfect job reciting everything in this short synopsis).  Because
   LON-CAPA machines will likely be handling many SQL requests at a time,
   there were some problems with current MySQL capabilities.
   
   PROBLEM SITUATION:
   
     If Server A wants data from Server B, Server A uses a lonc process to
     send a database command to a Server B lond process.
       lonc= loncapa client process    A-lonc= a lonc process on Server A
       lond= loncapa daemon process
   
                    database command
       A-lonc  --------TCP/IP----------------> B-lond
   
     The problem emerges that A-lonc and B-lond are kept waiting for the
     MySQL server to "do its stuff", or in other words, perform the conceivably
     sophisticated, data-intensive, time-sucking database transaction.  By tying
     up a lonc and lond process, this significantly cripples the capabilities
     of LON-CAPA servers. 
   
     While commercial databases have a variety of features that ATTEMPT to
     deal with this, freeware databases are still experimenting and exploring
     with different schemes with varying degrees of performance stability.
   
   THE SOLUTION:
   
     A separate daemon process was created that B-lond works with to
     handle database requests.  This daemon process is called "lonsql".
   
     So,
                   database command
     A-lonc  ---------TCP/IP-----------------> B-lond =====> B-lonsql
            <---------------------------------/                |
              "ok, I'll get back to you..."                    |
                                                               |
                                                               /
     A-lond  <-------------------------------  B-lonc   <======
              "Guess what? I have the result!"
   
     Of course, depending on success or failure, the messages may vary,
     but the principle remains the same where a separate pool of children
     processes (lonsql's) handle the MySQL database manipulations.
   
   Here are excerpts of code which implement the above handling:
   
   **LONSQL
   
   A subroutine from "lonsql" which establishes a child process for handling
   database interactions.
   
   sub make_new_child {
       my $pid;
       my $sigset;
       
       # block signal for fork
       $sigset = POSIX::SigSet->new(SIGINT);
       sigprocmask(SIG_BLOCK, $sigset)
           or die "Can't block SIGINT for fork: $!\n";
       
       die "fork: $!" unless defined ($pid = fork);
       
       if ($pid) {
           # Parent records the child's birth and returns.
           sigprocmask(SIG_UNBLOCK, $sigset)
               or die "Can't unblock SIGINT for fork: $!\n";
           $children{$pid} = 1;
           $children++;
           return;
       } else {
           # Child can *not* return from this subroutine.
           $SIG{INT} = 'DEFAULT';      # make SIGINT kill us as it did before
       
           # unblock signals
           sigprocmask(SIG_UNBLOCK, $sigset)
               or die "Can't unblock SIGINT for fork: $!\n";
   
   
           #open database handle
    # making dbh global to avoid garbage collector
    unless (
    $dbh = DBI->connect("DBI:mysql:loncapa","www","SOMEPASSWORD",{ RaiseError =>0,PrintError=>0})
    ) { 
               my $st=120+int(rand(240));
       &logthis("<font color=blue>WARNING: Couldn't connect to database  ($st secs): $@</font>");
       print "database handle error\n";
       sleep($st);
       exit;
   
     };
    # make sure that a database disconnection occurs with ending kill signals
    $SIG{TERM}=$SIG{INT}=$SIG{QUIT}=$SIG{__DIE__}=\&DISCONNECT;
   
           # handle connections until we've reached $MAX_CLIENTS_PER_CHILD
           for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {
               $client = $server->accept()     or last;
               
               # do something with the connection
       $run = $run+1;
       my $userinput = <$client>;
       chomp($userinput);
           
       my ($conserver,$querytmp)=split(/&/,$userinput);
       my $query=unescape($querytmp);
   
               #send query id which is pid_unixdatetime_runningcounter
       $queryid = $thisserver;
       $queryid .="_".($$)."_";
       $queryid .= time."_";
       $queryid .= $run;
       print $client "$queryid\n";
       
               #prepare and execute the query
       my $sth = $dbh->prepare($query);
       my $result;
       unless ($sth->execute())
       {
    &logthis("<font color=blue>WARNING: Could not retrieve from database: $@</font>");
    $result="";
       }
       else {
    my $r1=$sth->fetchall_arrayref;
    my @r2; map {my $a=$_; my @b=map {escape($_)} @$a; push @r2,join(",", @b)} (@$r1);
    $result=join("&",@r2) . "\n";
       }
               &reply("queryreply:$queryid:$result",$conserver);
   
           }
       
           # tidy up gracefully and finish
   
           #close the database handle
    $dbh->disconnect
      or &logthis("<font color=blue>WARNING: Couldn't disconnect from database  $DBI::errstr ($st secs): $@</font>");
       
           # this exit is VERY important, otherwise the child will become
           # a producer of more and more children, forking yourself into
           # process death.
           exit;
       }
   }
   
   ** LOND enabling of MySQL requestsw
   
     This code is part of every lond child process in the way that it parses command request syntax
     sent to it from lonc processes.  querysend corresponds to B-lonc sending the result of the query.
     queryreply corresponds to B-lond indicating that it has received the request and will start the
     database transaction (it returns "ok" to A-lonc ($client)).
   
   # ------------------------------------------------------------------- querysend
                      } elsif ($userinput =~ /^querysend/) {
                          my ($cmd,$query)=split(/:/,$userinput);
          $query=~s/\n*$//g;
                        print $client sqlreply("$hostid{$clientip}\&$query")."\n";
   # ------------------------------------------------------------------ queryreply
                      } elsif ($userinput =~ /^queryreply/) {
                          my ($cmd,$id,$reply)=split(/:/,$userinput); 
          my $store;
                          my $execdir=$perlvar{'lonDaemons'};
                          if ($store=IO::File->new(">$execdir/tmp/$id")) {
      print $store $reply;
      close $store;
      print $client "ok\n";
          }
          else {
      print $client "error:$!\n";
          }
   
   
   
   ** TEST the database connection with my current tester.pl code which mimics what command will eventually be
      sent through lonc.
   
   $reply=reply(
       "querysend:SELECT * FROM general_information WHERE Id='AAAAA'",$lonID);
   </PRE>
 </P>  </P>
 </BODY>  </BODY>
 </HTML>  
   
   </HTML>

Removed from v.1.1  
changed lines
  Added in v.1.4


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