Merge branch 'MDL-79628-move-navcache-to-muc-feature-MOODLE_404_STABLE' of https://github.com/rajandangi/moodle into MOODLE_404_STABLE

This commit is contained in:
Jun Pataleta 2024-09-19 11:09:07 +08:00
commit d73ad2127e
No known key found for this signature in database
GPG Key ID: F83510526D99E2C7
3 changed files with 65 additions and 91 deletions

View File

@ -78,6 +78,7 @@ $string['cachedef_moodlenet_usercanshare'] = 'Users can share resources to Moodl
$string['cachedef_locking'] = 'Locking'; $string['cachedef_locking'] = 'Locking';
$string['cachedef_message_processors_enabled'] = "Message processors enabled status"; $string['cachedef_message_processors_enabled'] = "Message processors enabled status";
$string['cachedef_contextwithinsights'] = 'Context with insights'; $string['cachedef_contextwithinsights'] = 'Context with insights';
$string['cachedef_navigation_cache'] = 'Navigation cache';
$string['cachedef_navigation_expandcourse'] = 'Navigation expandable courses'; $string['cachedef_navigation_expandcourse'] = 'Navigation expandable courses';
$string['cachedef_observers'] = 'Event observers'; $string['cachedef_observers'] = 'Event observers';
$string['cachedef_plugin_functions'] = 'Plugins available callbacks'; $string['cachedef_plugin_functions'] = 'Plugins available callbacks';

View File

@ -618,4 +618,11 @@ $definitions = array(
'simpledata' => true, 'simpledata' => true,
'staticacceleration' => true, 'staticacceleration' => true,
], ],
// The navigation_cache class used this cache to store the navigation nodes.
'navigation_cache' => [
'mode' => cache_store::MODE_SESSION,
'simplekeys' => true,
'simpledata' => true,
'ttl' => 1800,
],
); );

View File

@ -5997,10 +5997,9 @@ class navigation_json {
} }
/** /**
* The cache class used by global navigation and settings navigation. * The navigation_cache class is used for global and settings navigation data.
* *
* It is basically an easy access point to session with a bit of smarts to make * It provides an easy access to the session cache with TTL of 1800 seconds.
* sure that the information that is cached is valid still.
* *
* Example use: * Example use:
* <code php> * <code php>
@ -6019,13 +6018,14 @@ class navigation_json {
class navigation_cache { class navigation_cache {
/** @var int represents the time created */ /** @var int represents the time created */
protected $creation; protected $creation;
/** @var array An array of session keys */ /** @var cache_session The session cache instance */
protected $session; protected $cache;
/** @var array The current cache area data */
protected $session = [];
/** /**
* The string to use to segregate this particular cache. It can either be * @var string A unique string to segregate this particular cache.
* unique to start a fresh cache or if you want to share a cache then make * It can either be unique to start a fresh cache or shared to use an existing cache.
* it the string used in the original cache.
* @var string
*/ */
protected $area; protected $area;
/** @var int a time that the information will time out */ /** @var int a time that the information will time out */
@ -6038,152 +6038,121 @@ class navigation_cache {
const CACHEUSERID = 1; const CACHEUSERID = 1;
/** @var int cache value */ /** @var int cache value */
const CACHEVALUE = 2; const CACHEVALUE = 2;
/** @var null|array An array of navigation cache areas to expire on shutdown */ /** @var null|array An array of cache areas to expire on shutdown */
public static $volatilecaches; public static $volatilecaches = null;
/** /**
* Contructor for the cache. Requires two arguments * Contructor for the cache. Requires a area string be passed in.
* *
* @param string $area The string to use to segregate this particular cache * @param string $area The unique string to segregate this particular cache.
* it can either be unique to start a fresh cache or if you want
* to share a cache then make it the string used in the original
* cache
* @param int $timeout The number of seconds to time the information out after * @param int $timeout The number of seconds to time the information out after
*/ */
public function __construct($area, $timeout=1800) { public function __construct($area, $timeout=1800) {
global $USER;
$this->creation = time(); $this->creation = time();
$this->area = $area; $this->area = "user_{$USER->id}_{$area}";
$this->timeout = time() - $timeout; $this->timeout = time() - $timeout;
if (rand(0,100) === 0) { $this->cache = cache::make('core', 'navigation_cache');
$this->garbage_collection();
}
} }
/** /**
* Used to set up the cache within the SESSION. * Ensure the navigation cache is initialised
* *
* This is called for each access and ensure that we don't put anything into the session before * This is called for each access and ensures that no data is put into the cache before it is required.
* it is required.
*/ */
protected function ensure_session_cache_initialised() { protected function ensure_navigation_cache_initialised() {
global $SESSION;
if (empty($this->session)) { if (empty($this->session)) {
if (!isset($SESSION->navcache)) { $this->session = $this->cache->get($this->area);
$SESSION->navcache = new stdClass; if (!is_array($this->session)) {
$this->session = [];
} }
if (!isset($SESSION->navcache->{$this->area})) {
$SESSION->navcache->{$this->area} = array();
}
$this->session = &$SESSION->navcache->{$this->area}; // pointer to array, =& is correct here
} }
} }
/** /**
* Magic Method to retrieve something by simply calling using = cache->key * Magic Method to retrieve a cached item by simply calling using = cache->key
* *
* @param mixed $key The identifier for the information you want out again * @param mixed $key The identifier for the cached information
* @return void|mixed Either void or what ever was put in * @return mixed|void The cached information or void if not found
*/ */
public function __get($key) { public function __get($key) {
if (!$this->cached($key)) { if (!$this->cached($key)) {
return; return;
} }
$information = $this->session[$key][self::CACHEVALUE]; return unserialize($this->session[$key][self::CACHEVALUE]);
return unserialize($information);
} }
/** /**
* Magic method that simply uses {@link set();} to store something in the cache * Magic method that simply uses {@see navigation_cache::set()} to store an item in the cache
* *
* @param string|int $key * @param string|int $key The key to store the information against
* @param mixed $information * @param mixed $information The information to cache
*/ */
public function __set($key, $information) { public function __set($key, $information) {
$this->set($key, $information); $this->set($key, $information);
} }
/** /**
* Sets some information against the cache (session) for later retrieval * Sets some information in the session cache for later retrieval
* *
* @param string|int $key * @param string|int $key
* @param mixed $information * @param mixed $information
*/ */
public function set($key, $information) { public function set($key, $information) {
global $USER; global $USER;
$this->ensure_session_cache_initialised(); $this->ensure_navigation_cache_initialised();
$information = serialize($information); $information = serialize($information);
$this->session[$key]= array(self::CACHETIME=>time(), self::CACHEUSERID=>$USER->id, self::CACHEVALUE=>$information); $this->session[$key] = [self::CACHETIME => time(), self::CACHEUSERID => $USER->id, self::CACHEVALUE => $information];
$this->cache->set($this->area, $this->session);
} }
/** /**
* Check the existence of the identifier in the cache * Check the existence of the identifier in the cache
* *
* @param string|int $key * @param string|int $key The identifier to check
* @return bool * @return bool True if the item exists in the cache, false otherwise
*/ */
public function cached($key) { public function cached($key) {
global $USER; $this->ensure_navigation_cache_initialised();
$this->ensure_session_cache_initialised(); return isset($this->session[$key]) &&
if (!array_key_exists($key, $this->session) || !is_array($this->session[$key]) || $this->session[$key][self::CACHEUSERID]!=$USER->id || $this->session[$key][self::CACHETIME] < $this->timeout) { is_array($this->session[$key]);
return false;
}
return true;
} }
/** /**
* Compare something to it's equivilant in the cache * Compare something to it's equivilant in the cache
* *
* @param string $key * @param string $key The key to check
* @param mixed $value * @param mixed $value The value to compare
* @param bool $serialise Whether to serialise the value before comparison * @param bool $serialise Whether to serialise the value before comparison
* this should only be set to false if the value is already * this should only be set to false if the value is already
* serialised * serialised
* @return bool If the value is the same false if it is not set or doesn't match * @return bool True if the value is the same as the cached one, false otherwise
*/ */
public function compare($key, $value, $serialise = true) { public function compare($key, $value, $serialise = true) {
if ($this->cached($key)) { if ($this->cached($key)) {
if ($serialise) { if ($serialise) {
$value = serialize($value); $value = serialize($value);
} }
if ($this->session[$key][self::CACHEVALUE] === $value) { return $this->session[$key][self::CACHEVALUE] === $value;
return true;
}
} }
return false; return false;
} }
/** /**
* Wipes the entire cache, good to force regeneration * Deletes the entire cache area, forcing a fresh cache to be created
*/ */
public function clear() { public function clear() {
global $SESSION; $this->cache->delete($this->area);
unset($SESSION->navcache); $this->session = [];
$this->session = null;
} }
/** /**
* Checks all cache entries and removes any that have expired, good ole cleanup * Marks the cache as volatile (likely to change)
*/
protected function garbage_collection() {
if (empty($this->session)) {
return true;
}
foreach ($this->session as $key=>$cachedinfo) {
if (is_array($cachedinfo) && $cachedinfo[self::CACHETIME]<$this->timeout) {
unset($this->session[$key]);
}
}
}
/**
* Marks the cache as being volatile (likely to change)
* *
* Any caches marked as volatile will be destroyed at the on shutdown by * Any caches marked as volatile will be destroyed on shutdown by {@see navigation_node::destroy_volatile_caches()}
* {@link navigation_node::destroy_volatile_caches()} which is registered
* as a shutdown function if any caches are marked as volatile.
* *
* @param bool $setting True to destroy the cache false not too * @param bool $setting True to mark the cache as volatile, false to remove the volatile flag
*/ */
public function volatile($setting = true) { public function volatile($setting = true) {
if (self::$volatilecaches===null) { if (self::$volatilecaches === null) {
self::$volatilecaches = array(); self::$volatilecaches = [];
core_shutdown_manager::register_function(array('navigation_cache','destroy_volatile_caches')); core_shutdown_manager::register_function(['navigation_cache', 'destroy_volatile_caches']);
} }
if ($setting) { if ($setting) {
@ -6196,19 +6165,16 @@ class navigation_cache {
/** /**
* Destroys all caches marked as volatile * Destroys all caches marked as volatile
* *
* This function is static and works in conjunction with the static volatilecaches * This function is static and works with the static volatilecaches property of navigation cache.
* property of navigation cache. * It manually resets the cached areas back to an empty array.
* Because this function is static it manually resets the cached areas back to an
* empty array.
*/ */
public static function destroy_volatile_caches() { public static function destroy_volatile_caches() {
global $SESSION; if (is_array(self::$volatilecaches) && count(self::$volatilecaches) > 0) {
if (is_array(self::$volatilecaches) && count(self::$volatilecaches)>0) { $cache = cache::make('core', 'navigation_cache');
foreach (self::$volatilecaches as $area) { foreach (self::$volatilecaches as $area) {
$SESSION->navcache->{$area} = array(); $cache->delete($area);
} }
} else { self::$volatilecaches = null;
$SESSION->navcache = new stdClass;
} }
} }
} }