Skip to content

Commit

Permalink
MBS-8702: Improve caching (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
sh-csg authored Feb 21, 2024
1 parent a0fbf44 commit 5ac6e32
Show file tree
Hide file tree
Showing 19 changed files with 526 additions and 64 deletions.
2 changes: 1 addition & 1 deletion amd/build/placestore.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion amd/build/placestore.min.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions amd/src/placestore.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
let placestore = {
version: 2024012601,
version: 2024022102,
id: 0,
places: [],
paths: [],
Expand Down Expand Up @@ -36,7 +36,7 @@ let placestore = {
// eslint-disable-next-line no-empty
} catch { }
// Update version (only relevant if learning map is saved)
this.version = 2024012601;
this.version = 2024022102;
},
/**
* Returns placestore as a JSON string ()
Expand Down
2 changes: 1 addition & 1 deletion backup/moodle2/backup_learningmap_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ protected function define_structure(): backup_nested_element {
$learningmap = new backup_nested_element(
'learningmap',
['id'],
['course', 'name', 'intro', 'introformat', 'timemodified', 'placestore', 'completiontype']
['course', 'name', 'intro', 'introformat', 'timemodified', 'placestore', 'completiontype', 'backlink']
);

$learningmap->set_source_table('learningmap', ['id' => backup::VAR_ACTIVITYID]);
Expand Down
4 changes: 2 additions & 2 deletions backup/moodle2/restore_learningmap_activity_task.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ public function after_restore(): void {
$item->intro = str_replace('learningmap-svgmap-' . $oldmapid, 'learningmap-svgmap-' . $newmapid, $item->intro);
$placestore->mapid = $newmapid;

if (!isset($placestore->version) || $placestore->version < 2024012601) {
$placestore->version = 2024012601;
if (!isset($placestore->version) || $placestore->version < 2024022102) {
$placestore->version = 2024022102;
// Needs 1 as default value (otherwise all place strokes would be hidden).
if (!isset($placestore->strokeopacity)) {
$placestore->strokeopacity = 1;
Expand Down
27 changes: 17 additions & 10 deletions classes/autoupdate.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public static function update_from_event(\core\event\base $event): void {
*/
public static function update_from_delete_event(\core\event\base $event): void {
global $DB;

$data = $event->get_data();
if (isset($data['courseid']) && $data['courseid'] > 0) {
$modinfo = get_fast_modinfo($data['courseid']);
Expand All @@ -85,6 +86,7 @@ public static function update_from_delete_event(\core\event\base $event): void {
foreach ($placestore->places as $p) {
if ($p->linkedActivity == $data['objectid']) {
$p->linkedActivity = null;
cachemanager::remove_cmid($data['objectid']);
$changed = true;
}
}
Expand All @@ -95,23 +97,28 @@ public static function update_from_delete_event(\core\event\base $event): void {
}
}
}
self::reset_backlink_cache($event);

// If the course module was a learningmap, reset the backlink cache of the whole course.
self::reset_backlink_cache_if_necessary($event);
}

/**
* Resets backlink cache of the whole course if a learningmap was created / updated / deleted.
* Resets backlink cache of the whole course if a learningmap was created / updated / deleted or if
* the course settings have changed (as course format may have changed).
* @param \core\event\base $event
* @return void
* @return bool
*/
public static function reset_backlink_cache(\core\event\base $event): void {
public static function reset_backlink_cache_if_necessary(\core\event\base $event): bool {
$data = $event->get_data();
if (isset($data['courseid']) && $data['courseid'] > 0) {
$course = $data['courseid'];
$cache = \cache::make('mod_learningmap', 'backlinks');
$modinfo = get_fast_modinfo($course);
$cms = $modinfo->get_cms();
foreach ($cms as $cm) {
$cache->delete($cm->id);
if (
($data['objecttable'] == 'course' && in_array('format', $data['other']['updatedfields'])) ||
($data['objecttable'] == 'course_modules' && $data['other']['modulename'] == 'learningmap')
) {
cachemanager::reset_backlink_cache($data['courseid']);
return true;
}
}
return false;
}
}
113 changes: 113 additions & 0 deletions classes/cachemanager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.

namespace mod_learningmap;

/**
* Cache manager class for mod_learningmap
*
* @package mod_learningmap
* @copyright 2021-2024, ISB Bayern
* @author Stefan Hanauska <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cachemanager {
/**
* Reset the backlink cache for a course (includes rebuilding it) or the whole instance (if $courseid is 0).
*
* @param int $courseid The id of the course (defaults to 0).
* @return void
*/
public static function reset_backlink_cache(int $courseid = 0): void {
if (get_config('mod_learningmap', 'backlinkallowed') == 0) {
return;
}

$cache = \cache::make('mod_learningmap', 'backlinks');

if (empty($courseid)) {
// This also deletes the cache key 'fillstate', so the whole cache is invalidated.
$cache->purge();
} else {
$modinfo = get_fast_modinfo($courseid);
$cms = $modinfo->get_cms();
$cache->delete_many(array_keys($cms));
}
self::build_backlink_cache($courseid);
}

/**
* Builds the backlink cache for a course or for the whole instance (e.g. after purging the cache).
* Building the cache for a course should only be used as a fallback if the cache is not filled for the whole instance.
*
* @param int $courseid Id of the course, if 0 the cache will be built for the whole instance.
* @return void
*/
public static function build_backlink_cache(int $courseid = 0) {
global $DB;

if (get_config('mod_learningmap', 'backlinkallowed') == 0) {
return;
}

$backlinks = [];
$cache = \cache::make('mod_learningmap', 'backlinks');

$conditions = ['backlink' => 1];
if (!empty($courseid)) {
$conditions['course'] = $courseid;
}

$records = $DB->get_recordset('learningmap', $conditions, '', 'id, placestore, backlink, course');
foreach ($records as $record) {
$modinfo = get_fast_modinfo($record->course);
$module = $modinfo->instances['learningmap'][$record->id];
$placestore = json_decode($record->placestore);
$coursepageurl = course_get_format($module->course)->get_view_url($module->sectionnum);
$coursepageurl->set_anchor('module-' . $module->id);
foreach ($placestore->places as $place) {
$url = !empty($module->showdescription) ?
$coursepageurl->out() :
new \moodle_url('/mod/learningmap/view.php', ['id' => $module->id]);
$backlinks[$place->linkedActivity][$module->id] = [
'url' => $url,
'name' => $module->name,
'cmid' => $module->id,
];
}
}

foreach ($backlinks as $cmid => $backlink) {
$cache->set($cmid, $backlink);
}

if (empty($courseid)) {
// Finally set the flag to indicate that the cache is properly built.
$cache->set('fillstate', time());
}
}

/**
* Removes a cmid from the backlink cache (e.g. when the course module was deleted).
*
* @param int $cmid Course module id
* @return void
*/
public static function remove_cmid(int $cmid) {
$cache = \cache::make('mod_learningmap', 'backlinks');
$cache->delete($cmid);
}
}
61 changes: 61 additions & 0 deletions classes/task/fill_backlink_cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace mod_learningmap\task;

use mod_learningmap\cachemanager;

/**
* Task to fill backlink cache.
*
* @package mod_learningmap
* @copyright 2021-2024, ISB Bayern
* @author Stefan Hanauska
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fill_backlink_cache extends \core\task\scheduled_task {
/**
* Get a descriptive name for this task (shown to admins).
*
* @return string
*/
public function get_name() {
return get_string('fill_backlink_cache_task', 'mod_learningmap');
}

/**
* Fill backlink cache.
*/
public function execute() {
if (get_config('mod_learningmap', 'backlinkallowed') == 0) {
return;
}

$cache = \cache::make('mod_learningmap', 'backlinks');

$fillstate = $cache->get('fillstate');

// If the cache is filled within the last 24 hours, do nothing.
if (!empty($fillstate) && $fillstate < time() - 60 * 60 * 24) {
mtrace('Backlink cache is already filled within the last 24 hours. Exiting.');
return;
}

mtrace('Building backlink cache started...');
cachemanager::build_backlink_cache();
mtrace('Building backlink cache finished.');
}
}
Loading

0 comments on commit 5ac6e32

Please sign in to comment.