public function test_matching_cacherev() { global $DB, $CFG; $this->resetAfterTest(); $this->setAdminUser(); $cache = cache::make('core', 'coursemodinfo'); // Generate the course and pre-requisite module. $course = $this->getDataGenerator()->create_course(array('format' => 'topics', 'numsections' => 3), array('createsections' => true)); // Make sure the cacherev is set. $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan(0, $cacherev); $prevcacherev = $cacherev; // Reset course cache and make sure cacherev is bumped up but cache is empty. rebuild_course_cache($course->id, true); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); $prevcacherev = $cacherev; // Build course cache. Cacherev should not change but cache is now not empty. Make sure cacherev is the same everywhere. $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertEquals($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Little trick to check that cache is not rebuilt druing the next step - substitute the value in MUC and later check that it is still there. $cache->set($course->id, (object) array_merge((array) $cachedvalue, array('secretfield' => 1))); // Clear static cache and call get_fast_modinfo() again (pretend we are in another request). Cache should not be rebuilt. course_modinfo::clear_instance_cache(); $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertEquals($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertNotEmpty($cachedvalue->secretfield); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Rebuild course cache. Cacherev must be incremented everywhere. rebuild_course_cache($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $modinfo = get_fast_modinfo($course->id); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Update cacherev in DB and make sure the cache will be rebuilt on the next call to get_fast_modinfo(). increment_revision_number('course', 'cacherev', 'id = ?', array($course->id)); // We need to clear static cache for course_modinfo instances too. course_modinfo::clear_instance_cache(); $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Reset cache for all courses and make sure this course cache is reset. rebuild_course_cache(0, true); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); // Rebuild again. $modinfo = get_fast_modinfo($course->id); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Purge all caches and make sure cacherev is increased and data from MUC erased. purge_all_caches(); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); }
public function test_increment_revision_number() { global $DB; $this->resetAfterTest(); // Use one of the fields that are used with increment_revision_number(). $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $DB->set_field('course', 'cacherev', 1, array()); $record1 = $DB->get_record('course', array('id' => $course1->id)); $record2 = $DB->get_record('course', array('id' => $course2->id)); $this->assertEquals(1, $record1->cacherev); $this->assertEquals(1, $record2->cacherev); // Incrementing some lower value. $this->setCurrentTimeStart(); increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $course1->id)); $record1 = $DB->get_record('course', array('id' => $course1->id)); $record2 = $DB->get_record('course', array('id' => $course2->id)); $this->assertTimeCurrent($record1->cacherev); $this->assertEquals(1, $record2->cacherev); // Incrementing in the same second. $rev1 = $DB->get_field('course', 'cacherev', array('id' => $course1->id)); $now = time(); $DB->set_field('course', 'cacherev', $now, array('id' => $course1->id)); increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $course1->id)); $rev2 = $DB->get_field('course', 'cacherev', array('id' => $course1->id)); $this->assertGreaterThan($rev1, $rev2); increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $course1->id)); $rev3 = $DB->get_field('course', 'cacherev', array('id' => $course1->id)); $this->assertGreaterThan($rev2, $rev3); $this->assertGreaterThan($now + 1, $rev3); increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $course1->id)); $rev4 = $DB->get_field('course', 'cacherev', array('id' => $course1->id)); $this->assertGreaterThan($rev3, $rev4); $this->assertGreaterThan($now + 2, $rev4); // Recovering from runaway revision. $DB->set_field('course', 'cacherev', time() + 60 * 60 * 60, array('id' => $course2->id)); $record2 = $DB->get_record('course', array('id' => $course2->id)); $this->assertGreaterThan(time(), $record2->cacherev); $this->setCurrentTimeStart(); increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $course2->id)); $record2b = $DB->get_record('course', array('id' => $course2->id)); $this->assertTimeCurrent($record2b->cacherev); // Update all revisions. $DB->set_field('course', 'cacherev', 1, array()); $this->setCurrentTimeStart(); increment_revision_number('course', 'cacherev', ''); $record1 = $DB->get_record('course', array('id' => $course1->id)); $record2 = $DB->get_record('course', array('id' => $course2->id)); $this->assertTimeCurrent($record1->cacherev); $this->assertEquals($record1->cacherev, $record2->cacherev); }
/** * Rebuilds or resets the cached list of course activities stored in MUC. * * rebuild_course_cache() must NEVER be called from lib/db/upgrade.php. * At the same time course cache may ONLY be cleared using this function in * upgrade scripts of plugins. * * During the bulk operations if it is necessary to reset cache of multiple * courses it is enough to call {@link increment_revision_number()} for the * table 'course' and field 'cacherev' specifying affected courses in select. * * Cached course information is stored in MUC core/coursemodinfo and is * validated with the DB field {course}.cacherev * * @global moodle_database $DB * @param int $courseid id of course to rebuild, empty means all * @param boolean $clearonly only clear the cache, gets rebuild automatically on the fly. * Recommended to set to true to avoid unnecessary multiple rebuilding. */ function rebuild_course_cache($courseid=0, $clearonly=false) { global $COURSE, $SITE, $DB, $CFG; // Function rebuild_course_cache() can not be called during upgrade unless it's clear only. if (!$clearonly && !upgrade_ensure_not_running(true)) { $clearonly = true; } // Destroy navigation caches navigation_cache::destroy_volatile_caches(); if (class_exists('format_base')) { // if file containing class is not loaded, there is no cache there anyway format_base::reset_course_cache($courseid); } $cachecoursemodinfo = cache::make('core', 'coursemodinfo'); if (empty($courseid)) { // Clearing caches for all courses. increment_revision_number('course', 'cacherev', ''); $cachecoursemodinfo->purge(); course_modinfo::clear_instance_cache(); // Update global values too. $sitecacherev = $DB->get_field('course', 'cacherev', array('id' => SITEID)); $SITE->cachrev = $sitecacherev; if ($COURSE->id == SITEID) { $COURSE->cacherev = $sitecacherev; } else { $COURSE->cacherev = $DB->get_field('course', 'cacherev', array('id' => $COURSE->id)); } } else { // Clearing cache for one course, make sure it is deleted from user request cache as well. increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $courseid)); $cachecoursemodinfo->delete($courseid); course_modinfo::clear_instance_cache($courseid); // Update global values too. if ($courseid == $COURSE->id || $courseid == $SITE->id) { $cacherev = $DB->get_field('course', 'cacherev', array('id' => $courseid)); if ($courseid == $COURSE->id) { $COURSE->cacherev = $cacherev; } if ($courseid == $SITE->id) { $SITE->cachrev = $cacherev; } } } if ($clearonly) { return; } if ($courseid) { $select = array('id'=>$courseid); } else { $select = array(); core_php_time_limit::raise(); // this could take a while! MDL-10954 } $rs = $DB->get_recordset("course", $select,'','id,'.join(',', course_modinfo::$cachedfields)); // Rebuild cache for each course. foreach ($rs as $course) { course_modinfo::build_course_cache($course); } $rs->close(); }
/** * Invalidates browser caches and cached data in temp. * * IMPORTANT - If you are adding anything here to do with the cache directory you should also have a look at * {@link phpunit_util::reset_dataroot()} * * @return void */ function purge_all_caches() { global $CFG, $DB; reset_text_filters_cache(); js_reset_all_caches(); theme_reset_all_caches(); get_string_manager()->reset_caches(); core_text::reset_caches(); if (class_exists('core_plugin_manager')) { core_plugin_manager::reset_caches(); } // Bump up cacherev field for all courses. try { increment_revision_number('course', 'cacherev', ''); } catch (moodle_exception $e) { // Ignore exception since this function is also called before upgrade script when field course.cacherev does not exist yet. } $DB->reset_caches(); cache_helper::purge_all(); // Purge all other caches: rss, simplepie, etc. clearstatcache(); remove_dir($CFG->cachedir . '', true); // Make sure cache dir is writable, throws exception if not. make_cache_directory(''); // This is the only place where we purge local caches, we are only adding files there. // The $CFG->localcachedirpurged flag forces local directories to be purged on cluster nodes. remove_dir($CFG->localcachedir, true); set_config('localcachedirpurged', time()); make_localcache_directory('', true); \core\task\manager::clear_static_caches(); }
/** * Pre-uninstall hook. * * This is intended for disabling of plugin, some DB table purging, etc. * * NOTE: to be called from uninstall_plugin() only. * @private */ public function uninstall_cleanup() { global $DB, $CFG; if (!($module = $DB->get_record('modules', array('name' => $this->name)))) { parent::uninstall_cleanup(); return; } // Delete all the relevant instances from all course sections. if ($coursemods = $DB->get_records('course_modules', array('module' => $module->id))) { foreach ($coursemods as $coursemod) { // Do not verify results, there is not much we can do anyway. delete_mod_from_section($coursemod->id, $coursemod->section); } } // Increment course.cacherev for courses that used this module. // This will force cache rebuilding on the next request. increment_revision_number('course', 'cacherev', "id IN (SELECT DISTINCT course\n FROM {course_modules}\n WHERE module=?)", array($module->id)); // Delete all the course module records. $DB->delete_records('course_modules', array('module' => $module->id)); // Delete module contexts. if ($coursemods) { foreach ($coursemods as $coursemod) { \context_helper::delete_instance(CONTEXT_MODULE, $coursemod->id); } } // Delete the module entry itself. $DB->delete_records('modules', array('name' => $module->name)); // Cleanup the gradebook. require_once $CFG->libdir . '/gradelib.php'; grade_uninstalled_module($module->name); // Do not look for legacy $module->name . '_uninstall any more, // they should have migrated to db/uninstall.php by now. parent::uninstall_cleanup(); }