MUC
Moodle Universal Cache
Tim Hunt
Introduction
What is a cache
A place to keep data that would take time to re-compute
A cache stores values identified by a key
E.g. list of activities in a course, stored by course id
Caches may get purged at any time
Cannot 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 stores
Disc, Memcache, REDIS, …
cachestore_ is a plugin type
Admins should control which data goes in which store
Cache performance
Typical 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 cache
In 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 cache
Application – 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 in
E.g. list of all course categories this user can access
Request – Data within the handling of one request
E.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: implementation
public 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 administration
E.g. https://learn2acct.open.ac.uk/cache/admin.php
Cache administration: continued
Cache administration:
continued
Cache administration: continued
Issues
Memcache purging
MUC lets you store many different caches in one backend
cache::purge(); should just clear one of those caches
With Memcache, cache::purge() wipes all caches
This is one of the reasons the VLE crashed in October
Moodle 3.2 now has REDIS cache store in core
We should consider switching
Complex keys
MUC 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 features
Caching is about performance
Simple is good
Automated tests
MUC caches are automatically cleared between each unit
test or Behat test
Useful links
https://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)

MUC - Moodle Universal Cache

  • 1.
  • 2.
  • 3.
    What is acache A place to keep data that would take time to re-compute A cache stores values identified by a key E.g. list of activities in a course, stored by course id Caches may get purged at any time Cannot store real data there, just derived values for performance
  • 4.
    Why have acache system? Developers want to do stuff with data, not re-implement caching Want to be able to use different back-end stores Disc, Memcache, REDIS, … cachestore_ is a plugin type Admins should control which data goes in which store
  • 5.
    Cache performance Typical callto 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
  • 6.
  • 7.
    Typical use $cache =cache::make('core', 'string'); $strings = $cache->get($component); if ($strings === false) { $strings = load_strings($component); $cache->set($component, $strings); }
  • 8.
    Defining your cache Indb/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';
  • 9.
    Types of cache Application– 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 in E.g. list of all course categories this user can access Request – Data within the handling of one request E.g. list of temporary tables
  • 10.
    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
  • 11.
    Bulk actions $cache->get_many(['key1', 'key2']); //['key1' => 'value1', 'key2' => 'value2'] $cache->set_many( ['key1' => 'value1', 'key2' => 'value2']); $cache->delete_many(['key1', 'key2']);
  • 12.
  • 13.
    Data source: why? Avoidsthe 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
  • 14.
    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', ), )
  • 15.
    class question_finder implementscache_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);
  • 16.
    Data source: implementation publicfunction 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); } } } // ... }
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
    Memcache purging MUC letsyou store many different caches in one backend cache::purge(); should just clear one of those caches With Memcache, cache::purge() wipes all caches This is one of the reasons the VLE crashed in October Moodle 3.2 now has REDIS cache store in core We should consider switching
  • 24.
    Complex keys MUC triesto 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 features Caching is about performance Simple is good
  • 25.
    Automated tests MUC cachesare automatically cleared between each unit test or Behat test
  • 26.