Annotation of doc/build/loncapasqldatabase.html, revision 1.2

1.1       harris41    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>
1.2     ! harris41   13: <P>
1.1       harris41   14: This file describes issues associated with LON-CAPA
                     15: and a SQL database.
                     16: </P>
1.2     ! harris41   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>
1.1       harris41   79: <P>
1.2     ! harris41   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.
1.1       harris41  370: 
1.2     ! harris41  371: $reply=reply(
        !           372:     "querysend:SELECT * FROM general_information WHERE Id='AAAAA'",$lonID);
        !           373: </PRE>
1.1       harris41  374: </P>
                    375: </BODY>
                    376: </HTML>

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