[LON-CAPA-cvs] cvs: rat / lonuserstate.pm

raeburn raeburn@source.lon-capa.org
Sun, 15 Nov 2009 14:08:53 -0000


This is a MIME encoded message

--raeburn1258294133
Content-Type: text/plain

raeburn		Sun Nov 15 14:08:53 2009 EDT

  Modified files:              
    /rat	lonuserstate.pm 
  Log:
  Users occasionally prevented from initializing couse by missing .state file and corrupted $user_$course.db in /home/httpd/perl/tmp.
     
  - Move code used to populate tied hashes for $user_$course.db and $user_$course_parms.db in /home/httpd/perl/tmp to subroutine - &build_tmp_hashes().
  - Move code used to unlink old $user_$course.db and $user_$course_parms.db and $user_$course.state in /home/httpd/perl/tmp to subroutine - &unlink_tmpfiles().
  - More detailed logging to lonnet.log for failed operations.
  - Additional check for existence of $user_$course.state after hash population and/or busy wait to get lock and read of existing files.
  
  
--raeburn1258294133
Content-Type: text/plain
Content-Disposition: attachment; filename="raeburn-20091115140853.txt"

Index: rat/lonuserstate.pm
diff -u rat/lonuserstate.pm:1.131 rat/lonuserstate.pm:1.132
--- rat/lonuserstate.pm:1.131	Wed Feb 18 07:06:19 2009
+++ rat/lonuserstate.pm	Sun Nov 15 14:08:53 2009
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Construct and maintain state and binary representation of course for user
 #
-# $Id: lonuserstate.pm,v 1.131 2009/02/18 07:06:19 raeburn Exp $
+# $Id: lonuserstate.pm,v 1.132 2009/11/15 14:08:53 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -653,84 +653,41 @@
 
     open(LOCKFILE,">$fn.db.lock");
     my $lock=0;
+    my $gotstate=0;
     if (flock(LOCKFILE,LOCK_EX|LOCK_NB)) {
 	$lock=1;
-	unlink($fn.'.db');
-	unlink($fn.'_symb.db');
-	unlink($fn.'.state');
-	unlink($fn.'parms.db');
+        &unlink_tmpfiles($fn);
     }
     undef %randompick;
     undef %hiddenurl;
     undef %encurl;
     $retfrid='';
