Skip to content

Commit

Permalink
Merge branch 'MDL-61305-master' of https://github.com/sammarshallou/m…
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Feb 12, 2018
2 parents d461d09 + 08ec1b4 commit af5f446
Showing 1 changed file with 64 additions and 3 deletions.
67 changes: 64 additions & 3 deletions lib/modinfolib.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
* Is an array of grouping id => array of group id => group id. Includes grouping id 0 for 'all groups'
*/
class course_modinfo {
/** @var int Maximum time the course cache building lock can be held */
const COURSE_CACHE_LOCK_EXPIRY = 180;

/** @var int Time to wait for the course cache building lock before throwing an exception */
const COURSE_CACHE_LOCK_WAIT = 60;

/**
* List of fields from DB table 'course' that are cached in MUC and are always present in course_modinfo::$course
* @var array
Expand Down Expand Up @@ -448,7 +454,17 @@ public function __construct($course, $userid) {
// 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 = self::build_course_cache($course);
$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 = self::inner_build_course_cache($course, $lock);
}
} finally {
$lock->release();
}
}

// Set initial values
Expand Down Expand Up @@ -577,6 +593,34 @@ protected static function build_course_section_cache($course) {
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.
Expand All @@ -589,11 +633,28 @@ protected static function build_course_section_cache($course) {
* necessary fields it is re-requested from database)
*/
public static function build_course_cache($course) {
global $DB, $CFG;
require_once("$CFG->dirroot/course/lib.php");
if (empty($course->id)) {
throw new coding_exception('Object $course is missing required property \id\'');
}

$lock = self::get_course_cache_lock($course->id);
try {
return self::inner_build_course_cache($course, $lock);
} finally {
$lock->release();
}
}

/**
* 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
* @return stdClass Course object that has been stored in MUC
*/
protected static function inner_build_course_cache($course, \core\lock\lock $lock) {
global $DB;

// Ensure object has all necessary fields.
foreach (self::$cachedfields as $key) {
if (!isset($course->$key)) {
Expand Down

0 comments on commit af5f446

Please sign in to comment.