Annotation of doc/build/loncapasqldatabase.html, revision 1.3
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>
1.3 ! harris41 376: </HTML>
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>