-    if ($lock && (tie(%hash,'GDBM_File',"$fn.db",&GDBM_WRCREAT(),0640)) &&
-	(tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_WRCREAT(),0640))) {
-	%hash=();
-	%parmhash=();
-	$errtext='';
-	$pc=0;
-	&clear_mapalias_count();
-	&processversionfile(%cenv);
-	my $furi=&Apache::lonnet::clutter($uri);
-	$hash{'src_0.0'}=&versiontrack($furi);
-	$hash{'title_0.0'}=&Apache::lonnet::metadata($uri,'title');
-	$hash{'ids_'.$furi}='0.0';
-	$hash{'is_map_0.0'}=1;
-	loadmap($uri,'0.0');
-	if (defined($hash{'map_start_'.$uri})) {
-	    &Apache::lonnet::appenv({"request.course.id"  => $short,
-				     "request.course.fn"  => $fn,
-				     "request.course.uri" => $uri});
-	    $env{'request.course.id'}=$short;
-	    &traceroute('0',$hash{'map_start_'.$uri},'&');
-	    &accinit($uri,$short,$fn);
-	    &hiddenurls();
-	}
-	$errtext .= &get_mapalias_errors();
-# ------------------------------------------------------- Put versions into src
-	foreach my $key (keys(%hash)) {
-	    if ($key=~/^src_/) {
-		$hash{$key}=&putinversion($hash{$key});
-	    } elsif ($key =~ /^(map_(?:start|finish|pc)_)(.*)/) {
-		my ($type, $url) = ($1,$2);
-		my $value = $hash{$key};
-		$hash{$type.&putinversion($url)}=$value;
-	    }
-	}
-# ---------------------------------------------------------------- Encrypt URLs
-	foreach my $id (keys(%encurl)) {
-#	    $hash{'src_'.$id}=&Apache::lonenc::encrypted($hash{'src_'.$id});
-	    $hash{'encrypted_'.$id}=1;
-	}
-# ----------------------------------------------- Close hashes to finally store
-# --------------------------------- Routine must pass this point, no early outs
-	$hash{'first_rid'}=$retfrid;
-	my ($mapid,$resid)=split(/\./,$retfrid);
-	$hash{'first_mapurl'}=$hash{'map_id_'.$mapid};
-	my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$hash{'src_'.$retfrid});
-	$retfurl=&add_get_param($hash{'src_'.$retfrid},{ 'symb' => $symb });
-	if ($hash{'encrypted_'.$retfrid}) {
-	    $retfurl=&Apache::lonenc::encrypted($retfurl,(&Apache::lonnet::allowed('adv') ne 'F'));
-	}
-	$hash{'first_url'}=$retfurl;
-	unless ((untie(%hash)) && (untie(%parmhash))) {
-	    &Apache::lonnet::logthis("<font color=blue>WARNING: ".
-				     "Could not untie coursemap $fn for $uri.</font>"); 
-	}
-# ---------------------------------------------------- Store away initial state
-	{
-	    my $cfh;
-	    if (open($cfh,">$fn.state")) {
-		print $cfh join("\n",@cond);
-	    } else {
-		&Apache::lonnet::logthis("<font color=blue>WARNING: ".
-					 "Could not write statemap $fn for $uri.</font>"); 
-	    }
-	}
+    my ($untiedhash,$untiedparmhash,$tiedhash,$tiedparmhash);
+    if ($lock) {
+        if (tie(%hash,'GDBM_File',"$fn.db",&GDBM_WRCREAT(),0640)) {
+            $tiedhash = 1;
+            if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_WRCREAT(),0640)) {
+                $tiedparmhash = 1;
+                $gotstate = &build_tmp_hashes($uri,$fn,$short,\%cenv);
+                unless ($gotstate) {
+                    &Apache::lonnet::logthis('Failed to write statemap at first attempt '.$fn.' for '.$uri.'.</font>');
+                }
+                $untiedparmhash = untie(%parmhash);
+                unless ($untiedparmhash) {
+                    &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                        'Could not untie coursemap parmhash '.$fn.' for '.$uri.'.</font>');
+                }
+            }
+            $untiedhash = untie(%hash);
+            unless ($untiedhash) {
+                &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                    'Could not untie coursemap hash '.$fn.' for '.$uri.'.</font>');
+            }
+        }
 	flock(LOCKFILE,LOCK_UN);
 	close(LOCKFILE);
-    } else {
+    }
+    unless ($lock && $tiedhash && $tiedparmhash) { 
 	# if we are here it is likely because we are already trying to 
 	# initialize the course in another child, busy wait trying to 
 	# tie the hashes for the next 90 seconds, if we succeed forward 
@@ -739,34 +696,115 @@
 	if ($lock) {
 	    # Got the lock but not the DB files
 	    flock(LOCKFILE,LOCK_UN);
+            $lock = 0;
 	}
-	untie(%hash);
-	untie(%parmhash);
+        if ($tiedhash) {
+            unless($untiedhash) {
+	        untie(%hash);
+            }
+        }
+        if ($tiedparmhash) {
+            unless($untiedparmhash) {
+                untie(%parmhash);
+            }
+        }
 	&Apache::lonnet::logthis("<font color=blue>WARNING: ".
-				 "Could not tie coursemap $fn for $uri.</font>"); 
+				 "Could not tie coursemap $fn for $uri.</font>");
+        $tiedhash = '';
+        $tiedparmhash = '';
 	my $i=0;
 	while($i<90) {
 	    $i++;
 	    sleep(1);
-	    if (flock(LOCKFILE,LOCK_EX|LOCK_NB) &&
-		(tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER(),0640))) {
-		if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_READER(),0640)) {
-		    $retfurl='/adm/navmaps';
-		    &Apache::lonnet::appenv({"request.course.id"  => $short,
-					     "request.course.fn"  => $fn,
-					     "request.course.uri" => $uri});
-		    untie(%hash);
-		    untie(%parmhash);
-		    last;
-		}
-	    }
-	    untie(%hash);
-	    untie(%parmhash);
+	    if (flock(LOCKFILE,LOCK_EX|LOCK_NB)) {
+                $lock = 1;
+		if (tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER(),0640)) {
+                    $tiedhash = 1;
+		    if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_READER(),0640)) {
+                        $tiedparmhash = 1;
+                        if (-e "$fn.state") {
+		            $retfurl='/adm/navmaps';
+		            &Apache::lonnet::appenv({"request.course.id"  => $short,
+		   			             "request.course.fn"  => $fn,
+					             "request.course.uri" => $uri});
+		            $untiedhash = untie(%hash);
+		            $untiedparmhash = untie(%parmhash);
+                            $gotstate = 1;
+		            last;
+		        }
+                        $untiedparmhash = untie(%parmhash);
+	            }
+	            $untiedhash = untie(%hash);
+                }
+            }
 	}
