Skip to content

Commit

Permalink
MDL-63686 core: Preload parent contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Jan 8, 2019
1 parent 38a1b4f commit ccbdced
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
32 changes: 32 additions & 0 deletions lib/accesslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -5434,6 +5434,9 @@ public function get_parent_contexts($includeself = false) {
return array();
}

// Preload the contexts to reduce DB calls.
context_helper::preload_contexts_by_id($contextids);

$result = array();
foreach ($contextids as $contextid) {
$parent = context::instance_by_id($contextid, MUST_EXIST);
Expand Down Expand Up @@ -5870,6 +5873,35 @@ public static function preload_from_record(stdClass $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.
*
Expand Down
41 changes: 41 additions & 0 deletions lib/tests/accesslib_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -3820,6 +3820,47 @@ public function test_get_profile_roles() {
$this->setUser($user2);
$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);
}
}

/**
Expand Down

0 comments on commit ccbdced

Please sign in to comment.