mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 16:32:18 +02:00
MDL-67020 Cache: Make local caching work for coursemodinfo
This commit is contained in:
parent
045ee091da
commit
ea01fb2427
@ -251,6 +251,7 @@ $definitions = array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true,
|
||||
'canuselocalstore' => true,
|
||||
'requirelockingbeforewrite' => true
|
||||
),
|
||||
// This is the session user selections cache.
|
||||
// It's a special cache that is used to record user selections that should persist for the lifetime of the session.
|
||||
|
@ -475,7 +475,8 @@ class course_modinfo {
|
||||
// partial rebuild logic sometimes sets the $coursemodinfo->cacherev to -1 which is an
|
||||
// indicator that it needs rebuilding.
|
||||
if ($coursemodinfo === false || ($course->cacherev > $coursemodinfo->cacherev)) {
|
||||
$lock = self::get_course_cache_lock($course->id);
|
||||
$cachekey = $course->id;
|
||||
$lock = $cachecoursemodinfo->acquire_lock($cachekey);
|
||||
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).
|
||||
@ -484,7 +485,7 @@ class course_modinfo {
|
||||
$coursemodinfo = self::inner_build_course_cache($course, $lock);
|
||||
}
|
||||
} finally {
|
||||
$lock->release();
|
||||
$cachecoursemodinfo->release_lock($cachekey);
|
||||
}
|
||||
}
|
||||
|
||||
@ -631,34 +632,6 @@ class course_modinfo {
|
||||
return $compressedsections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a lock for rebuilding the cache of a single course.
|
||||
*
|
||||
* Caller must release the returned lock.
|
||||
*
|
||||
* This is used to ensure that the cache rebuild doesn't happen multiple times in parallel.
|
||||
* This function will wait up to 1 minute for the lock to be obtained. If the lock cannot
|
||||
* be obtained, it throws an exception.
|
||||
*
|
||||
* @param int $courseid Course id
|
||||
* @return \core\lock\lock Lock (must be released!)
|
||||
* @throws moodle_exception If the lock cannot be obtained
|
||||
*/
|
||||
protected static function get_course_cache_lock($courseid) {
|
||||
// Get database lock to ensure this doesn't happen multiple times in parallel. Wait a
|
||||
// reasonable time for the lock to be released, so we can give a suitable error message.
|
||||
// In case the system crashes while building the course cache, the lock will automatically
|
||||
// expire after a (slightly longer) period.
|
||||
$lockfactory = \core\lock\lock_config::get_lock_factory('core_modinfo');
|
||||
$lock = $lockfactory->get_lock('build_course_cache_' . $courseid,
|
||||
self::COURSE_CACHE_LOCK_WAIT, self::COURSE_CACHE_LOCK_EXPIRY);
|
||||
if (!$lock) {
|
||||
throw new moodle_exception('locktimeout', '', '', null,
|
||||
'core_modinfo/build_course_cache_' . $courseid);
|
||||
}
|
||||
return $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and stores in MUC object containing information about course
|
||||
* modules and sections together with cached fields from table course.
|
||||
@ -676,11 +649,13 @@ class course_modinfo {
|
||||
throw new coding_exception('Object $course is missing required property \id\'');
|
||||
}
|
||||
|
||||
$lock = self::get_course_cache_lock($course->id);
|
||||
$cachecoursemodinfo = cache::make('core', 'coursemodinfo');
|
||||
$cachekey = $course->id;
|
||||
$cachecoursemodinfo->acquire_lock($cachekey);
|
||||
try {
|
||||
return self::inner_build_course_cache($course, $lock, $partialrebuild);
|
||||
return self::inner_build_course_cache($course, $partialrebuild);
|
||||
} finally {
|
||||
$lock->release();
|
||||
$cachecoursemodinfo->release_lock($cachekey);
|
||||
}
|
||||
}
|
||||
|
||||
@ -688,20 +663,23 @@ class course_modinfo {
|
||||
* Called to build course cache when there is already a lock obtained.
|
||||
*
|
||||
* @param stdClass $course object from DB table course
|
||||
* @param \core\lock\lock $lock Lock object - not actually used, just there to indicate you have a lock
|
||||
* @param bool $partialrebuild Indicate if it's partial course cache rebuild or not
|
||||
* @return stdClass Course object that has been stored in MUC
|
||||
*/
|
||||
protected static function inner_build_course_cache(\stdClass $course, \core\lock\lock $lock,
|
||||
bool $partialrebuild = false): \stdClass {
|
||||
protected static function inner_build_course_cache(\stdClass $course, bool $partialrebuild = false): \stdClass {
|
||||
global $DB, $CFG;
|
||||
require_once("{$CFG->dirroot}/course/lib.php");
|
||||
|
||||
$cachekey = $course->id;
|
||||
$cachecoursemodinfo = cache::make('core', 'coursemodinfo');
|
||||
if (!$cachecoursemodinfo->check_lock_state($cachekey)) {
|
||||
throw new coding_exception('You must acquire a lock on the course ID before calling inner_build_course_cache');
|
||||
}
|
||||
|
||||
// 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.
|
||||
$coursemodinfo = new stdClass();
|
||||
$coursemodinfo->modinfo = self::get_array_of_activities($course, $partialrebuild);
|
||||
@ -710,8 +688,7 @@ class course_modinfo {
|
||||
$coursemodinfo->$key = $course->$key;
|
||||
}
|
||||
// Set the accumulated activities and sections information in cache, together with cacherev.
|
||||
$cachecoursemodinfo = cache::make('core', 'coursemodinfo');
|
||||
$cachecoursemodinfo->set_versioned($course->id, $course->cacherev, $coursemodinfo);
|
||||
$cachecoursemodinfo->set_versioned($cachekey, $course->cacherev, $coursemodinfo);
|
||||
return $coursemodinfo;
|
||||
}
|
||||
|
||||
@ -724,19 +701,20 @@ class course_modinfo {
|
||||
public static function purge_course_section_cache_by_id(int $courseid, int $sectionid): void {
|
||||
$course = get_course($courseid);
|
||||
$cache = cache::make('core', 'coursemodinfo');
|
||||
$cache->acquire_lock($course->id);
|
||||
$coursemodinfo = $cache->get_versioned($course->id, $course->cacherev);
|
||||
$cachekey = $course->id;
|
||||
$cache->acquire_lock($cachekey);
|
||||
$coursemodinfo = $cache->get_versioned($cachekey, $course->cacherev);
|
||||
if ($coursemodinfo !== false) {
|
||||
foreach ($coursemodinfo->sectioncache as $sectionno => $sectioncache) {
|
||||
if ($sectioncache->id == $sectionid) {
|
||||
$coursemodinfo->cacherev = -1;
|
||||
unset($coursemodinfo->sectioncache[$sectionno]);
|
||||
$cache->set_versioned($course->id, $course->cacherev, $coursemodinfo);
|
||||
$cache->set_versioned($cachekey, $course->cacherev, $coursemodinfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$cache->release_lock($course->id);
|
||||
$cache->release_lock($cachekey);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -748,14 +726,15 @@ class course_modinfo {
|
||||
public static function purge_course_section_cache_by_number(int $courseid, int $sectionno): void {
|
||||
$course = get_course($courseid);
|
||||
$cache = cache::make('core', 'coursemodinfo');
|
||||
$cache->acquire_lock($course->id);
|
||||
$coursemodinfo = $cache->get_versioned($course->id, $course->cacherev);
|
||||
$cachekey = $course->id;
|
||||
$cache->acquire_lock($cachekey);
|
||||
$coursemodinfo = $cache->get_versioned($cachekey, $course->cacherev);
|
||||
if ($coursemodinfo !== false && array_key_exists($sectionno, $coursemodinfo->sectioncache)) {
|
||||
$coursemodinfo->cacherev = -1;
|
||||
unset($coursemodinfo->sectioncache[$sectionno]);
|
||||
$cache->set_versioned($course->id, $course->cacherev, $coursemodinfo);
|
||||
$cache->set_versioned($cachekey, $course->cacherev, $coursemodinfo);
|
||||
}
|
||||
$cache->release_lock($course->id);
|
||||
$cache->release_lock($cachekey);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -767,16 +746,17 @@ class course_modinfo {
|
||||
public static function purge_course_module_cache(int $courseid, int $cmid): void {
|
||||
$course = get_course($courseid);
|
||||
$cache = cache::make('core', 'coursemodinfo');
|
||||
$cache->acquire_lock($course->id);
|
||||
$coursemodinfo = $cache->get_versioned($course->id, $course->cacherev);
|
||||
$cachekey = $course->id;
|
||||
$cache->acquire_lock($cachekey);
|
||||
$coursemodinfo = $cache->get_versioned($cachekey, $course->cacherev);
|
||||
$hascache = ($coursemodinfo !== false) && array_key_exists($cmid, $coursemodinfo->modinfo);
|
||||
if ($hascache) {
|
||||
$coursemodinfo->cacherev = -1;
|
||||
unset($coursemodinfo->modinfo[$cmid]);
|
||||
$cache->set_versioned($course->id, $course->cacherev, $coursemodinfo);
|
||||
$coursemodinfo = $cache->get_versioned($course->id, $course->cacherev);
|
||||
$cache->set_versioned($cachekey, $course->cacherev, $coursemodinfo);
|
||||
$coursemodinfo = $cache->get_versioned($cachekey, $course->cacherev);
|
||||
}
|
||||
$cache->release_lock($course->id);
|
||||
$cache->release_lock($cachekey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -274,7 +274,9 @@ class modinfolib_test extends advanced_testcase {
|
||||
$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->acquire_lock($course->id);
|
||||
$cache->set_versioned($course->id, $cacherev, (object)array_merge((array)$cachedvalue, array('secretfield' => 1)));
|
||||
$cache->release_lock($course->id);
|
||||
|
||||
// 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();
|
||||
|
@ -9,6 +9,9 @@ Declaration is as follow:
|
||||
$deprecatedcapabilities = [
|
||||
'fake/access:fakecapability' => ['replacement' => '', 'message' => 'This capability should not be used anymore.']
|
||||
];
|
||||
* coursemodinfo cache uses the new `requirelockingbeforewrite` option, and rebuilding the cache now uses the cache lock API, rather
|
||||
than using the core lock factory directly. This allows the locks to be stored locally if the cache is stored locally, and
|
||||
avoids the risk of delays and timeouts when multiple nodes need to rebuild the cache locally, but are waiting for a central lock.
|
||||
|
||||
=== 4.1 ===
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2022101800.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2022101800.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '4.1dev+ (Build: 20221018)'; // Human-friendly version name
|
||||
|
Loading…
x
Reference in New Issue
Block a user