-	flock(LOCKFILE,LOCK_UN);
-	close(LOCKFILE);
+        if ($lock) {
+            flock(LOCKFILE,LOCK_UN);
+            if ($tiedparmhash) {
+                unless ($untiedparmhash) {
+                    &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                        'Could not untie coursemap parmhash '.$fn.' for '.$uri.'.</font>');
+                }
+            }
+            if ($tiedparmhash) {
+                unless ($untiedhash) {
+                    &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                        'Could not untie coursemap hash '.$fn.' for '.$uri.'.</font>');
+                }
+            }
+        }
+    }
+    unless ($gotstate) {
+        &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                     'Could not read statemap '.$fn.' for '.$uri.'.</font>');
+        &unlink_tmpfiles($fn);
+        if (open(LOCKFILE,">$fn.db.lock")) {
+            my $lock=0;
+            if (flock(LOCKFILE,LOCK_EX|LOCK_NB)) {
+                $lock=1;
+                &unlink_tmpfiles($fn);
+            }
+            undef %randompick;
+            undef %hiddenurl;
+            undef %encurl;
+            $retfrid='';
+            if ($lock) {
+                if (tie(%hash,'GDBM_File',"$fn.db",&GDBM_WRCREAT(),0640)) {
+                    if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_WRCREAT(),0640)) {
+                        $gotstate = &build_tmp_hashes($uri,$fn,$short,\%cenv);
+                        unless ($gotstate) {
+                            &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                                'Failed to write statemap at second attempt '.$fn.' for '.$uri.'.</font>');
+                        }
+                        unless (untie(%parmhash)) {
+                            &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                                'Could not untie coursemap parmhash '.$fn.'.db for '.$uri.'.</font>');
+                        }
+                    } else {
+                        &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                            'Could not tie coursemap '.$fn.'__parms.db for '.$uri.'.</font>');
+                    }
+                    unless (untie(%hash)) {
+                        &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                            'Could not untie coursemap hash '.$fn.'.db for '.$uri.'.</font>');
+                    }
+               } else {
+                   &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                       'Could not tie coursemap '.$fn.'.db for '.$uri.'.</font>');
+               }
+               flock(LOCKFILE,LOCK_UN);
+               close(LOCKFILE);
+            } else {
+                &Apache::lonnet::logthis('<font color="blue">WARNING: '.
+                'Could not obtain lock to tie coursemap hash '.$fn.'.db for '.$uri.'.</font>');
+            }
+	    close(LOCKFILE);
+        }
+    }
+    unless (($errtext eq '') || ($env{'request.course.uri'} =~ m{^/uploaded/})) {
+        &Apache::lonmsg::author_res_msg($env{'request.course.uri'},
+                                        $errtext);
     }
