MDL-36789 Quick course cache integrity check for deleted modules
authorMarina Glancy <marina@moodle.com>
Fri, 9 Aug 2013 03:09:26 +0000 (13:09 +1000)
committerMarina Glancy <marina@moodle.com>
Thu, 15 Aug 2013 11:48:24 +0000 (21:48 +1000)
lib/modinfolib.php

index 990fc6a..db40d87 100644 (file)
@@ -242,7 +242,7 @@ class course_modinfo extends stdClass {
      * @param int $userid User ID
      */
     public function __construct($course, $userid) {
-        global $CFG, $DB;
+        global $CFG, $DB, $COURSE, $SITE;
 
         // Check modinfo field is set. If not, build and load it.
         if (empty($course->modinfo) || empty($course->sectioncache)) {
@@ -288,8 +288,28 @@ class course_modinfo extends stdClass {
         }
 
         // If we haven't already preloaded contexts for the course, do it now
+        // Modules are also cached here as long as it's the first time this course has been preloaded.
         context_helper::preload_course($course->id);
 
+        // Quick integrity check: as a result of race conditions modinfo may not be regenerated after the change.
+        // It is especially dangerous if modinfo contains the deleted course module, as it results in fatal error.
+        // We can check it very cheap by validating the existence of module context.
+        if ($course->id == $COURSE->id || $course->id == $SITE->id) {
+            // Only verify current course (or frontpage) as pages with many courses may not have module contexts cached.
+            // (Uncached modules will result in a very slow verification).
+            foreach ($info as $mod) {
+                if (!context_module::instance($mod->cm, IGNORE_MISSING)) {
+                    debugging('Course cache integrity check failed: course module with id '. $mod->cm.
+                            ' does not have context. Rebuilding cache for course '. $course->id);
+                    rebuild_course_cache($course->id);
+                    $this->course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
+                    $info = unserialize($this->course->modinfo);
+                    $sectioncache = unserialize($this->course->sectioncache);
+                    break;
+                }
+            }
+        }
+
         // Loop through each piece of module data, constructing it
         $modexists = array();
         foreach ($info as $mod) {