MDL-72837 core_cache: Use versioned cache for modinfo

Uses the new versioned cache feature for modinfo, which should make it
safe as a localisable cache.
This commit is contained in:
sam marshall 2022-01-12 13:21:49 +00:00
parent 8a0f706033
commit 02bb326314
3 changed files with 21 additions and 25 deletions

View File

@ -884,8 +884,9 @@ class courselib_test extends advanced_testcase {
$this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence); $this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence);
// Check that modinfo cache was reset but not rebuilt (important for performance if calling repeatedly). // Check that modinfo cache was reset but not rebuilt (important for performance if calling repeatedly).
$this->assertGreaterThan($coursecacherev, $DB->get_field('course', 'cacherev', array('id' => $course->id))); $newcacherev = $DB->get_field('course', 'cacherev', ['id' => $course->id]);
$this->assertEmpty(cache::make('core', 'coursemodinfo')->get($course->id)); $this->assertGreaterThan($coursecacherev, $newcacherev);
$this->assertEmpty(cache::make('core', 'coursemodinfo')->get_versioned($course->id, $newcacherev));
// Add one to section that doesn't exist (this might rebuild modinfo). // Add one to section that doesn't exist (this might rebuild modinfo).
course_add_cm_to_section($course, $cmids[2], 2); course_add_cm_to_section($course, $cmids[2], 2);

View File

@ -475,14 +475,14 @@ class course_modinfo {
$cachecoursemodinfo = cache::make('core', 'coursemodinfo'); $cachecoursemodinfo = cache::make('core', 'coursemodinfo');
// Retrieve modinfo from cache. If not present or cacherev mismatches, call rebuild and retrieve again. // Retrieve modinfo from cache. If not present or cacherev mismatches, call rebuild and retrieve again.
$coursemodinfo = $cachecoursemodinfo->get($course->id); $coursemodinfo = $cachecoursemodinfo->get_versioned($course->id, $course->cacherev);
if ($coursemodinfo === false || ($course->cacherev != $coursemodinfo->cacherev)) { if (!$coursemodinfo) {
$lock = self::get_course_cache_lock($course->id); $lock = self::get_course_cache_lock($course->id);
try { try {
// Only actually do the build if it's still needed after getting the lock (not if // Only actually do the build if it's still needed after getting the lock (not if
// somebody else, who might have been holding the lock, built it already). // somebody else, who might have been holding the lock, built it already).
$coursemodinfo = $cachecoursemodinfo->get($course->id); $coursemodinfo = $cachecoursemodinfo->get_versioned($course->id, $course->cacherev);
if ($coursemodinfo === false || ($course->cacherev != $coursemodinfo->cacherev)) { if (!$coursemodinfo) {
$coursemodinfo = self::inner_build_course_cache($course, $lock); $coursemodinfo = self::inner_build_course_cache($course, $lock);
} }
} finally { } finally {
@ -681,17 +681,12 @@ class course_modinfo {
global $DB, $CFG; global $DB, $CFG;
require_once("{$CFG->dirroot}/course/lib.php"); require_once("{$CFG->dirroot}/course/lib.php");
// Ensure object has all necessary fields. // Always reload the course object from database to ensure we have the latest possible
foreach (self::$cachedfields as $key) { // value for cacherev.
if (!isset($course->$key)) { $course = $DB->get_record('course', ['id' => $course->id],
$course = $DB->get_record('course', array('id' => $course->id), implode(',', array_merge(['id'], self::$cachedfields)), MUST_EXIST);
implode(',', array_merge(array('id'), self::$cachedfields)), MUST_EXIST);
break;
}
}
// Retrieve all information about activities and sections. // Retrieve all information about activities and sections.
// This may take time on large courses and it is possible that another user modifies the same course during this process.
// Field cacherev stored in both DB and cache will ensure that cached data matches the current course state.
$coursemodinfo = new stdClass(); $coursemodinfo = new stdClass();
$coursemodinfo->modinfo = get_array_of_activities($course->id); $coursemodinfo->modinfo = get_array_of_activities($course->id);
$coursemodinfo->sectioncache = self::build_course_section_cache($course); $coursemodinfo->sectioncache = self::build_course_section_cache($course);
@ -700,7 +695,7 @@ class course_modinfo {
} }
// Set the accumulated activities and sections information in cache, together with cacherev. // Set the accumulated activities and sections information in cache, together with cacherev.
$cachecoursemodinfo = cache::make('core', 'coursemodinfo'); $cachecoursemodinfo = cache::make('core', 'coursemodinfo');
$cachecoursemodinfo->set($course->id, $coursemodinfo); $cachecoursemodinfo->set_versioned($course->id, $course->cacherev, $coursemodinfo);
return $coursemodinfo; return $coursemodinfo;
} }
} }

View File

@ -260,28 +260,28 @@ class modinfolib_test extends advanced_testcase {
rebuild_course_cache($course->id, true); rebuild_course_cache($course->id, true);
$cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
$this->assertGreaterThan($prevcacherev, $cacherev); $this->assertGreaterThan($prevcacherev, $cacherev);
$this->assertEmpty($cache->get($course->id)); $this->assertEmpty($cache->get_versioned($course->id, $prevcacherev));
$prevcacherev = $cacherev; $prevcacherev = $cacherev;
// Build course cache. Cacherev should not change but cache is now not empty. Make sure cacherev is the same everywhere. // 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); $modinfo = get_fast_modinfo($course->id);
$cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
$this->assertEquals($prevcacherev, $cacherev); $this->assertEquals($prevcacherev, $cacherev);
$cachedvalue = $cache->get($course->id); $cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue); $this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $cachedvalue->cacherev);
$this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev);
$prevcacherev = $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. // 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))); $cache->set_versioned($course->id, $cacherev, (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. // 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(); course_modinfo::clear_instance_cache();
$modinfo = get_fast_modinfo($course->id); $modinfo = get_fast_modinfo($course->id);
$cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
$this->assertEquals($prevcacherev, $cacherev); $this->assertEquals($prevcacherev, $cacherev);
$cachedvalue = $cache->get($course->id); $cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue); $this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $cachedvalue->cacherev);
$this->assertNotEmpty($cachedvalue->secretfield); $this->assertNotEmpty($cachedvalue->secretfield);
@ -292,7 +292,7 @@ class modinfolib_test extends advanced_testcase {
rebuild_course_cache($course->id); rebuild_course_cache($course->id);
$cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
$this->assertGreaterThan($prevcacherev, $cacherev); $this->assertGreaterThan($prevcacherev, $cacherev);
$cachedvalue = $cache->get($course->id); $cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue); $this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $cachedvalue->cacherev);
$modinfo = get_fast_modinfo($course->id); $modinfo = get_fast_modinfo($course->id);
@ -306,7 +306,7 @@ class modinfolib_test extends advanced_testcase {
$modinfo = get_fast_modinfo($course->id); $modinfo = get_fast_modinfo($course->id);
$cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
$this->assertGreaterThan($prevcacherev, $cacherev); $this->assertGreaterThan($prevcacherev, $cacherev);
$cachedvalue = $cache->get($course->id); $cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue); $this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $cachedvalue->cacherev);
$this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev);
@ -316,10 +316,10 @@ class modinfolib_test extends advanced_testcase {
rebuild_course_cache(0, true); rebuild_course_cache(0, true);
$cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
$this->assertGreaterThan($prevcacherev, $cacherev); $this->assertGreaterThan($prevcacherev, $cacherev);
$this->assertEmpty($cache->get($course->id)); $this->assertEmpty($cache->get_versioned($course->id, $cacherev));
// Rebuild again. // Rebuild again.
$modinfo = get_fast_modinfo($course->id); $modinfo = get_fast_modinfo($course->id);
$cachedvalue = $cache->get($course->id); $cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue); $this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $cachedvalue->cacherev);
$this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev);