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);
// 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)));
$this->assertEmpty(cache::make('core', 'coursemodinfo')->get($course->id));
$newcacherev = $DB->get_field('course', 'cacherev', ['id' => $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).
course_add_cm_to_section($course, $cmids[2], 2);

View File

@ -475,14 +475,14 @@ class course_modinfo {
$cachecoursemodinfo = cache::make('core', 'coursemodinfo');
// Retrieve modinfo from cache. If not present or cacherev mismatches, call rebuild and retrieve again.
$coursemodinfo = $cachecoursemodinfo->get($course->id);
if ($coursemodinfo === false || ($course->cacherev != $coursemodinfo->cacherev)) {
$coursemodinfo = $cachecoursemodinfo->get_versioned($course->id, $course->cacherev);
if (!$coursemodinfo) {
$lock = self::get_course_cache_lock($course->id);
try {
// 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).
$coursemodinfo = $cachecoursemodinfo->get($course->id);
if ($coursemodinfo === false || ($course->cacherev != $coursemodinfo->cacherev)) {
$coursemodinfo = $cachecoursemodinfo->get_versioned($course->id, $course->cacherev);
if (!$coursemodinfo) {
$coursemodinfo = self::inner_build_course_cache($course, $lock);
}
} finally {
@ -681,17 +681,12 @@ class course_modinfo {
global $DB, $CFG;
require_once("{$CFG->dirroot}/course/lib.php");
// Ensure object has all necessary fields.
foreach (self::$cachedfields as $key) {
if (!isset($course->$key)) {
$course = $DB->get_record('course', array('id' => $course->id),
implode(',', array_merge(array('id'), self::$cachedfields)), MUST_EXIST);
break;
}
}
// Always reload the course object from database to ensure we have the latest possible
// value for cacherev.
$course = $DB->get_record('course', ['id' => $course->id],
implode(',', array_merge(['id'], self::$cachedfields)), MUST_EXIST);
// 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->modinfo = get_array_of_activities($course->id);
$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.
$cachecoursemodinfo = cache::make('core', 'coursemodinfo');
$cachecoursemodinfo->set($course->id, $coursemodinfo);
$cachecoursemodinfo->set_versioned($course->id, $course->cacherev, $coursemodinfo);
return $coursemodinfo;
}
}

View File

@ -260,28 +260,28 @@ class modinfolib_test extends advanced_testcase {
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));
$this->assertEmpty($cache->get_versioned($course->id, $prevcacherev));
$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);
$cachedvalue = $cache->get_versioned($course->id, $cacherev);
$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)));
$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.
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);
$cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev);
$this->assertNotEmpty($cachedvalue->secretfield);
@ -292,7 +292,7 @@ class modinfolib_test extends advanced_testcase {
rebuild_course_cache($course->id);
$cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
$this->assertGreaterThan($prevcacherev, $cacherev);
$cachedvalue = $cache->get($course->id);
$cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev);
$modinfo = get_fast_modinfo($course->id);
@ -306,7 +306,7 @@ class modinfolib_test extends advanced_testcase {
$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);
$cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev);
$this->assertEquals($cacherev, $modinfo->get_course()->cacherev);
@ -316,10 +316,10 @@ class modinfolib_test extends advanced_testcase {
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));
$this->assertEmpty($cache->get_versioned($course->id, $cacherev));
// Rebuild again.
$modinfo = get_fast_modinfo($course->id);
$cachedvalue = $cache->get($course->id);
$cachedvalue = $cache->get_versioned($course->id, $cacherev);
$this->assertNotEmpty($cachedvalue);
$this->assertEquals($cacherev, $cachedvalue->cacherev);
$this->assertEquals($cacherev, $modinfo->get_course()->cacherev);