MUC Moodle Universal Cache Tim Hunt
MUCMoodle Universal Cache
Tim Hunt
Introduction
What is a cacheA place to keep data that would take time to re-compute
A cache stores values identified by a keyE.g. list of activities in a course, stored by course id
Caches may get purged at any timeCannot store real data there, just derived values for performance
Why have a cache system?Developers want to do stuff with data, not re-implement caching
Want to be able to use different back-end storesDisc, Memcache, REDIS, …cachestore_ is a plugin type
Admins should control which data goes in which store
Cache performanceTypical call to cache::get For a typical page• 0.5 ms data fetched from Memcache
150• 0.05 ms data in static acceleration
700
For comparison: typical DB query• 3 ms
40
Using caches
Typical use$cache = cache::make('core', 'string');$strings = $cache->get($component);
if ($strings === false) { $strings = load_strings($component); $cache->set($component, $strings);}
Defining your cacheIn db/caches.php in your plugin. E.g. from lib/db/caches.php$definitions = array( // Used to store processed lang files. // Keys used are revision, lang and component of the string file. // Static acceleration size is based on student access of the site. 'string' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, // Metadata helps cache 'staticaccelerationsize' => 30, // system handle the data 'canuselocalstore' => true, // efficiently. ),);
$string['cachedef_string'] = 'Language string cache';
Types of cacheApplication – Data that applies throughout Moodle
E.g. list of activities in each course / language strings
Session – Data that is relevant while a user is logged inE.g. list of all course categories this user can access
Request – Data within the handling of one requestE.g. list of temporary tables
Clearing the cache
$cache = cache::make('core', 'questiondata');$cache->delete($questionid);
cache::make('core', 'questiondata')->purge();
Recommended: just clear out what has changed
Dangerous
Also not recommended: time-to-live in cache definition
Bulk actions$cache->get_many(['key1', 'key2']);// ['key1' => 'value1', 'key2' => 'value2']
$cache->set_many( ['key1' => 'value1', 'key2' => 'value2']);
$cache->delete_many(['key1', 'key2']);
Advanced use: data source
Data source: why?Avoids the normal
“is this in the cache? Yes: good; No, re-compute”
Tell the cache where to get the data if it is not yet stored
Data source: definition$definitions = array( // Cache for question definitions. This is used by the question // bank class. Users probably do not need to know about this cache. // They will just call question_bank::load_question. 'questiondata' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, // The id of the question is used. 'requiredataguarantee' => false, 'datasource' => 'question_finder', 'datasourcefile' => 'question/engine/bank.php', ),)
class question_finder implements cache_data_source { public static function get_instance_for_cache(cache_definition $definition) { return self::get_instance(); // Singleton pattern. } public function load_for_cache($questionid) { global $DB; $questiondata = $DB->get_record_sql('SELECT q.*, qc.contextid FROM {question} q JOIN {question_categories} qc ON q.category = qc.id WHERE q.id = :id', array('id' => $questionid), MUST_EXIST); get_question_options($questiondata); return $questiondata; }}
Data source: use$cache = cache::make('core', 'questiondata');$cache->get($questionid);
Data source: implementationpublic function get($key, $strictness = IGNORE_MISSING) { // ... $result = $this->store->get($key); // ...
if ($result === false) { if ($this->loader !== false) { $result = $this->loader->get($key); } else { if ($this->datasource !== false) { $result = $this->datasource->load_for_cache($key); } } }
// ...}
Cache administration
Cache administrationE.g. https://learn2acct.open.ac.uk/cache/admin.php
Cache administration: continued
Cache administration:continued
Cache administration: continued
Issues
Memcache purgingMUC lets you store many different caches in one backend
cache::purge(); should just clear one of those caches
With Memcache, cache::purge() wipes all cachesThis is one of the reasons the VLE crashed in October
Moodle 3.2 now has REDIS cache store in coreWe should consider switching
Complex keysMUC tries to support keys that are not just ints or strings
However this hurts performance – best not to use it
This probably applies to other advanced MUC featuresCaching is about performanceSimple is good
Automated testsMUC caches are automatically cleared between each unit test or Behat test
Useful linkshttps://docs.moodle.org/dev/Cache_API
https://docs.moodle.org/dev/Cache_API_-_Quick_reference
cache/README.md in the code
https://docs.moodle.org/dev/The_Moodle_Universal_Cache_(MUC)