-    &Apache::lonmsg::author_res_msg($env{'request.course.uri'},$errtext);
 # ------------------------------------------------- Check for critical messages
 
     my @what=&Apache::lonnet::dump('critical',$env{'user.domain'},
@@ -779,6 +817,91 @@
     return ($retfurl,$errtext);
 }
 
+sub build_tmp_hashes {
+    my ($uri,$fn,$short,$cenvref) = @_;
+    unless(ref($cenvref) eq 'HASH') {
+        return;
+    }
+    my %cenv = %{$cenvref};
+    my $gotstate = 0;
+    %hash=();
+    %parmhash=();
+    $errtext='';
+    $pc=0;
+    &clear_mapalias_count();
+    &processversionfile(%cenv);
+    my $furi=&Apache::lonnet::clutter($uri);
+    $hash{'src_0.0'}=&versiontrack($furi);
+    $hash{'title_0.0'}=&Apache::lonnet::metadata($uri,'title');
+    $hash{'ids_'.$furi}='0.0';
+    $hash{'is_map_0.0'}=1;
+    &loadmap($uri,'0.0');
+    if (defined($hash{'map_start_'.$uri})) {
+        &Apache::lonnet::appenv({"request.course.id"  => $short,
+                                 "request.course.fn"  => $fn,
+                                 "request.course.uri" => $uri});
+        $env{'request.course.id'}=$short;
+        &traceroute('0',$hash{'map_start_'.$uri},'&');
+        &accinit($uri,$short,$fn);
+        &hiddenurls();
+    }
+    $errtext .= &get_mapalias_errors();
+# ------------------------------------------------------- Put versions into src
+    foreach my $key (keys(%hash)) {
+        if ($key=~/^src_/) {
+            $hash{$key}=&putinversion($hash{$key});
+        } elsif ($key =~ /^(map_(?:start|finish|pc)_)(.*)/) {
+            my ($type, $url) = ($1,$2);
+            my $value = $hash{$key};
+            $hash{$type.&putinversion($url)}=$value;
+        }
+    }
+# ---------------------------------------------------------------- Encrypt URLs
+    foreach my $id (keys(%encurl)) {
+#           $hash{'src_'.$id}=&Apache::lonenc::encrypted($hash{'src_'.$id});
+        $hash{'encrypted_'.$id}=1;
+    }
+# ----------------------------------------------- Close hashes to finally store
+# --------------------------------- Routine must pass this point, no early outs
+    $hash{'first_rid'}=$retfrid;
+    my ($mapid,$resid)=split(/\./,$retfrid);
+    $hash{'first_mapurl'}=$hash{'map_id_'.$mapid};
+    my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$hash{'src_'.$retfrid});
+    $retfurl=&add_get_param($hash{'src_'.$retfrid},{ 'symb' => $symb });
+    if ($hash{'encrypted_'.$retfrid}) {
+        $retfurl=&Apache::lonenc::encrypted($retfurl,(&Apache::lonnet::allowed('adv') ne 'F'));
+    }
+    $hash{'first_url'}=$retfurl;
+# ---------------------------------------------------- Store away initial state
+    {
+        my $cfh;
+        if (open($cfh,">$fn.state")) {
+            print $cfh join("\n",@cond);
+            $gotstate = 1;
+        } else {
+            &Apache::lonnet::logthis("<font color=blue>WARNING: ".
+                                     "Could not write statemap $fn for $uri.</font>");
+        }
+    }
+    return $gotstate;
+}
+
+sub unlink_tmpfiles {
+    my ($fn) = @_;
+    if ($fn =~ m{^\Q$Apache::lonnet::perlvar{'lonUsersDir'}\E/tmp/}) {
+        my @files = qw (.db _symb.db .state _parms.db);
+        foreach my $file (@files) {
+            if (-e $fn.$file) {
+                unless (unlink($fn.$file)) {
+                    &Apache::lonnet::logthis("<font color=blue>WARNING: ".
+                                 "Could not unlink ".$fn.$file."</font>");
+                }
+            }
+        }
+    }
+    return;
+}
+
 # ------------------------------------------------------- Evaluate state string
 
 sub evalstate {

--raeburn1258294133--