Annotation of doc/build/loncapasqldatabase.html, revision 1.8
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>
1.5 harris41 11: Last updated: 02/12/2001
1.1 harris41 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.5 harris41 17: <H2>Latest HOWTO</H2>
1.2 harris41 18: <P>
1.4 harris41 19: <UL>
20: <LI>Current status of documentation</LI>
21: <LI>Current status of implementation</LI>
22: <LI>Purpose within LON-CAPA</LI>
1.8 ! harris41 23: <LI>Dependencies</LI>
1.4 harris41 24: <LI>Installation</LI>
25: <LI>Installation from source</LI>
26: <LI>Configuration (automated)</LI>
27: <LI>Manual configuration</LI>
28: <LI>Testing</LI>
29: <LI>Example sections of code relevant to LON-CAPA</LI>
30: </UL>
31: </P>
1.5 harris41 32: <H2>Current status of documentation</H2>
1.4 harris41 33: <P>
1.2 harris41 34: I am going to begin documentation by inserting what notes
35: I have into this file. I will be subsequently rearranging
1.4 harris41 36: them and editing them based on the tests that I conduct.
1.2 harris41 37: I am trying to make sure that documentation, installation,
38: and run-time issues are all consistent and correct. The
39: current status of everything is that it works and has
40: been minimally tested, but things need to be cleaned up
41: and checked again!
42: </P>
1.5 harris41 43: <H2>Current status of implementation</H2>
1.4 harris41 44: <P>
1.6 harris41 45: Need to
46: <UL>
47: <LI>Installation: Fix binary file listings for user permissions and ownership.
48: <LI>Installation: Make sure sql server starts, and if database does not
49: exist, then create. (/etc/rc.d).
50: <LI>Processes: Make sure loncron initiates lonsql on library machines.
51: <LI>Read in metadata from right place periodically.
52: <LI>Implement tested perl module handler.
53: </UL>
54: <P>
1.4 harris41 55: Right now, a lot of "feasibility" work has been done.
56: Recipes for manual installation and configuration have
57: been gathered. Network connectivity of lond->lonsql->lond->lonc
58: type tests have been performed. A binary installation
1.7 harris41 59: has been compiled in an RPM (LON-CAPA-mysql, with perl components
60: a part of LON-CAPA-systemperl).
1.4 harris41 61: The most lacking test in terms of feasibility has
62: been looking at benchmarks to analyze the load at which
63: the SQL database can efficiently allow many users to
64: make simultaneous requests of the metadata database.
65: </P>
66: <P>
67: Documentation has been pieced together over time. But,
68: as mentioned in the previous section, it needs an
69: overhaul.
70: </P>
71: <P>
72: The binary installation has some quirks associated with it.
73: Some of the user permissions are wrong, although this is
74: benign. Also, other options of binary installation (such
75: as using binary RPMs put together by others) were dismissed
76: given the difficulty of getting differing combinations of
77: these external RPMs to work together.
78: </P>
79: <P>
80: Most configuration questions have been initially worked out
81: to the point of getting this SQL software component working,
82: however there may be more optimal approaches than currently
83: exist.
84: </P>
1.5 harris41 85: <H2>Purpose within LON-CAPA</H2>
1.4 harris41 86: <P>
87: LON-CAPA is meant to distribute A LOT of educational content
88: to A LOT of people. It is ineffective to directly rely on contents
89: within the ext2 filesystem to be speedily scanned for
90: on-the-fly searches of content descriptions. (Simply put,
91: it takes a cumbersome amount of time to open, read, analyze, and
92: close thousands of files.)
93: </P>
94: <P>
95: The solution is to hash-index various data fields that are
96: descriptive of the educational resources on a LON-CAPA server
97: machine. Descriptive data fields are referred to as
98: "metadata". The question then arises as to how this metadata
99: is handled in terms of the rest of the LON-CAPA network
100: without burdening client and daemon processes. I now
101: answer this question in the format of Problem and Solution
102: below.
103: </P>
104: <P>
105: <PRE>
106: PROBLEM SITUATION:
107:
108: If Server A wants data from Server B, Server A uses a lonc process to
109: send a database command to a Server B lond process.
110: lonc= loncapa client process A-lonc= a lonc process on Server A
111: lond= loncapa daemon process
112:
113: database command
114: A-lonc --------TCP/IP----------------> B-lond
115:
116: The problem emerges that A-lonc and B-lond are kept waiting for the
117: MySQL server to "do its stuff", or in other words, perform the conceivably
118: sophisticated, data-intensive, time-sucking database transaction. By tying
119: up a lonc and lond process, this significantly cripples the capabilities
120: of LON-CAPA servers.
121:
122: While commercial databases have a variety of features that ATTEMPT to
123: deal with this, freeware databases are still experimenting and exploring
124: with different schemes with varying degrees of performance stability.
125:
126: THE SOLUTION:
127:
128: A separate daemon process was created that B-lond works with to
129: handle database requests. This daemon process is called "lonsql".
130:
131: So,
132: database command
133: A-lonc ---------TCP/IP-----------------> B-lond =====> B-lonsql
134: <---------------------------------/ |
135: "ok, I'll get back to you..." |
136: |
137: /
138: A-lond <------------------------------- B-lonc <======
139: "Guess what? I have the result!"
140:
141: Of course, depending on success or failure, the messages may vary,
142: but the principle remains the same where a separate pool of children
143: processes (lonsql's) handle the MySQL database manipulations.
144: </PRE>
145: </P>
1.8 ! harris41 146: <H2>Dependencies</H2>
! 147: <P>
! 148: I believe (but am not 100% confident) that the following
! 149: RPMs are necessary (in addition to the current ones
! 150: in rpm_list.txt) to run MySQL. Basically I discovered these
! 151: dependencies while trying to do external RPM based installs.
! 152: I assume, and sometimes found, that these dependencies apply
! 153: to tarball-based distributions too. (So to play it on the
! 154: safe side, I am going to include these RPMs as part of the
! 155: core, minimal RPM set.)
! 156: <UL>
! 157: <LI>egcs-1.1.2-30</LI>
! 158: <LI>cpp-1.1.2-30</LI>
! 159: <LI>glibc-devel-2.1.3-15</LI>
! 160: <LI>glibc-devel-2.1.3-15</LI>
! 161: <LI>zlib-devel-1.1.3-6</LI>
! 162: </UL>
! 163:
! 164: </P>
1.5 harris41 165: <H2>Installation</H2>
1.4 harris41 166: <P>
167: Installation of the LON-CAPA SQL database normally occurs
168: by default when using the LON-CAPA installation CD
169: (see http://install.lon-capa.org). It is installed
170: as the LON-CAPA-mysql RPM. This RPM encodes for the MySQL
1.7 harris41 171: engine. Related perl interfaces (Perl::DBI, Perl::Msql-Mysql)
172: are encoded in the LON-CAPA-systemperl RPM.
1.4 harris41 173: </P>
174: <P>
175: The three components of a MySQL installation for the
176: LON-CAPA system are further described immediately below.
177: <TABLE BORDER="0">
178: <TR><TD COLSPAN="2"><STRONG>Perl::DBI module</STRONG>-
179: the API "front-end"...</TD></TR>
180: <TR><TD WIDTH="10%"></TD><TD>database interface module for organizing generic
181: database commands which are independent of specific
182: database implementation (such as MySQL, mSQL, Postgres, etc).
183: </TD></TR>
184: <TR><TD COLSPAN="2"><STRONG>Perl::MySQL module</STRONG>-
185: the API "mid-section"...</TD></TR>
186: <TR><TD WIDTH="10%"></TD><TD>the module to directly interface with the actual
187: MySQL database engine</TD></TR>
188: <TR><TD COLSPAN="2"><STRONG>MySQL database engine</STRONG>-
189: the "back-end"...</TD></TR>
190: <TR><TD WIDTH="10%"></TD><TD>the binary installation (compiled either
191: from source or pre-compiled file listings) which provides the
192: actual MySQL functionality on the system</TD></TR>
193: </TABLE>
194: </P>
1.5 harris41 195: <H2>Installation from source</H2>
1.4 harris41 196: <P>
1.8 ! harris41 197: Note: the mysql site recommends that Linux users install by
! 198: using the MySQL RPMs (MySQL-client, MySQL, MySQL-shared, etc).
! 199: While these RPMs work, I was unsuccessful at integrating
! 200: this RPM-installed database with perl modules from www.cpan.org.
! 201: Hence, I <STRONG>strongly</STRONG> recommend that, when installing
! 202: from "source", MySQL and the perl components be in fact installed
! 203: from their tarballs (.tar.gz, .tgz). (Perl components, when installed
! 204: from RPMs, also wound up in incorrect locations on the disk.)
! 205: Do not coordinate a source install with externally made RPMs!
! 206: It is, of course, okay to use LON-CAPA RPMs such as LON-CAPA-systemperl
! 207: and LON-CAPA-mysql since we, in fact, made these RPMs correctly :).
! 208: <UL>
! 209: <LI>http://www.cpan.org/authors/id/JWIED/Msql-Mysql-modules-1.2215.tar.gz
! 210: <BR>This tarball Released 20th August 2000
! 211: <LI>http://www.mysql.com/Downloads/MySQL-3.23/mysql-3.23.33-pc-linux-gnu-i686.tar.gz
! 212: <BR>This tarball Last changed 2000-11-11
! 213: <BR>This is actually a binary tarball (as opposed to source code
! 214: that is subsequently compiled).
! 215: <LI>
! 216: <BR>
! 217: </UL>
! 218: </P>
! 219: <FONT COLOR="green"> old notes in green
! 220: <P>
1.4 harris41 221: The following set of tarballs was found to work together
222: properly on a LON-CAPA RedHat 6.2 system:
223: <UL>
224: <LI>DBI-1.13.tar.gz
225: <LI>Msql-Mysql-modules-1.2209.tar.gz
226: <LI>mysql-3.22.32.tar.gz
227: </UL>
228: </P>
229: <P>
230: Installation was simply a matter of following the instructions
231: and typing the several "make" commands for each
232: </P>
1.8 ! harris41 233: </FONT>
1.5 harris41 234: <H2>Configuration (automated)</H2>
1.4 harris41 235: <P>
236: Not yet developed. This will be part of an interface
237: present on LON-CAPA systems that can be launched by
238: entering the command <TT>/usr/sbin/loncapaconfig</TT>.
239: </P>
1.5 harris41 240: <H2>Manual configuration</H2>
1.4 harris41 241: <P>
242: This is not complete.
243: </P>
244: <P>
245: <STRONG>Starting the mysql daemon</STRONG>: Login on the Linux
246: system as user 'www'. Enter the command
247: <TT>/usr/local/bin/safe_mysqld &</TT>
248: </P>
249: <P>
250: <STRONG>Set a password for 'root'</STRONG>:
251: <TT>/usr/local/bin/mysqladmin -u root password 'new-password'</TT>
252: </P>
253: <P>
254: <STRONG>Adding a user</STRONG>: Start the mysql daemon. Login to the
255: mysql system as root (<TT>mysql -u root -p mysql</TT>)
256: and enter the right password (for instance 'newmysql'). Add the user
257: www
258: <PRE>
259: INSERT INTO user (Host, User, Password)
260: VALUES ('localhost','www',password('newmysql'));
261: </PRE>
262: </P>
263: <P>
264: <STRONG>Granting privileges to user 'www'</STRONG>:
265: <PRE>
266: GRANT ALL PRIVILEGES ON *.* TO www@localhost;
267: FLUSH PRIVILEGES;
268: </PRE>
269: </P>
270: <P>
271: <STRONG>Set the SQL server to start upon system startup</STRONG>:
272: Copy support-files/mysql.server to the right place on the system
273: (/etc/rc.d/...).
274: </P>
1.1 harris41 275: <P>
1.5 harris41 276: <STRONG>The Perl API</STRONG>
1.2 harris41 277: <PRE>
278: $dbh = DBI->connect( "DBI:mysql:loncapa",
279: "www",
280: "SOMEPASSWORD",
281: { RaiseError =>0,PrintError=>0});
282:
283: There is an obvious need to CONNECT to the database, and in order to do
284: this, there must be:
285: a RUNNING mysql daemon;
286: a DATABASE named "loncapa";
287: a USER named "www";
288: and an ABILITY for LON-CAPA on one machine to access
289: SQL database on another machine;
290:
291: So, here are some notes on implementing these configurations.
292:
293: ** RUNNING mysql daemon (safe_mysqld method)
294:
295: The recommended way to run the MySQL daemon is as a non-root user
296: (probably www)...
297:
298: so, 1) login as user www on the linux machine
299: 2) start the mysql daemon as /usr/local/bin/safe_mysqld &
300:
301: safe_mysqld only works if the local installation of MySQL is set to the
302: right directory permissions which I found to be:
303: chown www:users /usr/local/var/mysql
304: chown www:users /usr/local/lib/mysql
305: chown -R www:users /usr/local/mysql
306: chown www:users /usr/local/include/mysql
307: chown www:users /usr/local/var
308:
309: ** DATABASE named "loncapa"
310:
311: As user www, run this command
312: mysql -u root -p mysql
313: enter the password as SOMEPASSWORD
314:
315: This allows you to manually enter MySQL commands.
316: The MySQL command to generate the loncapa DATABASE is:
317:
318: CREATE DATABASE 'loncapa';
319:
320: ** USER named "www"
321:
322: As user www, run this command
323: mysql -u root -p mysql
324: enter the password as SOMEPASSWORD
325:
326: To add the user www to the MySQL server, and grant all
327: privileges on *.* to www@localhost identified by 'SOMEPASSWORD'
328: with grant option;
329:
330: INSERT INTO user (Host, User, Password)
331: VALUES ('localhost','www',password('SOMEPASSWORD'));
332:
333: GRANT ALL PRIVILEGES ON *.* TO www@localhost;
334:
335: FLUSH PRIVILEGES;
336:
337: ** ABILITY for LON-CAPA machines to communicate with SQL databases on
338: other LON-CAPA machines
339:
1.5 harris41 340: An up-to-date lond and lonsql.
341: </PRE>
342: </P>
343: <H2>Testing</H2>
344: <P>
345: <PRE>
346: <STRONG>** TEST the database connection with my current tester.pl code
347: which mimics what command will eventually be sent through lonc.</STRONG>
1.2 harris41 348:
1.5 harris41 349: $reply=reply(
350: "querysend:SELECT * FROM general_information WHERE Id='AAAAA'",$lonID);
351: </PRE>
352: </P>
353: <H2>Example sections of code relevant to LON-CAPA</H2>
354: <P>
1.2 harris41 355: Here are excerpts of code which implement the above handling:
1.5 harris41 356: </P>
357: <P>
358: <PRE>
359: <STRONG>**LONSQL
1.2 harris41 360: A subroutine from "lonsql" which establishes a child process for handling
1.5 harris41 361: database interactions.</STRONG>
1.2 harris41 362:
363: sub make_new_child {
364: my $pid;
365: my $sigset;
366:
367: # block signal for fork
368: $sigset = POSIX::SigSet->new(SIGINT);
369: sigprocmask(SIG_BLOCK, $sigset)
370: or die "Can't block SIGINT for fork: $!\n";
371:
372: die "fork: $!" unless defined ($pid = fork);
373:
374: if ($pid) {
375: # Parent records the child's birth and returns.
376: sigprocmask(SIG_UNBLOCK, $sigset)
377: or die "Can't unblock SIGINT for fork: $!\n";
378: $children{$pid} = 1;
379: $children++;
380: return;
381: } else {
382: # Child can *not* return from this subroutine.
383: $SIG{INT} = 'DEFAULT'; # make SIGINT kill us as it did before
384:
385: # unblock signals
386: sigprocmask(SIG_UNBLOCK, $sigset)
387: or die "Can't unblock SIGINT for fork: $!\n";
388:
389:
390: #open database handle
391: # making dbh global to avoid garbage collector
392: unless (
393: $dbh = DBI->connect("DBI:mysql:loncapa","www","SOMEPASSWORD",{ RaiseError =>0,PrintError=>0})
394: ) {
395: my $st=120+int(rand(240));
396: &logthis("<font color=blue>WARNING: Couldn't connect to database ($st secs): $@</font>");
397: print "database handle error\n";
398: sleep($st);
399: exit;
400:
401: };
402: # make sure that a database disconnection occurs with ending kill signals
403: $SIG{TERM}=$SIG{INT}=$SIG{QUIT}=$SIG{__DIE__}=\&DISCONNECT;
404:
405: # handle connections until we've reached $MAX_CLIENTS_PER_CHILD
406: for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {
407: $client = $server->accept() or last;
408:
409: # do something with the connection
410: $run = $run+1;
411: my $userinput = <$client>;
412: chomp($userinput);
413:
414: my ($conserver,$querytmp)=split(/&/,$userinput);
415: my $query=unescape($querytmp);
416:
417: #send query id which is pid_unixdatetime_runningcounter
418: $queryid = $thisserver;
419: $queryid .="_".($$)."_";
420: $queryid .= time."_";
421: $queryid .= $run;
422: print $client "$queryid\n";
423:
424: #prepare and execute the query
425: my $sth = $dbh->prepare($query);
426: my $result;
427: unless ($sth->execute())
428: {
429: &logthis("<font color=blue>WARNING: Could not retrieve from database: $@</font>");
430: $result="";
431: }
432: else {
433: my $r1=$sth->fetchall_arrayref;
434: my @r2; map {my $a=$_; my @b=map {escape($_)} @$a; push @r2,join(",", @b)} (@$r1);
435: $result=join("&",@r2) . "\n";
436: }
437: &reply("queryreply:$queryid:$result",$conserver);
438:
439: }
440:
441: # tidy up gracefully and finish
442:
443: #close the database handle
444: $dbh->disconnect
445: or &logthis("<font color=blue>WARNING: Couldn't disconnect from database $DBI::errstr ($st secs): $@</font>");
446:
447: # this exit is VERY important, otherwise the child will become
448: # a producer of more and more children, forking yourself into
449: # process death.
450: exit;
451: }
452: }
1.5 harris41 453: </P>
454: <P>
455: <STRONG>** LOND enabling of MySQL requests</STRONG>
456: <BR />
457: This code is part of every lond child process in the
458: way that it parses command request syntax sent to it
459: from lonc processes. Based on the diagram above, querysend
460: corresponds to B-lonc sending the result of the query.
461: queryreply corresponds to B-lond indicating that it has
462: received the request and will start the database transaction
463: (it returns "ok" to
464: A-lonc ($client)).
465: <PRE>
1.2 harris41 466: # ------------------------------------------------------------------- querysend
467: } elsif ($userinput =~ /^querysend/) {
468: my ($cmd,$query)=split(/:/,$userinput);
469: $query=~s/\n*$//g;
470: print $client sqlreply("$hostid{$clientip}\&$query")."\n";
471: # ------------------------------------------------------------------ queryreply
472: } elsif ($userinput =~ /^queryreply/) {
473: my ($cmd,$id,$reply)=split(/:/,$userinput);
474: my $store;
475: my $execdir=$perlvar{'lonDaemons'};
476: if ($store=IO::File->new(">$execdir/tmp/$id")) {
477: print $store $reply;
478: close $store;
479: print $client "ok\n";
480: }
481: else {
482: print $client "error:$!\n";
483: }
484:
1.5 harris41 485: </PRE>
1.2 harris41 486:
1.1 harris41 487: </P>
488: </BODY>
1.3 harris41 489: </HTML>
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>