Skip to content

Commit 9edc675

Browse files
committed
MDL-86493 mod_quiz: Optimise mod_quiz_cm_info_dynamic
1 parent 74ee977 commit 9edc675

File tree

8 files changed

+332
-285
lines changed

8 files changed

+332
-285
lines changed

public/mod/quiz/classes/cache/overrides.php

Lines changed: 0 additions & 115 deletions
This file was deleted.
Lines changed: 108 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
// This file is part of Moodle - http://moodle.org/
2+
// This file is part of Moodle - https://moodle.org/
33
//
44
// Moodle is free software: you can redistribute it and/or modify
55
// it under the terms of the GNU General Public License as published by
@@ -12,115 +12,160 @@
1212
// GNU General Public License for more details.
1313
//
1414
// You should have received a copy of the GNU General Public License
15-
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15+
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
1616

1717
namespace mod_quiz\local;
1818

19+
use cache;
20+
use core_cache\data_source_interface;
21+
use core_cache\definition;
22+
1923
/**
20-
* Cache manager for quiz overrides
24+
* Cache encapsulation for quiz overrides.
2125
*
22-
* Override cache data is set via its data source, {@see \mod_quiz\cache\overrides}
23-
* @package mod_quiz
24-
* @copyright 2024 Matthew Hilton <[email protected]>
25-
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26+
* @package mod_quiz
27+
* @copyright 2025 Catalyst IT Australia Pty Ltd
28+
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2629
*/
27-
class override_cache {
28-
/** @var string invalidation event used to purge data when reset_userdata is called, {@see \cache_helper::purge_by_event()} **/
29-
public const INVALIDATION_USERDATARESET = 'userdatareset';
30+
class override_cache implements data_source_interface {
31+
32+
/**
33+
* @var string Invalidation event used to purge data when reset_userdata is called.
34+
* @see \cache_helper::purge_by_event()
35+
**/
36+
public const INVALIDATION_RESET_USERDATA = 'resetuserdata';
37+
38+
/** @var ?override_cache The singleton instance for this class. */
39+
private static $instance = null;
3040

3141
/**
32-
* Create override_cache object and link to quiz
42+
* Returns the singleton instance of this class.
3343
*
34-
* @param int $quizid The quiz to link this cache to
44+
* @param definition $definition The cache definition.
45+
* @return override_cache The singleton instance.
3546
*/
36-
public function __construct(
37-
/** @var int $quizid ID of quiz cache is being operated on **/
38-
protected readonly int $quizid
39-
) {
47+
public static function get_instance_for_cache(definition $definition): override_cache {
48+
return self::$instance ??= new override_cache();
4049
}
4150

4251
/**
43-
* Returns the override cache
44-
*
45-
* @return \cache
52+
* {@inheritdoc}
53+
* @see \core_cache\data_source_interface::load_for_cache()
54+
*/
55+
public function load_for_cache($key) {
56+
global $DB;
57+
58+
if ($key === 'lastinvalidation') {
59+
return false;
60+
}
61+
62+
[$quizid, $userid] = self::split_cache_key($key);
63+
64+
$subquery = "SELECT g.id
65+
FROM {groups} g
66+
JOIN {groups_members} gm ON gm.groupid = g.id
67+
JOIN {quiz} q ON q.course = g.courseid
68+
WHERE q.id = :subqueryquizid AND gm.userid = :subqueryuserid";
69+
70+
$sql = "SELECT *
71+
FROM {quiz_overrides}
72+
WHERE quiz = :quizid AND (userid = :userid OR groupid IN ($subquery))";
73+
74+
$records = $DB->get_records_sql($sql, [
75+
'quizid' => $quizid,
76+
'userid' => $userid,
77+
'subqueryquizid' => $quizid,
78+
'subqueryuserid' => $userid,
79+
]);
80+
81+
return empty($records) ? null : $records;
82+
}
83+
84+
/**
85+
* {@inheritdoc}
86+
* @see \core_cache\data_source_interface::load_many_for_cache()
4687
*/
47-
protected function get_cache(): \cache {
48-
return \cache::make('mod_quiz', 'overrides');
88+
public function load_many_for_cache(array $keys) {
89+
$results = [];
90+
foreach ($keys as $key) {
91+
$results[$key] = $this->load_for_cache($key);
92+
}
93+
return $results;
4994
}
5095

5196
/**
52-
* Returns group cache key
97+
* Get all overrides for a given quiz and user.
5398
*
54-
* @param int $groupid
55-
* @return string the group cache key
99+
* @param int $quizid The quiz id.
100+
* @param int $userid The user id.
101+
* @return ?array Array of overrides or null if none found.
56102
*/
57-
protected function get_group_cache_key(int $groupid): string {
58-
return "{$this->quizid}_g_{$groupid}";
103+
public static function get_overrides(int $quizid, int $userid): array|null {
104+
$cache = self::get_cache();
105+
$key = self::get_cache_key($quizid, $userid);
106+
return $cache->get($key);
59107
}
60108

61109
/**
62-
* Returns user cache key
63-
*
64-
* @param int $userid
65-
* @return string the user cache key
110+
* Purge all overrides from the cache.
66111
*/
67-
protected function get_user_cache_key(int $userid): string {
68-
return "{$this->quizid}_u_{$userid}";
112+
public static function purge_all(): void {
113+
self::get_cache()->purge();
69114
}
70115

71116
/**
72-
* Returns the override value in the cache for the given group
117+
* Purge overrides for a specific user in a specific quiz.
73118
*
74-
* @param int $groupid group to get cached override data for
75-
* @return ?\stdClass override value in the cache for the given group, or null if there is none.
119+
* @param int $quizid The quiz id.
120+
* @param int $userid The user id.
76121
*/
77-
public function get_cached_group_override(int $groupid): ?\stdClass {
78-
$raw = $this->get_cache()->get($this->get_group_cache_key($groupid));
79-
return empty($raw) || !is_object($raw) ? null : (object) $raw;
122+
public static function purge_for_user(int $quizid, int $userid): void {
123+
self::purge_for_users($quizid, [$userid]);
80124
}
81125

82126
/**
83-
* Returns the override value in the cache for the given user
127+
* Purge overrides for specific users in a specific quiz.
84128
*
85-
* @param int $userid user to get cached override data for
86-
* @return ?\stdClass the override value in the cache for the given user, or null if there is none.
129+
* @param int $quizid The quiz id.
130+
* @param int[] $userids The user ids.
87131
*/
88-
public function get_cached_user_override(int $userid): ?\stdClass {
89-
$raw = $this->get_cache()->get($this->get_user_cache_key($userid));
90-
return empty($raw) || !is_object($raw) ? null : (object) $raw;
132+
public static function purge_for_users(int $quizid, array $userids): void {
133+
if (empty($userids)) {
134+
return;
135+
}
136+
137+
$keys = array_map(fn($userid): string => self::get_cache_key($quizid, $userid), $userids);
138+
$cache = self::get_cache();
139+
$cache->delete_many($keys);
91140
}
92141

93142
/**
94-
* Deletes the cached override data for a given group
143+
* Get the cache instance.
95144
*
96-
* @param int $groupid group to delete data for
145+
* @return cache The cache instance.
97146
*/
98-
public function clear_for_group(int $groupid): void {
99-
$this->get_cache()->delete($this->get_group_cache_key($groupid));
147+
private static function get_cache(): cache {
148+
return cache::make('mod_quiz', 'overrides');
100149
}
101150

102151
/**
103-
* Deletes the cached override data for the given user
152+
* Generate a cache key for a given quiz and user.
104153
*
105-
* @param int $userid user to delete data for
154+
* @param int $quizid The quiz id.
155+
* @param int $userid The user id.
156+
* @return string The cache key.
106157
*/
107-
public function clear_for_user(int $userid): void {
108-
$this->get_cache()->delete($this->get_user_cache_key($userid));
158+
private static function get_cache_key(int $quizid, int $userid): string {
159+
return "{$quizid}_{$userid}";
109160
}
110161

111162
/**
112-
* Clears the cache for the given user and/or group.
163+
* Split a cache key into its quizid and userid components.
113164
*
114-
* @param ?int $userid user to delete data for, or null.
115-
* @param ?int $groupid group to delete data for, or null.
165+
* @param string $key The cache key.
166+
* @return array An array with quizid and userid.
116167
*/
117-
public function clear_for(?int $userid = null, ?int $groupid = null): void {
118-
if (!empty($userid)) {
119-
$this->clear_for_user($userid);
120-
}
121-
122-
if (!empty($groupid)) {
123-
$this->clear_for_group($groupid);
124-
}
168+
private static function split_cache_key(string $key): array {
169+
return array_map('intval', explode('_', $key));
125170
}
126171
}

0 commit comments

Comments
 (0)