Annotation of doc/mysql_note.txt, revision 1.1
1.1 ! harris41 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>