MDL-37500 cache: implemented sharing options and gui

This commit is contained in:
Sam Hemelryk 2013-02-07 10:59:52 +13:00
parent 95190fda69
commit 46e17f04c7
10 changed files with 290 additions and 12 deletions

10
cache/README.md vendored
View File

@ -31,6 +31,7 @@ A definition:
'invalidationevents' => array( // Optional
'contextmarkeddirty'
),
'sharingoptions' => null // Optional
)
);
@ -147,11 +148,12 @@ The following optional settings can also be defined:
* ttl - Can be used to set a ttl value for data being set for this cache.
* mappingsonly - This definition can only be used if there is a store mapping for it. More on this later.
* 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.
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 persist option.
As noted the persist option causes the loader generated for this definition to be stored when first created. Subsequent requests for this definition will be given the original loader instance.
The persistent option.
As noted the persistent option causes the loader generated for this definition to be stored when first created. Subsequent requests for this definition will be given the original loader instance.
Data passed to or retrieved from the loader and its chained loaders gets cached by the instance.
This option should be used when you know you will require the loader several times and perhaps in different areas of code.
Because it caches key=>value data it avoids the need to re-fetch things from stores after the first request. Its good for performance, bad for memory.
@ -162,6 +164,10 @@ The administrator of a site can create mappings between stores and definitions.
Setting this option to true means that the definition can only be used if a mapping has been made for it.
Normally if no mappings exist then the default store for the definition mode is used.
Sharing options.
This controls the options available to the user when configuring the sharing of a definitions cached data.
By default all sharing options are available to select. This particular option allows the developer to limit the options available to the admin configuring the cache.
### Data source
Data sources allow cache _misses_ (requests for a key that doesn't exist) to be handled and loaded internally.
The loader gets used as the last resort if provided and means that code using the cache doesn't need to handle the situation that information isn't cached.

28
cache/admin.php vendored
View File

@ -131,6 +131,9 @@ if (!empty($action) && confirm_sesskey()) {
break;
case 'editdefinitionmapping' : // Edit definition mappings.
$definition = required_param('definition', PARAM_SAFEPATH);
if (!array_key_exists($definition, $definitions)) {
throw new cache_exception('Invalid cache definition requested');
}
$title = get_string('editdefinitionmappings', 'cache', $definition);
$mform = new cache_definition_mappings_form($PAGE->url, array('definition' => $definition));
if ($mform->is_cancelled()) {
@ -147,6 +150,31 @@ if (!empty($action) && confirm_sesskey()) {
redirect($PAGE->url);
}
break;
case 'editdefinitionsharing' :
$definition = required_param('definition', PARAM_SAFEPATH);
if (!array_key_exists($definition, $definitions)) {
throw new cache_exception('Invalid cache definition requested');
}
$title = get_string('editdefinitionmappings', 'cache', $definition);
$sharingoptions = $definitions[$definition]['sharingoptions'];
$mform = new cache_definition_sharing_form($PAGE->url, array('definition' => $definition, 'sharingoptions' => $sharingoptions));
$mform->set_data(array(
'sharing' => $definitions[$definition]['selectedsharingoption'],
'userinputsharingkey' => $definitions[$definition]['userinputsharingkey']
));
if ($mform->is_cancelled()) {
redirect($PAGE->url);
} else if ($data = $mform->get_data()) {
$component = $definitions[$definition]['component'];
$area = $definitions[$definition]['area'];
cache_helper::purge_by_definition($component, $area);
$writer = cache_config_writer::instance();
$sharing = array_sum(array_keys($data->sharing));
$userinputsharingkey = $data->userinputsharingkey;
$writer->set_definition_sharing($definition, $sharing, $userinputsharingkey);
redirect($PAGE->url);
}
break;
case 'editmodemappings': // Edit default mode mappings.
$mform = new cache_mode_mappings_form(null, $stores);
$mform->set_data(array(

View File

@ -231,6 +231,21 @@ class cache_config {
// Invalid cache mode used for the definition.
continue;
}
// Default the sharing option as it was added for 2.5.
// This can be removed sometime after the release of 2.6.
if (!array_key_exists('sharingoptions', $conf)) {
$conf['sharingoptions'] = cache_definition::SHARING_DEFAULTOPTIONS;
}
// Default the selected sharing option as it was added for 2.5.
// This can be removed sometime after the release of 2.6.
if (!array_key_exists('selectedsharingoption', $conf)) {
$conf['selectedsharingoption'] = cache_definition::SHARING_DEFAULT;
}
// Default the user input sharing key as it was added for 2.5.
// This can be removed sometime after the release of 2.6.
if (!array_key_exists('userinputsharingkey', $conf)) {
$conf['userinputsharingkey'] = '';
}
$this->configdefinitions[$id] = $conf;
}

View File

@ -100,6 +100,8 @@ defined('MOODLE_INTERNAL') || die();
* reason or another.
* + invalidationevents
* [array] An array of events that should cause this cache to invalidate some or all of the items within it.
* + sharingoptions
* [int] The sharing options that are appropriate for this definition. Should be the sum of the possible options.
*
* For examples take a look at lib/db/caches.php
*
@ -110,6 +112,26 @@ defined('MOODLE_INTERNAL') || die();
*/
class cache_definition {
/** The cache can be shared with everyone */
const SHARING_ALL = 1;
/** The cache can be shared with other sites using the same siteid. */
const SHARING_SITEID = 2;
/** The cache can be shared with other sites of the same version. */
const SHARING_VERSION = 4;
/** The cache can be shared with other sites using the same key */
const SHARING_INPUT = 8;
/**
* The default sharing options available.
* All + SiteID + Version + Input.
*/
const SHARING_DEFAULTOPTIONS = 15;
/**
* The default sharing option that gets used if none have been selected.
* SiteID. It is the most restrictive.
*/
const SHARING_DEFAULT = 2;
/**
* The identifier for the definition
* @var string
@ -281,6 +303,24 @@ class cache_definition {
*/
protected $definitionhash = null;
/**
* The selected sharing mode for this definition.
* @var int
*/
protected $sharingoptions;
/**
* The selected sharing option.
* @var int One of self::SHARING_*
*/
protected $selectedsharingoption = self::SHARING_DEFAULT;
/**
* The user input key to use if the SHARING_INPUT option has been selected.
* @var string Must be ALPHANUMEXT
*/
protected $userinputsharingkey = '';
/**
* Creates a cache definition given a definition from the cache configuration or from a caches.php file.
*
@ -325,6 +365,9 @@ class cache_definition {
$ttl = 0;
$mappingsonly = false;
$invalidationevents = array();
$sharingoptions = self::SHARING_DEFAULT;
$selectedsharingoption = self::SHARING_DEFAULT;
$userinputsharingkey = '';
if (array_key_exists('simplekeys', $definition)) {
$simplekeys = (bool)$definition['simplekeys'];
@ -387,6 +430,26 @@ class cache_definition {
if (array_key_exists('invalidationevents', $definition)) {
$invalidationevents = (array)$definition['invalidationevents'];
}
if (array_key_exists('sharingoptions', $definition)) {
$sharingoptions = (int)$definition['sharingoptions'];
}
if (array_key_exists('selectedsharingoption', $definition)) {
$selectedsharingoption = (int)$definition['selectedsharingoption'];
} else if (array_key_exists('defaultsharing', $definition)) {
$selectedsharingoption = (int)$definition['defaultsharing'];
} else if ($sharingoptions ^ $selectedsharingoption) {
if ($sharingoptions & self::SHARING_SITEID) {
$selectedsharingoption = self::SHARING_SITEID;
} else if ($sharingoptions & self::SHARING_VERSION) {
$selectedsharingoption = self::SHARING_VERSION;
} else {
$selectedsharingoption = self::SHARING_ALL;
}
}
if (array_key_exists('userinputsharingkey', $definition) && !empty($definition['userinputsharingkey'])) {
$userinputsharingkey = (string)$definition['userinputsharingkey'];
}
if (!is_null($overrideclass)) {
if (!is_null($overrideclassfile)) {
@ -457,6 +520,9 @@ class cache_definition {
$cachedefinition->ttl = $ttl;
$cachedefinition->mappingsonly = $mappingsonly;
$cachedefinition->invalidationevents = $invalidationevents;
$cachedefinition->sharingoptions = $sharingoptions;
$cachedefinition->selectedsharingoption = $selectedsharingoption;
$cachedefinition->userinputsharingkey = $userinputsharingkey;
return $cachedefinition;
}
@ -496,6 +562,9 @@ class cache_definition {
if (!empty($options['overrideclass'])) {
$definition['overrideclass'] = $options['overrideclass'];
}
if (!empty($options['sharingoptions'])) {
$definition['sharingoptions'] = $options['sharingoptions'];
}
return self::load($id, $definition, null);
}
@ -819,6 +888,20 @@ class cache_definition {
* @return string A string to be used as part of keys.
*/
protected function get_cache_identifier() {
return cache_helper::get_site_identifier();
$identifiers = array();
if ($this->selectedsharingoption & self::SHARING_ALL) {
// Nothing to do here.
} else {
if ($this->selectedsharingoption & self::SHARING_SITEID) {
$identifiers[] = cache_helper::get_site_identifier();
}
if ($this->selectedsharingoption & self::SHARING_VERSION) {
$identifiers[] = cache_helper::get_site_version();
}
if ($this->selectedsharingoption & self::SHARING_INPUT && !empty($this->userinputsharingkey)) {
$identifiers[] = $this->userinputsharingkey;
}
}
return join('/', $identifiers);
}
}

49
cache/forms.php vendored
View File

@ -180,6 +180,55 @@ class cache_definition_mappings_form extends moodleform {
}
}
/**
* Form to set definition sharing option
*
* @package core
* @category cache
* @copyright 2013 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cache_definition_sharing_form extends moodleform {
/**
* The definition of the form
*/
protected final function definition() {
$definition = $this->_customdata['definition'];
$sharingoptions = $this->_customdata['sharingoptions'];
$form = $this->_form;
$form->addElement('hidden', 'definition', $definition);
$form->setType('sharing', PARAM_SAFEPATH);
$form->addElement('hidden', 'action', 'editdefinitionsharing');
$form->setType('sharing', PARAM_ALPHA);
$count = 0;
foreach ($sharingoptions as $value => $text) {
$count++;
$name = ($count == 1) ? get_string('sharing', 'cache') : null;
$form->addElement('checkbox', 'sharing['.$value.']', $name, $text);
}
$form->setType('sharing', PARAM_INT);
$form->setDefault('sharing', cache_administration_helper::get_definition_sharing_options(cache_definition::SHARING_DEFAULT));
$form->addElement('text', 'userinputsharingkey', get_string('userinputsharingkey', 'cache'));
$form->addHelpButton('userinputsharingkey', 'userinputsharingkey', 'cache');
$form->disabledIf('userinputsharingkey', 'sharing['.cache_definition::SHARING_INPUT.']', 'notchecked');
$form->setType('userinputsharingkey', PARAM_ALPHANUMEXT);
$values = array_keys($sharingoptions);
if (in_array(cache_definition::SHARING_ALL, $values)) {
// If you share with all thenthe other options don't really make sense.
foreach ($values as $value) {
$form->disabledIf('sharing['.$value.']', 'sharing['.cache_definition::SHARING_ALL.']', 'checked');
}
$form->disabledIf('userinputsharingkey', 'sharing['.cache_definition::SHARING_ALL.']', 'checked');
}
$this->add_action_buttons();
}
}
/**
* Form to set the mappings for a mode.
*

74
cache/locallib.php vendored
View File

@ -551,6 +551,20 @@ class cache_config_writer extends cache_config {
* @param array $definitions
*/
private function write_definitions_to_cache(array $definitions) {
// Preserve the selected sharing option when updating the definitions.
// This is set by the user and should never come from caches.php.
foreach ($definitions as $key => $definition) {
unset($definitions[$key]['selectedsharingoption']);
unset($definitions[$key]['userinputsharingkey']);
if (isset($this->configdefinitions[$key]) && isset($this->configdefinitions[$key]['selectedsharingoption'])) {
$definitions[$key]['selectedsharingoption'] = $this->configdefinitions[$key]['selectedsharingoption'];
}
if (isset($this->configdefinitions[$key]) && isset($this->configdefinitions[$key]['userinputsharingkey'])) {
$definitions[$key]['userinputsharingkey'] = $this->configdefinitions[$key]['userinputsharingkey'];
}
}
$this->configdefinitions = $definitions;
foreach ($this->configdefinitionmappings as $key => $mapping) {
if (!array_key_exists($mapping['definition'], $definitions)) {
@ -619,6 +633,29 @@ class cache_config_writer extends cache_config {
$this->config_save();
return $this->siteidentifier;
}
/**
* Sets the selected sharing options and key for a definition.
*
* @param string $definition The name of the definition to set for.
* @param int $sharingoption The sharing option to set.
* @param string|null $userinputsharingkey The user input key or null.
* @throws coding_exception
*/
public function set_definition_sharing($definition, $sharingoption, $userinputsharingkey = null) {
if (!array_key_exists($definition, $this->configdefinitions)) {
throw new coding_exception('Invalid definition name passed when updating sharing options.');
}
if (!($this->configdefinitions[$definition]['sharingoptions'] & $sharingoption)) {
throw new coding_exception('Invalid sharing option passed when updating definition.');
}
$this->configdefinitions[$definition]['selectedsharingoption'] = (int)$sharingoption;
if (!empty($userinputsharingkey)) {
$this->configdefinitions[$definition]['userinputsharingkey'] = (string)$userinputsharingkey;
}
$this->config_save();
}
}
/**
@ -790,12 +827,40 @@ abstract class cache_administration_helper extends cache_helper {
'mode' => $definition['mode'],
'component' => $definition['component'],
'area' => $definition['area'],
'mappings' => $mappings
'mappings' => $mappings,
'sharingoptions' => self::get_definition_sharing_options($definition['sharingoptions'], false),
'selectedsharingoption' => self::get_definition_sharing_options($definition['selectedsharingoption'], true),
'userinputsharingkey' => $definition['userinputsharingkey']
);
}
return $return;
}
/**
* Given a sharing option hash this function returns an array of strings that can be used to describe it.
*
* @param int $sharingoption The sharing option hash to get strings for.
* @param bool $isselectedoptions Set to true if the strings will be used to view the selected options.
* @return array An array of lang_string's.
*/
public static function get_definition_sharing_options($sharingoption, $isselectedoptions = true) {
$options = array();
$prefix = ($isselectedoptions) ? 'sharingselected' : 'sharing';
if ($sharingoption & cache_definition::SHARING_ALL) {
$options[cache_definition::SHARING_ALL] = new lang_string($prefix.'_all', 'cache');
}
if ($sharingoption & cache_definition::SHARING_SITEID) {
$options[cache_definition::SHARING_SITEID] = new lang_string($prefix.'_siteid', 'cache');
}
if ($sharingoption & cache_definition::SHARING_VERSION) {
$options[cache_definition::SHARING_VERSION] = new lang_string($prefix.'_version', 'cache');
}
if ($sharingoption & cache_definition::SHARING_INPUT) {
$options[cache_definition::SHARING_INPUT] = new lang_string($prefix.'_input', 'cache');
}
return $options;
}
/**
* Returns all of the actions that can be performed on a definition.
* @param context $context
@ -804,10 +869,17 @@ abstract class cache_administration_helper extends cache_helper {
public static function get_definition_actions(context $context) {
if (has_capability('moodle/site:config', $context)) {
return array(
// Edit mappings.
array(
'text' => get_string('editmappings', 'cache'),
'url' => new moodle_url('/cache/admin.php', array('action' => 'editdefinitionmapping', 'sesskey' => sesskey()))
),
// Edit sharing.
array(
'text' => get_string('editsharing', 'cache'),
'url' => new moodle_url('/cache/admin.php', array('action' => 'editdefinitionsharing', 'sesskey' => sesskey()))
),
// Purge.
array(
'text' => get_string('purge', 'cache'),
'url' => new moodle_url('/cache/admin.php', array('action' => 'purgedefinition', 'sesskey' => sesskey()))

3
cache/renderer.php vendored
View File

@ -222,6 +222,7 @@ class core_cache_renderer extends plugin_renderer_base {
get_string('component', 'cache'),
get_string('area', 'cache'),
get_string('mappings', 'cache'),
get_string('sharing', 'cache'),
get_string('actions', 'cache'),
);
$table->colclasses = array(
@ -230,6 +231,7 @@ class core_cache_renderer extends plugin_renderer_base {
'component',
'area',
'mappings',
'sharing',
'actions'
);
$table->data = array();
@ -253,6 +255,7 @@ class core_cache_renderer extends plugin_renderer_base {
$definition['component'],
$definition['area'],
$mapping,
join(', ', $definition['selectedsharingoption']),
join(', ', $htmlactions)
));
$row->attributes['class'] = 'definition-'.$definition['component'].'-'.$definition['area'];

View File

@ -58,6 +58,14 @@ class cache_config_phpunittest extends cache_config_writer {
$this->configdefinitions[$area] = $properties;
}
/**
* Removes a definition.
* @param string $name
*/
public function phpunit_remove_definition($name) {
unset($this->configdefinitions[$name]);
}
/**
* Removes the configured stores so that there are none available.
*/

View File

@ -154,15 +154,16 @@ class cache_config_writer_phpunit_tests extends advanced_testcase {
*/
public function test_update_definitions() {
$config = cache_config_writer::instance();
$earlydefinitions = $config->get_definitions();
unset($config);
cache_factory::reset();
// Remove the definition.
$config->phpunit_remove_definition('core/string');
$definitions = $config->get_definitions();
// Check it is gone.
$this->assertFalse(array_key_exists('core/string', $definitions));
// Update definitions. This should re-add it.
cache_config_writer::update_definitions();
$config = cache_config_writer::instance();
$latedefinitions = $config->get_definitions();
$this->assertSame($latedefinitions, $earlydefinitions);
$definitions = $config->get_definitions();
// Check it is back again.
$this->assertTrue(array_key_exists('core/string', $definitions));
}
/**

View File

@ -81,6 +81,7 @@ $string['deletestoreconfirmation'] = 'Are you sure you want to delete the "{$a}"
$string['deletestorehasmappings'] = 'You cannot delete this store because it has mappings. Please delete all mappings before deleting the store';
$string['deletestoresuccess'] = 'Successfully deleted the cache store';
$string['editmappings'] = 'Edit mappings';
$string['editsharing'] = 'Edit sharing';
$string['editstore'] = 'Edit store';
$string['editstoresuccess'] = 'Succesfully edited the cache store.';
$string['editdefinitionmappings'] = '{$a} definition store mappings';
@ -123,6 +124,16 @@ $string['requestcount'] = 'Test with {$a} requests';
$string['rescandefinitions'] = 'Rescan definitions';
$string['result'] = 'Result';
$string['set'] = 'Set';
$string['sharing'] = 'Sharing';
$string['sharing_all'] = 'Everyone.';
$string['sharing_input'] = 'Custom key (entered below)';
$string['sharing_help'] = 'This allows you to determine how the cache data can be shared if you have a clustered setup, or if you have multiple sites all set up with the same store and wish to share the data. This is an advanced setting please make sure you understand its purpose before changing it.';
$string['sharing_siteid'] = 'Sites with the same site id.';
$string['sharing_version'] = 'Sites running the same version.';
$string['sharingselected_all'] = 'Everyone';
$string['sharingselected_input'] = 'Custom key';
$string['sharingselected_siteid'] = 'Site identifier';
$string['sharingselected_version'] = 'Version';
$string['storeconfiguration'] = 'Store configuration';
$string['storename'] = 'Store name';
$string['storename_help'] = 'This sets the store name. It is used to identify the store within the system and can only consist of a-z A-Z 0-9 -_ and spaces. It also must be unique. If you attempt to use a name that has already been used you will receive an error.';
@ -152,3 +163,5 @@ $string['tested'] = 'Tested';
$string['testperformance'] = 'Test performance';
$string['unsupportedmode'] = 'Unsupported mode';
$string['untestable'] = 'Untestable';
$string['userinputsharingkey'] = 'Custom key for sharing';
$string['userinputsharingkey_help'] = 'Enter your own private key here. When you set up other stores on other sites you wish to share data with make sure you set the exact same key there.';