Skip to content

Commit

Permalink
Merge branch 'MDL-72619-master' of https://github.com/sammarshallou/m…
Browse files Browse the repository at this point in the history
  • Loading branch information
stronk7 committed Nov 3, 2021
2 parents faddd24 + 1b94bb8 commit c110965
Show file tree
Hide file tree
Showing 16 changed files with 769 additions and 25 deletions.
2 changes: 2 additions & 0 deletions admin/settings/plugins.php
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,8 @@
$ADMIN->add('modules', new admin_category('cache', new lang_string('caching', 'cache')));
$ADMIN->add('cache', new admin_externalpage('cacheconfig', new lang_string('cacheconfig', 'cache'), $CFG->wwwroot .'/cache/admin.php'));
$ADMIN->add('cache', new admin_externalpage('cachetestperformance', new lang_string('testperformance', 'cache'), $CFG->wwwroot . '/cache/testperformance.php'));
$ADMIN->add('cache', new admin_externalpage('cacheusage',
new lang_string('cacheusage', 'cache'), $CFG->wwwroot . '/cache/usage.php'));
$ADMIN->add('cache', new admin_category('cachestores', new lang_string('cachestores', 'cache')));
$ADMIN->locate('cachestores')->set_sorting(true);
foreach (core_component::get_plugin_list('cachestore') as $plugin => $path) {
Expand Down
2 changes: 1 addition & 1 deletion cache/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

$PAGE->set_title($title);
$PAGE->set_heading($SITE->fullname);
/* @var core_cache_renderer $renderer */
/** @var \core_cache\output\renderer $renderer */
$renderer = $PAGE->get_renderer('core_cache');

echo $renderer->header();
Expand Down
28 changes: 26 additions & 2 deletions cache/classes/administration_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,32 @@ public abstract function perform_cache_actions(string $action, array $forminfo):
/**
* This function must be implemented to display the cache admin page.
*
* @param core_cache_renderer $renderer the renderer used to generate the page.
* @param \core_cache\output\renderer $renderer the renderer used to generate the page.
* @return string the HTML for the page.
*/
public abstract function generate_admin_page(\core_cache_renderer $renderer): string;
abstract public function generate_admin_page(\core_cache\output\renderer $renderer): string;

/**
* Gets usage information about the whole cache system.
*
* This is a slow function and should only be used on an admin information page.
*
* The returned array lists all cache definitions with fields 'cacheid' and 'stores'. For
* each store, the following fields are available:
*
* - name (store name)
* - class (e.g. cachestore_redis)
* - supported (true if we have any information)
* - items (number of items stored)
* - mean (mean size of item)
* - sd (standard deviation for item sizes)
* - margin (margin of error for mean at 95% confidence)
* - storetotal (total usage for store if known, otherwise null)
*
* The storetotal field will be the same for every cache that uses the same store.
*
* @param int $samplekeys Number of keys to sample when checking size of large caches
* @return array Details of cache usage
*/
abstract public function get_usage(int $samplekeys): array;
}
91 changes: 87 additions & 4 deletions cache/classes/local/administration_display_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
namespace core_cache\local;

defined('MOODLE_INTERNAL') || die();
use cache_store, cache_factory, cache_config_writer, cache_helper, core_cache_renderer;
use cache_store, cache_factory, cache_config_writer, cache_helper;

/**
* A cache helper for administration tasks
Expand Down Expand Up @@ -765,10 +765,10 @@ public function action_deletelock(string $action) {
/**
* Outputs the main admin page by generating it through the renderer.
*
* @param core_cache_renderer $renderer the renderer to use to generate the page.
* @param \core_cache\output\renderer $renderer the renderer to use to generate the page.
* @return string the HTML for the admin page.
*/
public function generate_admin_page(core_cache_renderer $renderer): string {
public function generate_admin_page(\core_cache\output\renderer $renderer): string {
$context = \context_system::instance();
$html = '';

Expand All @@ -792,4 +792,87 @@ public function generate_admin_page(core_cache_renderer $renderer): string {

return $html;
}
}

/**
* Gets usage information about the whole cache system.
*
* This is a slow function and should only be used on an admin information page.
*
* The returned array lists all cache definitions with fields 'cacheid' and 'stores'. For
* each store, the following fields are available:
*
* - name (store name)
* - class (e.g. cachestore_redis)
* - supported (true if we have any information)
* - items (number of items stored)
* - mean (mean size of item)
* - sd (standard deviation for item sizes)
* - margin (margin of error for mean at 95% confidence)
* - storetotal (total usage for store if known, otherwise null)
*
* The storetotal field will be the same for every cache that uses the same store.
*
* @param int $samplekeys Number of keys to sample when checking size of large caches
* @return array Details of cache usage
*/
public function get_usage(int $samplekeys): array {
$results = [];

$factory = cache_factory::instance();

// Check the caches we already have an instance of, so we don't make another one...
$got = $factory->get_caches_in_use();
$gotid = [];
foreach ($got as $longid => $unused) {
// The IDs here can be of the form cacheid/morestuff if there are parameters in the
// cache. Any entry for a cacheid is good enough to consider that we don't need to make
// another entry ourselves, so we remove the extra bits and track the basic cache id.
$gotid[preg_replace('~^([^/]+/[^/]+)/.*$~', '$1', $longid)] = true;
}

$storetotals = [];

$config = $factory->create_config_instance();
foreach ($config->get_definitions() as $configdetails) {
if (!array_key_exists($configdetails['component'] . '/' . $configdetails['area'], $gotid)) {
// Where possible (if it doesn't need identifiers), make an instance of the cache, otherwise
// we can't get the store instances for it (and it won't show up in the list).
if (empty($configdetails['requireidentifiers'])) {
\cache::make($configdetails['component'], $configdetails['area']);
}
}
$definition = $factory->create_definition($configdetails['component'], $configdetails['area']);
$stores = $factory->get_store_instances_in_use($definition);

// Create object for results about this cache definition.
$currentresult = (object)['cacheid' => $definition->get_id(), 'stores' => []];
$results[$currentresult->cacheid] = $currentresult;

/** @var cache_store $store */
foreach ($stores as $store) {
// Skip static cache.
if ($store instanceof \cachestore_static) {
continue;
}

// Get cache size details from store.
$currentstore = $store->cache_size_details($samplekeys);

// Add in basic information about store.
$currentstore->name = $store->my_name();
$currentstore->class = get_class($store);

// Add in store total.
if (!array_key_exists($currentstore->name, $storetotals)) {
$storetotals[$currentstore->name] = $store->store_total_size();
}
$currentstore->storetotal = $storetotals[$currentstore->name];

$currentresult->stores[] = $currentstore;
}
}

ksort($results);
return $results;
}
}
164 changes: 149 additions & 15 deletions cache/renderer.php → cache/classes/output/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,28 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* The Cache renderer.
*
* This file is part of Moodle's cache API, affectionately called MUC.
*
* @package core
* @category cache
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();
namespace core_cache\output;

