Commit eabbfa82 authored by Marina Glancy's avatar Marina Glancy
Browse files

MDL-38147 Improvements to caching of course categories, coursecat::get_children() improvements

- Course categories caches are purged by event now
- session cache has additional 10 minutes ttl to clear itself and accomodate for permission changes that do not trigger event purging
- additional request-level cache for coursecat::get()
- We store only children of one category in one cache key
- Function coursecat::get_children() can return results sorted and/or paginated. Added tests
parent 15d50fff
......@@ -2649,6 +2649,7 @@ function move_courses($courseids, $categoryid) {
}
}
fix_course_sortorder();
cache_helper::purge_by_event('changesincourse');
return true;
}
......@@ -2828,6 +2829,8 @@ function create_course($data, $editoroptions = NULL) {
course_create_sections_if_missing($course, 0);
fix_course_sortorder();
// purge appropriate caches in case fix_course_sortorder() did not change anything
cache_helper::purge_by_event('changesincourse');
// new context created - better mark it as dirty
mark_context_dirty($context->path);
......@@ -2908,6 +2911,8 @@ function update_course($data, $editoroptions = NULL) {
}
fix_course_sortorder();
// purge appropriate caches in case fix_course_sortorder() did not change anything
cache_helper::purge_by_event('changesincourse');
// Test for and remove blocks which aren't appropriate anymore
blocks_remove_inappropriate($course);
......
......@@ -188,6 +188,7 @@ if ($coursecat->id && $canmanage && $resort && confirm_sesskey()) {
}
// This should not be needed but we do it just to be safe.
fix_course_sortorder();
cache_helper::purge_by_event('changesincourse');
}
}
......@@ -233,6 +234,7 @@ if ((!empty($hide) or !empty($show)) && confirm_sesskey()) {
// Set the visibility of the course. we set the old flag when user manually changes visibility of course.
$params = array('id' => $course->id, 'visible' => $visible, 'visibleold' => $visible, 'timemodified' => time());
$DB->update_record('course', $params);
cache_helper::purge_by_event('changesincourse');
add_to_log($course->id, "course", ($visible ? 'show' : 'hide'), "edit.php?id=$course->id", $course->id);
}
......@@ -260,6 +262,7 @@ if ((!empty($moveup) or !empty($movedown)) && confirm_sesskey()) {
}
$DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id' => $movecourse->id));
$DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id' => $swapcourse->id));
cache_helper::purge_by_event('changesincourse');
add_to_log($movecourse->id, "course", "move", "edit.php?id=$movecourse->id", $movecourse->id);
}
}
......
......@@ -36,7 +36,9 @@ $string['cacheadmin'] = 'Cache administration';
$string['cacheconfig'] = 'Configuration';
$string['cachedef_calendar_subscriptions'] = 'Calendar subscriptions';
$string['cachedef_config'] = 'Config settings';
$string['cachedef_coursecat'] = 'Course categories';
$string['cachedef_coursecat'] = 'Course categories lists for particular user';
$string['cachedef_coursecatrecords'] = 'Course categories records';
$string['cachedef_coursecattree'] = 'Course categories tree';
$string['cachedef_databasemeta'] = 'Database meta information';
$string['cachedef_eventinvalidation'] = 'Event invalidation';
$string['cachedef_groupdata'] = 'Course group information';
......
This diff is collapsed.
......@@ -951,9 +951,14 @@ function fix_course_sortorder() {
//WARNING: this is PHP5 only code!
// if there are any changes made to courses or categories we will trigger
// the cache events to purge all cached courses/categories data
$cacheevents = array();
if ($unsorted = $DB->get_records('course_categories', array('sortorder'=>0))) {
//move all categories that are not sorted yet to the end
$DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES, array('sortorder'=>0));
$cacheevents['changesincoursecat'] = true;
}
$allcats = $DB->get_records('course_categories', null, 'sortorder, id', 'id, sortorder, parent, depth, path');
......@@ -993,7 +998,9 @@ function fix_course_sortorder() {
// now walk recursively the tree and fix any problems found
$sortorder = 0;
$fixcontexts = array();
_fix_course_cats($topcats, $sortorder, 0, 0, '', $fixcontexts);
if (_fix_course_cats($topcats, $sortorder, 0, 0, '', $fixcontexts)) {
$cacheevents['changesincoursecat'] = true;
}
// detect if there are "multiple" frontpage courses and fix them if needed
$frontcourses = $DB->get_records('course', array('category'=>0), 'id');
......@@ -1009,6 +1016,7 @@ function fix_course_sortorder() {
$DB->set_field('course', 'category', $defaultcat->id, array('id'=>$course->id));
$context = context_course::instance($course->id);
$fixcontexts[$context->id] = $context;
$cacheevents['changesincourse'] = true;
}
unset($frontcourses);
} else {
......@@ -1022,6 +1030,8 @@ function fix_course_sortorder() {
}
context_helper::build_all_paths(false);
unset($fixcontexts);
$cacheevents['changesincourse'] = true;
$cacheevents['changesincoursecat'] = true;
}
// release memory
......@@ -1032,6 +1042,7 @@ function fix_course_sortorder() {
// fix frontpage course sortorder
if ($frontcourse->sortorder != 1) {
$DB->set_field('course', 'sortorder', 1, array('id'=>$frontcourse->id));
$cacheevents['changesincourse'] = true;
}
// now fix the course counts in category records if needed
......@@ -1056,6 +1067,7 @@ function fix_course_sortorder() {
$str = implode(', ', $categories);
debugging("The number of courses (category id: $str) has reached MAX_COURSES_IN_CATEGORY (" . MAX_COURSES_IN_CATEGORY . "), it will cause a sorting performance issue, please increase the value of MAX_COURSES_IN_CATEGORY in lib/datalib.php file. See tracker issue: MDL-25669", DEBUG_DEVELOPER);
}
$cacheevents['changesincoursecat'] = true;
}
// now make sure that sortorders in course table are withing the category sortorder ranges
......@@ -1072,6 +1084,7 @@ function fix_course_sortorder() {
WHERE category = ?";
$DB->execute($sql, array($cat->sortorder, $cat->id));
}
$cacheevents['changesincoursecat'] = true;
}
unset($fixcategories);
......@@ -1105,6 +1118,7 @@ function fix_course_sortorder() {
// it needs full resorting
$fixcategories[$cat->id] = $cat;
}
$cacheevents['changesincourse'] = true;
}
unset($gapcategories);
......@@ -1116,13 +1130,16 @@ function fix_course_sortorder() {
if ($course->sortorder != $cat->sortorder + $i) {
$course->sortorder = $cat->sortorder + $i;
$DB->update_record_raw('course', $course, true);
$cacheevents['changesincourse'] = true;
}
$i++;
}
}
$coursecatcache = cache::make('core', 'coursecat');
$coursecatcache->purge();
// advise all caches that need to be rebuilt
foreach (array_keys($cacheevents) as $event) {
cache_helper::purge_by_event($event);
}
}
/**
......@@ -1139,12 +1156,13 @@ function fix_course_sortorder() {
* @param int $depth
* @param string $path
* @param array $fixcontexts
* @return void
* @return bool if changes were made
*/
function _fix_course_cats($children, &$sortorder, $parent, $depth, $path, &$fixcontexts) {
global $DB;
$depth++;
$changesmade = false;
foreach ($children as $cat) {
$sortorder = $sortorder + MAX_COURSES_IN_CATEGORY;
......@@ -1165,11 +1183,15 @@ function _fix_course_cats($children, &$sortorder, $parent, $depth, $path, &$fixc
}
if ($update) {
$DB->update_record('course_categories', $cat, true);
$changesmade = true;
}
if (isset($cat->children)) {
_fix_course_cats($cat->children, $sortorder, $cat->id, $cat->depth, $cat->path, $fixcontexts);
if (_fix_course_cats($cat->children, $sortorder, $cat->id, $cat->depth, $cat->path, $fixcontexts)) {
$changesmade = true;
}
}
}
return $changesmade;
}
/**
......
......@@ -198,10 +198,31 @@ $definitions = array(
'persistentmaxsize' => 1,
),
// Used to store data for course categories
'coursecat' => array(
// Used to store the full tree of course categories
'coursecattree' => array(
'mode' => cache_store::MODE_APPLICATION,
'persistent' => true,
'invalidationevents' => array(
'changesincoursecat',
)
),
// Used to store data for course categories visible to current user. Helps to browse list of categories
'coursecat' => array(
'mode' => cache_store::MODE_SESSION,
'persistent' => true,
'invalidationevents' => array(
'changesincoursecat',
'changesincourse',
),
'ttl' => 600,
),
// Used to store data for course categories visible to current user. Helps to browse list of categories
'coursecatrecords' => array(
'mode' => cache_store::MODE_REQUEST,
'simplekeys' => true,
'persistent' => true,
'invalidationevents' => array(
'changesincoursecat',
),
),
);
......@@ -239,7 +239,7 @@ class coursecatlib_testcase extends advanced_testcase {
$this->assertEquals($testdescription, $category1->description);
$category1 = coursecat::get($category1->id);
$this->assertEquals($testdescription, $category1->description);
coursecat::purge_cache();
cache_helper::purge_by_event('changesincoursecat');
$category1 = coursecat::get($category1->id);
$this->assertEquals($testdescription, $category1->description);
......@@ -319,4 +319,44 @@ class coursecatlib_testcase extends advanced_testcase {
$this->assertEquals(array('id' => $course4->id, 'category' => $category1->id),
(array)$DB->get_record_sql('SELECT id, category from {course} where id <> ?', array(SITEID)));
}
public function test_get_children() {
$category1 = coursecat::create(array('name' => 'Cat1'));
$category2 = coursecat::create(array('name' => 'Cat2', 'parent' => $category1->id));
$category3 = coursecat::create(array('name' => 'Cat3', 'parent' => $category1->id, 'visible' => 0));
$category4 = coursecat::create(array('name' => 'Cat4', 'idnumber' => '12', 'parent' => $category1->id));
$category5 = coursecat::create(array('name' => 'Cat5', 'idnumber' => '11', 'parent' => $category1->id, 'visible' => 0));
$category6 = coursecat::create(array('name' => 'Cat6', 'idnumber' => '10', 'parent' => $category1->id));
$category7 = coursecat::create(array('name' => 'Cat0', 'parent' => $category1->id));
$children = $category1->get_children();
// user does not have the capability to view hidden categories, so the list should be
// 2,4,6,7
$this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children));
$this->assertEquals(4, $category1->get_children_count());
$children = $category1->get_children(array('offset' => 2));
$this->assertEquals(array($category6->id, $category7->id), array_keys($children));
$this->assertEquals(4, $category1->get_children_count());
$children = $category1->get_children(array('limit' => 2));
$this->assertEquals(array($category2->id, $category4->id), array_keys($children));
$children = $category1->get_children(array('offset' => 1, 'limit' => 2));
$this->assertEquals(array($category4->id, $category6->id), array_keys($children));
$children = $category1->get_children(array('sort' => array('name' => 1)));
// must be 7,2,4,6
$this->assertEquals(array($category7->id, $category2->id, $category4->id, $category6->id), array_keys($children));
$children = $category1->get_children(array('sort' => array('idnumber' => 1, 'name' => -1)));
// must be 2,7,6,4
$this->assertEquals(array($category2->id, $category7->id, $category6->id, $category4->id), array_keys($children));
// check that everything is all right after purging the caches
cache_helper::purge_by_event('changesincoursecat');
$children = $category1->get_children();
$this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children));
$this->assertEquals(4, $category1->get_children_count());
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment