From 4350192a8de9eddc840af4664742aba50ccf9783 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Tue, 24 Nov 2015 19:27:25 +1100 Subject: [PATCH] MDL-52346 caching: Allow localstorage as a cache option. We display a notification on screen during configuration telling the administrator if it's safe to map this store to a local storage cache or not. --- cache/README.md | 5 +++-- cache/classes/definition.php | 24 ++++++++++++++++++++++++ cache/forms.php | 10 ++++++++++ cache/locallib.php | 1 + lang/en/cache.php | 2 ++ lib/db/caches.php | 9 +++++---- 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/cache/README.md b/cache/README.md index 71f44f5d8a0..76761718f7c 100644 --- a/cache/README.md +++ b/cache/README.md @@ -31,6 +31,7 @@ A definition: 'invalidationevents' => array( // Optional 'contextmarkeddirty' ), + 'canuselocalstore' => false // Optional 'sharingoptions' => null // Optional 'defaultsharing' => null // Optional ) @@ -151,7 +152,7 @@ The following optional settings can also be defined: * invalidationevents - An array of events that should trigger this cache to invalidate. * sharingoptions - The sum of the possible sharing options that are applicable to the definition. An advanced setting. * defaultsharing - The default sharing option to use. It's highly recommended that you don't set this unless there is a very specific reason not to use the system default. - +* canuselocalstore - The default is to required a shared cache location for all nodes in a multi webserver environment. If the cache uses revisions and never updates key data, administrators can use a local storage cache for this cache. It's important to note that internally the definition is also aware of the component. This is picked up when the definition is read, based upon the location of the caches.php file. The staticacceleration option. @@ -269,4 +270,4 @@ There are a couple of considerations to using this method: Please be aware that if you are using Memcache or Memcached it is recommended to use dedicated Memcached servers. When caches get purged the memcached servers you have configured get purged, any data stored within them whether it belongs to Moodle or not will be removed. -If you are using Memcached for sessions as well as caching/testing and caches get purged your sessions will be removed prematurely and users will be need to start again. \ No newline at end of file +If you are using Memcached for sessions as well as caching/testing and caches get purged your sessions will be removed prematurely and users will be need to start again. diff --git a/cache/classes/definition.php b/cache/classes/definition.php index a5fd64a6ca8..2d618e4304c 100644 --- a/cache/classes/definition.php +++ b/cache/classes/definition.php @@ -100,6 +100,11 @@ defined('MOODLE_INTERNAL') || die(); * + defaultsharing * [int] The default sharing option to use. It's highly recommended that you don't set this unless there is a very * specific reason not to use the system default. + * + canuselocalstore + * [bool] The cache is able to safely run with multiple copies on different webservers without any need for administrator + * intervention to ensure that data stays in sync across nodes. This is usually managed by a revision + * system as seen in modinfo cache or language cache. Requiring purge on upgrade is not sufficient as + * it requires administrator intervention on each node to make it work. * * For examples take a look at lib/db/caches.php * @@ -308,6 +313,11 @@ class cache_definition { */ protected $sharingoptions; + /** + * Whether this cache supports local storages. + * @var bool + */ + protected $canuselocalstore = false; /** * The selected sharing option. * @var int One of self::SHARING_* @@ -367,6 +377,7 @@ class cache_definition { $sharingoptions = self::SHARING_DEFAULT; $selectedsharingoption = self::SHARING_DEFAULT; $userinputsharingkey = ''; + $canuselocalstore = false; if (array_key_exists('simplekeys', $definition)) { $simplekeys = (bool)$definition['simplekeys']; @@ -453,6 +464,9 @@ class cache_definition { $selectedsharingoption = self::SHARING_ALL; } } + if (array_key_exists('canuselocalstore', $definition)) { + $canuselocalstore = (bool)$definition['canuselocalstore']; + } if (array_key_exists('userinputsharingkey', $definition) && !empty($definition['userinputsharingkey'])) { $userinputsharingkey = (string)$definition['userinputsharingkey']; @@ -529,6 +543,7 @@ class cache_definition { $cachedefinition->sharingoptions = $sharingoptions; $cachedefinition->selectedsharingoption = $selectedsharingoption; $cachedefinition->userinputsharingkey = $userinputsharingkey; + $cachedefinition->canuselocalstore = $canuselocalstore; return $cachedefinition; } @@ -732,6 +747,15 @@ class cache_definition { return $this->requirelockingwrite; } + /** + * Returns true if this definition allows local storage to be used for caching. + * @since Moodle 3.1.0 + * @return bool + */ + public function can_use_localstore() { + return $this->canuselocalstore; + } + /** * Returns true if this definition requires a searchable cache. * @since Moodle 2.4.4 diff --git a/cache/forms.php b/cache/forms.php index 5a2ef3b3cc1..e482702c424 100644 --- a/cache/forms.php +++ b/cache/forms.php @@ -132,6 +132,8 @@ class cache_definition_mappings_form extends moodleform { * The definition of the form */ protected final function definition() { + global $OUTPUT; + $definition = $this->_customdata['definition']; $form = $this->_form; @@ -139,6 +141,14 @@ class cache_definition_mappings_form extends moodleform { list($currentstores, $storeoptions, $defaults) = cache_administration_helper::get_definition_store_options($component, $area); + $storedata = cache_administration_helper::get_definition_summaries(); + if ($storedata[$definition]['mode'] != cache_store::MODE_REQUEST) { + if (isset($storedata[$definition]['canuselocalstore']) && $storedata[$definition]['canuselocalstore']) { + $form->addElement('html', $OUTPUT->notification(get_string('localstorenotification', 'cache'), 'notifymessage')); + } else { + $form->addElement('html', $OUTPUT->notification(get_string('sharedstorenotification', 'cache'), 'notifymessage')); + } + } $form->addElement('hidden', 'definition', $definition); $form->setType('definition', PARAM_SAFEPATH); $form->addElement('hidden', 'action', 'editdefinitionmapping'); diff --git a/cache/locallib.php b/cache/locallib.php index 58dcdd897a6..2e6813e1bfd 100644 --- a/cache/locallib.php +++ b/cache/locallib.php @@ -806,6 +806,7 @@ abstract class cache_administration_helper extends cache_helper { 'component' => $definition->get_component(), 'area' => $definition->get_area(), 'mappings' => $mappings, + 'canuselocalstore' => $definition->can_use_localstore(), 'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false), 'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true), 'userinputsharingkey' => $definition->get_user_input_sharing_key() diff --git a/lang/en/cache.php b/lang/en/cache.php index 3a2c783c28e..11cdf96f1e0 100644 --- a/lang/en/cache.php +++ b/lang/en/cache.php @@ -101,6 +101,7 @@ $string['inadequatestoreformapping'] = 'This store doesn\'t meet the requirement $string['invalidlock'] = 'Invalid lock'; $string['invalidplugin'] = 'Invalid plugin'; $string['invalidstore'] = 'Invalid cache store provided'; +$string['localstorenotification'] = 'This cache can be safely mapped to a store that is local to each webserver'; $string['lockdefault'] = 'Default'; $string['locking'] = 'Locking'; $string['locking_help'] = 'Locking is a mechanism that restricts access to cached data to one process at a time to prevent the data from being overwritten. The locking method determines how the lock is acquired and checked.'; @@ -130,6 +131,7 @@ $string['requestcount'] = 'Test with {$a} requests'; $string['rescandefinitions'] = 'Rescan definitions'; $string['result'] = 'Result'; $string['set'] = 'Set'; +$string['sharedstorenotification'] = 'This cache must be mapped to a store that is shared to all webservers'; $string['sharing'] = 'Sharing'; $string['sharing_all'] = 'Everyone.'; $string['sharing_input'] = 'Custom key (entered below)'; diff --git a/lib/db/caches.php b/lib/db/caches.php index dd8dc960aee..840be97ceb0 100644 --- a/lib/db/caches.php +++ b/lib/db/caches.php @@ -31,22 +31,22 @@ $definitions = array( // Used to store processed lang files. // The keys used are the revision, lang and component of the string file. // The static acceleration size has been based upon student access of the site. - // NOTE: this data may be safely stored in local caches on cluster nodes. 'string' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, - 'staticaccelerationsize' => 30 + 'staticaccelerationsize' => 30, + 'canuselocalstore' => true ), // Used to store cache of all available translations. - // NOTE: this data may be safely stored in local caches on cluster nodes. 'langmenu' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, + 'canuselocalstore' => true ), // Used to store database meta information. @@ -91,9 +91,9 @@ $definitions = array( // This caches the html purifier cleaned text. This is done because the text is usually cleaned once for every user // and context combo. Text caching handles caching for the combination, this cache is responsible for caching the // cleaned text which is shareable. - // NOTE: this data may be safely stored in local caches on cluster nodes. 'htmlpurifier' => array( 'mode' => cache_store::MODE_APPLICATION, + 'canuselocalstore' => true ), // Used to store data from the config + config_plugins table in the database. @@ -206,6 +206,7 @@ $definitions = array( 'coursemodinfo' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, + 'canuselocalstore' => true ), // This is the session user selections cache. // It's a special cache that is used to record user selections that should persist for the lifetime of the session.