use cache_factory;
use cache_store;
use context;
use core_collator;
use html_table;
use html_table_cell;
use html_table_row;
use html_writer;
use lang_string;
use moodle_url;
use single_select;

/**
* The cache renderer (mainly admin interfaces).
*
* @package core
* @category cache
* @package core_cache
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_cache_renderer extends plugin_renderer_base {
class renderer extends \plugin_renderer_base {

/**
* Displays store summaries.
Expand Down Expand Up @@ -413,4 +413,138 @@ public function notifications(array $notifications = array()) {
$html .= html_writer::end_div();
return $html;
}

/**
* Creates the two tables which display on the usage page.
*
* @param array $usage Usage information (from cache_helper::usage)
* @return array Array of 2 tables (main and summary table)
* @throws \coding_exception
*/
public function usage_tables(array $usage): array {
$table = new \html_table();
$table->id = 'usage_main';
$table->head = [
get_string('definition', 'cache'),
get_string('storename', 'cache'),
get_string('plugin', 'cache'),
get_string('usage_items', 'cache'),
get_string('usage_mean', 'cache'),
get_string('usage_sd', 'cache'),
get_string('usage_total', 'cache'),
get_string('usage_totalmargin', 'cache')];
$table->align = [
'left', 'left', 'left',
'right', 'right', 'right', 'right', 'right'
];
$table->data = [];

$summarytable = new \html_table();
$summarytable->id = 'usage_summary';
$summarytable->head = [
get_string('storename', 'cache'),
get_string('plugin', 'cache'),
get_string('usage_total', 'cache'),
get_string('usage_realtotal', 'cache')
];
$summarytable->align = [
'left', 'left',
'right', 'right',
];
$summarytable->data = [];
$summarytable->attributes['class'] = 'generaltable w-auto';
$storetotals = [];

// We will highlight all cells that are more than 2% of total size, so work that out first.
$total = 0;
foreach ($usage as $definition) {
foreach ($definition->stores as $storedata) {
$total += $storedata->items * $storedata->mean;
}
}
$highlightover = round($total / 50);

foreach ($usage as $definition) {
foreach ($definition->stores as $storedata) {
$row = [];
$row[] = s($definition->cacheid);
$row[] = s($storedata->name);
$row[] = s($storedata->class);
if (!$storedata->supported) {
// We don't have data for this store because it isn't searchable.
$row[] = '-';
} else {
$row[] = $storedata->items;
}
if ($storedata->items) {
$row[] = display_size(round($storedata->mean));
if ($storedata->items > 1) {
$row[] = display_size(round($storedata->sd));
} else {
$row[] = '';
}
$cellsize = round($storedata->items * $storedata->mean);
$row[] = display_size($cellsize, 1, 'MB');

if (!array_key_exists($storedata->name, $storetotals)) {
$storetotals[$storedata->name] = (object)[
'plugin' => $storedata->class,
'total' => 0,
'storetotal' => $storedata->storetotal,
];
}
$storetotals[$storedata->name]->total += $cellsize;
} else {
$row[] = '';
$row[] = '';
$cellsize = 0;
$row[] = '';
}
if ($storedata->margin) {
// Plus or minus.
$row[] = '&#xb1;' . display_size($storedata->margin * $storedata->items, 1, 'MB');
} else {
$row[] = '';
}
$htmlrow = new \html_table_row($row);
if ($cellsize > $highlightover) {
$htmlrow->attributes = ['class' => 'table-warning'];
}
$table->data[] = $htmlrow;
}
}

ksort($storetotals);

foreach ($storetotals as $storename => $storedetails) {
$row = [s($storename), s($storedetails->plugin)];
$row[] = display_size($storedetails->total, 1, 'MB');
if ($storedetails->storetotal !== null) {
$row[] = display_size($storedetails->storetotal, 1, 'MB');
} else {
$row[] = '-';
}
$summarytable->data[] = $row;
}

return [$table, $summarytable];
}

/**
* Renders the usage page.
*
* @param \html_table $maintable Main table
* @param \html_table $summarytable Summary table
* @param \moodleform $samplesform Form to select number of samples
* @return string HTML for page
*/
public function usage_page(\html_table $maintable, \html_table $summarytable, \moodleform $samplesform): string {
$data = [
'maintable' => \html_writer::table($maintable),
'summarytable' => \html_writer::table($summarytable),
'samplesform' => $samplesform->render()
];

return $this->render_from_template('core_cache/usage', $data);
}
}
Loading

0 comments on commit c110965

Please sign in to comment.