MDL-63686 core: Preload parent contexts

This commit is contained in:
Andrew Nicols 2018-10-17 13:36:39 +08:00
parent 2fbae51df0
commit 5b01d6249b
2 changed files with 73 additions and 0 deletions

View file

@ -5334,6 +5334,9 @@ abstract class context extends stdClass implements IteratorAggregate {
return array(); return array();
} }
// Preload the contexts to reduce DB calls.
context_helper::preload_contexts_by_id($contextids);
$result = array(); $result = array();
foreach ($contextids as $contextid) { foreach ($contextids as $contextid) {
$parent = context::instance_by_id($contextid, MUST_EXIST); $parent = context::instance_by_id($contextid, MUST_EXIST);
@ -5710,6 +5713,35 @@ class context_helper extends context {
context::preload_from_record($rec); context::preload_from_record($rec);
} }
/**
* Preload a set of contexts using their contextid.
*
* @param array $contextids
*/
public static function preload_contexts_by_id(array $contextids) {
global $DB;
// Determine which contexts are not already cached.
$tofetch = [];
foreach ($contextids as $contextid) {
if (!self::cache_get_by_id($contextid)) {
$tofetch[] = $contextid;
}
}
if (count($tofetch) > 1) {
// There are at least two to fetch.
// There is no point only fetching a single context as this would be no more efficient than calling the existing code.
list($insql, $inparams) = $DB->get_in_or_equal($tofetch, SQL_PARAMS_NAMED);
$ctxs = $DB->get_recordset_select('context', "id {$insql}", $inparams, '',
\context_helper::get_preload_record_columns_sql('{context}'));
foreach ($ctxs as $ctx) {
self::preload_from_record($ctx);
}
$ctxs->close();
}
}
/** /**
* Preload all contexts instances from course. * Preload all contexts instances from course.
* *

View file

@ -3782,6 +3782,47 @@ class core_accesslib_testcase extends advanced_testcase {
$this->setUser($user2); $this->setUser($user2);
$this->assertEquals($expectedteacher, get_profile_roles($coursecontext)); $this->assertEquals($expectedteacher, get_profile_roles($coursecontext));
} }
/**
* Ensure that the get_parent_contexts() function limits the number of queries it performs.
*/
public function test_get_parent_contexts_preload() {
global $DB;
$this->resetAfterTest();
/*
* Given the following data structure:
* System
* - Category
* --- Category
* ----- Category
* ------- Category
* --------- Course
* ----------- Activity (Forum)
*/
$contexts = [];
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category(['parent' => $cat1->id]);
$cat3 = $this->getDataGenerator()->create_category(['parent' => $cat2->id]);
$cat4 = $this->getDataGenerator()->create_category(['parent' => $cat3->id]);
$course = $this->getDataGenerator()->create_course(['category' => $cat4->id]);
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
$modcontext = context_module::instance($forum->cmid);
context_helper::reset_caches();
// There should only be a single DB query.
$predbqueries = $DB->perf_get_reads();
$parents = $modcontext->get_parent_contexts();
// Note: For some databases There is one read, plus one FETCH, plus one CLOSE.
// These all show as reads, when there has actually only been a single query.
$this->assertLessThanOrEqual(3, $DB->perf_get_reads() - $predbqueries);
}
} }
/** /**