From 9f4bb48e40c4bf267b7db3bd1615fb31af5d6828 Mon Sep 17 00:00:00 2001 From: Brendan Heywood Date: Tue, 7 Apr 2020 23:02:06 +1000 Subject: [PATCH] MDL-68329 cache: Improve cache performance footer info --- cache/classes/helper.php | 39 +++++++-- cache/classes/loaders.php | 28 +++---- cache/classes/store.php | 4 + cache/tests/cache_test.php | 130 ++++++++++++++--------------- cache/upgrade.txt | 3 + lib/moodlelib.php | 166 ++++++++++++++++++++++++++++++++----- 6 files changed, 265 insertions(+), 105 deletions(-) diff --git a/cache/classes/helper.php b/cache/classes/helper.php index 27763390952..dc4821b2558 100644 --- a/cache/classes/helper.php +++ b/cache/classes/helper.php @@ -361,20 +361,23 @@ class cache_helper { /** * Ensure that the stats array is ready to collect information for the given store and definition. * @param string $store + * @param string $storeclass * @param string $definition A string that identifies the definition. * @param int $mode One of cache_store::MODE_*. Since 2.9. */ - protected static function ensure_ready_for_stats($store, $definition, $mode = cache_store::MODE_APPLICATION) { + protected static function ensure_ready_for_stats($store, $storeclass, $definition, $mode = cache_store::MODE_APPLICATION) { // This function is performance-sensitive, so exit as quickly as possible // if we do not need to do anything. if (isset(self::$stats[$definition]['stores'][$store])) { return; } + if (!array_key_exists($definition, self::$stats)) { self::$stats[$definition] = array( 'mode' => $mode, 'stores' => array( $store => array( + 'class' => $storeclass, 'hits' => 0, 'misses' => 0, 'sets' => 0, @@ -383,6 +386,7 @@ class cache_helper { ); } else if (!array_key_exists($store, self::$stats[$definition]['stores'])) { self::$stats[$definition]['stores'][$store] = array( + 'class' => $storeclass, 'hits' => 0, 'misses' => 0, 'sets' => 0, @@ -418,15 +422,22 @@ class cache_helper { * In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a * cache_definition instance. It is preferable to pass a cache definition instance. * + * In Moodle 3.9 the first argument changed to also accept a cache_store. + * * @internal - * @param cache_definition $store + * @param string|cache_store $store * @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the * actual cache_definition object now. * @param int $hits The number of hits to record (by default 1) */ public static function record_cache_hit($store, $definition, $hits = 1) { + $storeclass = ''; + if ($store instanceof cache_store) { + $storeclass = get_class($store); + $store = $store->my_name(); + } list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition); - self::ensure_ready_for_stats($store, $definitionstr, $mode); + self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode); self::$stats[$definitionstr]['stores'][$store]['hits'] += $hits; } @@ -436,15 +447,22 @@ class cache_helper { * In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a * cache_definition instance. It is preferable to pass a cache definition instance. * + * In Moodle 3.9 the first argument changed to also accept a cache_store. + * * @internal - * @param string $store + * @param string|cache_store $store * @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the * actual cache_definition object now. * @param int $misses The number of misses to record (by default 1) */ public static function record_cache_miss($store, $definition, $misses = 1) { + $storeclass = ''; + if ($store instanceof cache_store) { + $storeclass = get_class($store); + $store = $store->my_name(); + } list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition); - self::ensure_ready_for_stats($store, $definitionstr, $mode); + self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode); self::$stats[$definitionstr]['stores'][$store]['misses'] += $misses; } @@ -454,15 +472,22 @@ class cache_helper { * In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a * cache_definition instance. It is preferable to pass a cache definition instance. * + * In Moodle 3.9 the first argument changed to also accept a cache_store. + * * @internal - * @param string $store + * @param string|cache_store $store * @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the * actual cache_definition object now. * @param int $sets The number of sets to record (by default 1) */ public static function record_cache_set($store, $definition, $sets = 1) { + $storeclass = ''; + if ($store instanceof cache_store) { + $storeclass = get_class($store); + $store = $store->my_name(); + } list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition); - self::ensure_ready_for_stats($store, $definitionstr, $mode); + self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode); self::$stats[$definitionstr]['stores'][$store]['sets'] += $sets; } diff --git a/cache/classes/loaders.php b/cache/classes/loaders.php index 8cd7914d3e8..6236cb02d50 100644 --- a/cache/classes/loaders.php +++ b/cache/classes/loaders.php @@ -414,7 +414,7 @@ class cache implements cache_loader { $setaftervalidation = false; if ($result === false) { if ($this->perfdebug) { - cache_helper::record_cache_miss($this->storetype, $this->definition); + cache_helper::record_cache_miss($this->store, $this->definition); } if ($this->loader !== false) { // We must pass the original (unparsed) key to the next loader in the chain. @@ -426,7 +426,7 @@ class cache implements cache_loader { } $setaftervalidation = ($result !== false); } else if ($this->perfdebug) { - cache_helper::record_cache_hit($this->storetype, $this->definition); + cache_helper::record_cache_hit($this->store, $this->definition); } // 5. Validate strictness. if ($strictness === MUST_EXIST && $result === false) { @@ -580,8 +580,8 @@ class cache implements cache_loader { $hits++; } } - cache_helper::record_cache_hit($this->storetype, $this->definition, $hits); - cache_helper::record_cache_miss($this->storetype, $this->definition, $misses); + cache_helper::record_cache_hit($this->store, $this->definition, $hits); + cache_helper::record_cache_miss($this->store, $this->definition, $misses); } // Return the result. Phew! @@ -607,7 +607,7 @@ class cache implements cache_loader { */ public function set($key, $data) { if ($this->perfdebug) { - cache_helper::record_cache_set($this->storetype, $this->definition); + cache_helper::record_cache_set($this->store, $this->definition); } if ($this->loader !== false) { // We have a loader available set it there as well. @@ -762,7 +762,7 @@ class cache implements cache_loader { } $successfullyset = $this->store->set_many($data); if ($this->perfdebug && $successfullyset) { - cache_helper::record_cache_set($this->storetype, $this->definition, $successfullyset); + cache_helper::record_cache_set($this->store, $this->definition, $successfullyset); } return $successfullyset; } @@ -1112,7 +1112,7 @@ class cache implements cache_loader { } if ($result !== false) { if ($this->perfdebug) { - cache_helper::record_cache_hit('** static acceleration **', $this->definition); + cache_helper::record_cache_hit(cache_store::STATIC_ACCEL, $this->definition); } if ($this->staticaccelerationsize > 1 && $this->staticaccelerationcount > 1) { // Check to see if this is the last item on the static acceleration keys array. @@ -1126,7 +1126,7 @@ class cache implements cache_loader { return $result; } else { if ($this->perfdebug) { - cache_helper::record_cache_miss('** static acceleration **', $this->definition); + cache_helper::record_cache_miss(cache_store::STATIC_ACCEL, $this->definition); } return false; } @@ -1830,7 +1830,7 @@ class cache_session extends cache { // 4. Load if from the loader/datasource if we don't already have it. if ($result === false) { if ($this->perfdebug) { - cache_helper::record_cache_miss($this->storetype, $this->get_definition()); + cache_helper::record_cache_miss($this->get_store(), $this->get_definition()); } if ($this->get_loader() !== false) { // We must pass the original (unparsed) key to the next loader in the chain. @@ -1845,7 +1845,7 @@ class cache_session extends cache { $this->set($key, $result); } } else if ($this->perfdebug) { - cache_helper::record_cache_hit($this->storetype, $this->get_definition()); + cache_helper::record_cache_hit($this->get_store(), $this->get_definition()); } // 5. Validate strictness. if ($strictness === MUST_EXIST && $result === false) { @@ -1889,7 +1889,7 @@ class cache_session extends cache { $loader->set($key, $data); } if ($this->perfdebug) { - cache_helper::record_cache_set($this->storetype, $this->get_definition()); + cache_helper::record_cache_set($this->get_store(), $this->get_definition()); } if (is_object($data) && $data instanceof cacheable_object) { $data = new cache_cached_object($data); @@ -2019,8 +2019,8 @@ class cache_session extends cache { $hits++; } } - cache_helper::record_cache_hit($this->storetype, $this->get_definition(), $hits); - cache_helper::record_cache_miss($this->storetype, $this->get_definition(), $misses); + cache_helper::record_cache_hit($this->get_store(), $this->get_definition(), $hits); + cache_helper::record_cache_miss($this->get_store(), $this->get_definition(), $misses); } return $return; @@ -2097,7 +2097,7 @@ class cache_session extends cache { } $successfullyset = $this->get_store()->set_many($data); if ($this->perfdebug && $successfullyset) { - cache_helper::record_cache_set($this->storetype, $this->get_definition(), $successfullyset); + cache_helper::record_cache_set($this->store, $this->get_definition(), $successfullyset); } return $successfullyset; } diff --git a/cache/classes/store.php b/cache/classes/store.php index 4fcb03ff20b..a2cfe3e6dce 100644 --- a/cache/classes/store.php +++ b/cache/classes/store.php @@ -144,6 +144,10 @@ abstract class cache_store implements cache_store_interface { * Request caches. Static caches really. */ const MODE_REQUEST = 4; + /** + * Static caches. + */ + const STATIC_ACCEL = '** static accel. **'; /** * Constructs an instance of the cache store. diff --git a/cache/tests/cache_test.php b/cache/tests/cache_test.php index 9c0f1d93475..0e6b203d3d1 100644 --- a/cache/tests/cache_test.php +++ b/cache/tests/cache_test.php @@ -2092,15 +2092,15 @@ class core_cache_testcase extends advanced_testcase { $this->assertFalse($request->get('missMe')); $endstats = cache_helper::get_stats(); - $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['misses']); - $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['hits']); - $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets']); - $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['misses']); - $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['hits']); - $this->assertEquals(1, $endstats[$sessionid]['stores']['cachestore_session']['sets']); - $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['misses']); - $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['hits']); - $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets']); + $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['misses']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets']); + $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['misses']); + $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits']); + $this->assertEquals(1, $endstats[$sessionid]['stores']['default_session']['sets']); + $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['misses']); + $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits']); + $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets']); $startstats = cache_helper::get_stats(); @@ -2116,24 +2116,24 @@ class core_cache_testcase extends advanced_testcase { $this->assertTrue($request->set('setMe4', 4)); $endstats = cache_helper::get_stats(); - $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] - - $startstats[$applicationid]['stores']['cachestore_file']['misses']); - $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['hits'] - - $startstats[$applicationid]['stores']['cachestore_file']['hits']); - $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['sets'] - - $startstats[$applicationid]['stores']['cachestore_file']['sets']); - $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] - - $startstats[$sessionid]['stores']['cachestore_session']['misses']); - $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['hits'] - - $startstats[$sessionid]['stores']['cachestore_session']['hits']); - $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['sets'] - - $startstats[$sessionid]['stores']['cachestore_session']['sets']); - $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] - - $startstats[$requestid]['stores']['cachestore_static']['misses']); - $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['hits'] - - $startstats[$requestid]['stores']['cachestore_static']['hits']); - $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['sets'] - - $startstats[$requestid]['stores']['cachestore_static']['sets']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - + $startstats[$applicationid]['stores']['default_application']['misses']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] - + $startstats[$applicationid]['stores']['default_application']['hits']); + $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] - + $startstats[$applicationid]['stores']['default_application']['sets']); + $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - + $startstats[$sessionid]['stores']['default_session']['misses']); + $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] - + $startstats[$sessionid]['stores']['default_session']['hits']); + $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] - + $startstats[$sessionid]['stores']['default_session']['sets']); + $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - + $startstats[$requestid]['stores']['default_request']['misses']); + $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] - + $startstats[$requestid]['stores']['default_request']['hits']); + $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] - + $startstats[$requestid]['stores']['default_request']['sets']); $startstats = cache_helper::get_stats(); @@ -2149,24 +2149,24 @@ class core_cache_testcase extends advanced_testcase { $this->assertEquals($request->get('setMe4'), 4); $endstats = cache_helper::get_stats(); - $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] - - $startstats[$applicationid]['stores']['cachestore_file']['misses']); - $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['hits'] - - $startstats[$applicationid]['stores']['cachestore_file']['hits']); - $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets'] - - $startstats[$applicationid]['stores']['cachestore_file']['sets']); - $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] - - $startstats[$sessionid]['stores']['cachestore_session']['misses']); - $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['hits'] - - $startstats[$sessionid]['stores']['cachestore_session']['hits']); - $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['sets'] - - $startstats[$sessionid]['stores']['cachestore_session']['sets']); - $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] - - $startstats[$requestid]['stores']['cachestore_static']['misses']); - $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['hits'] - - $startstats[$requestid]['stores']['cachestore_static']['hits']); - $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets'] - - $startstats[$requestid]['stores']['cachestore_static']['sets']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - + $startstats[$applicationid]['stores']['default_application']['misses']); + $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] - + $startstats[$applicationid]['stores']['default_application']['hits']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] - + $startstats[$applicationid]['stores']['default_application']['sets']); + $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - + $startstats[$sessionid]['stores']['default_session']['misses']); + $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] - + $startstats[$sessionid]['stores']['default_session']['hits']); + $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] - + $startstats[$sessionid]['stores']['default_session']['sets']); + $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - + $startstats[$requestid]['stores']['default_request']['misses']); + $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] - + $startstats[$requestid]['stores']['default_request']['hits']); + $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] - + $startstats[$requestid]['stores']['default_request']['sets']); $startstats = cache_helper::get_stats(); @@ -2176,24 +2176,24 @@ class core_cache_testcase extends advanced_testcase { $request->get_many(array('setMe1', 'setMe2', 'setMe3', 'setMe4')); $endstats = cache_helper::get_stats(); - $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] - - $startstats[$applicationid]['stores']['cachestore_file']['misses']); - $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['hits'] - - $startstats[$applicationid]['stores']['cachestore_file']['hits']); - $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets'] - - $startstats[$applicationid]['stores']['cachestore_file']['sets']); - $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] - - $startstats[$sessionid]['stores']['cachestore_session']['misses']); - $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['hits'] - - $startstats[$sessionid]['stores']['cachestore_session']['hits']); - $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['sets'] - - $startstats[$sessionid]['stores']['cachestore_session']['sets']); - $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] - - $startstats[$requestid]['stores']['cachestore_static']['misses']); - $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['hits'] - - $startstats[$requestid]['stores']['cachestore_static']['hits']); - $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets'] - - $startstats[$requestid]['stores']['cachestore_static']['sets']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - + $startstats[$applicationid]['stores']['default_application']['misses']); + $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] - + $startstats[$applicationid]['stores']['default_application']['hits']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] - + $startstats[$applicationid]['stores']['default_application']['sets']); + $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - + $startstats[$sessionid]['stores']['default_session']['misses']); + $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] - + $startstats[$sessionid]['stores']['default_session']['hits']); + $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] - + $startstats[$sessionid]['stores']['default_session']['sets']); + $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - + $startstats[$requestid]['stores']['default_request']['misses']); + $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] - + $startstats[$requestid]['stores']['default_request']['hits']); + $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] - + $startstats[$requestid]['stores']['default_request']['sets']); } public function test_static_cache() { @@ -2225,8 +2225,8 @@ class core_cache_testcase extends advanced_testcase { // Check that the static acceleration worked, even on empty arrays and the number 0. $endstats = cache_helper::get_stats(); - $this->assertEquals(0, $endstats[$applicationid]['stores']['** static acceleration **']['misses']); - $this->assertEquals(3, $endstats[$applicationid]['stores']['** static acceleration **']['hits']); + $this->assertEquals(0, $endstats[$applicationid]['stores']['** static accel. **']['misses']); + $this->assertEquals(3, $endstats[$applicationid]['stores']['** static accel. **']['hits']); } public function test_performance_debug_off() { diff --git a/cache/upgrade.txt b/cache/upgrade.txt index eaf6344da3d..076bd5312de 100644 --- a/cache/upgrade.txt +++ b/cache/upgrade.txt @@ -1,6 +1,9 @@ This files describes API changes in /cache/stores/* - cache store plugins. Information provided here is intended especially for developers. +=== 3.9 === +* The record_cache_hit/miss/set methods now take a cache_store instead of a cache_definition object + === 3.8 === * The Redis cache store can now make use of the Zstandard compression algorithm (see MDL-66428). diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 6d4a2be8803..542fd0d32d6 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -9546,37 +9546,83 @@ function get_performance_info() { } $info['html'] .= ''; + $html = ''; if ($stats = cache_helper::get_stats()) { - $html = ' '; - $html .= "
Total: $hits / $misses / $sets
"; + + $html .= html_writer::table($table); + + // Now lets also show sub totals for each cache store. + $storetotals = []; + $storetotal = ['hits' => 0, 'misses' => 0, 'sets' => 0]; + foreach ($stats as $definition => $details) { + foreach ($details['stores'] as $store => $data) { + if (!array_key_exists($store, $storetotals)) { + $storetotals[$store] = ['hits' => 0, 'misses' => 0, 'sets' => 0]; + } + $storetotals[$store]['class'] = $data['class']; + $storetotals[$store]['hits'] += $data['hits']; + $storetotals[$store]['misses'] += $data['misses']; + $storetotals[$store]['sets'] += $data['sets']; + $storetotal['hits'] += $data['hits']; + $storetotal['misses'] += $data['misses']; + $storetotal['sets'] += $data['sets']; + } + } + + $table = new html_table(); + $table->attributes['class'] = 'cachesused table table-dark table-sm w-auto table-striped'; + $table->head = [get_string('storename', 'cache'), get_string('type_cachestore', 'plugin'), 'H', 'M', 'S']; + $table->data = []; + $table->align = ['left', 'left', 'right', 'right', 'right']; + + ksort($storetotals); + + foreach ($storetotals as $store => $data) { + $row = []; + if ($data['hits'] == 0 and $data['misses'] > 0) { + $cachestoreclass = 'nohits text-danger'; + } else if ($data['hits'] < $data['misses']) { + $cachestoreclass = 'lowhits text-warning'; + } else { + $cachestoreclass = 'hihits text-success'; + } + $cell = new html_table_cell($store); + $cell->attributes = ['class' => $cachestoreclass]; + $row[] = $cell; + $cell = new html_table_cell($data['class']); + $cell->attributes = ['class' => $cachestoreclass]; + $row[] = $cell; + $cell = new html_table_cell($data['hits']); + $cell->attributes = ['class' => $cachestoreclass]; + $row[] = $cell; + $cell = new html_table_cell($data['misses']); + $cell->attributes = ['class' => $cachestoreclass]; + $row[] = $cell; + $cell = new html_table_cell($data['sets']); + $cell->attributes = ['class' => $cachestoreclass]; + $row[] = $cell; + $table->data[] = $row; + } + $row = [ + get_string('total'), + '', + $storetotal['hits'], + $storetotal['misses'], + $storetotal['sets'], + ]; + $table->data[] = $row; + + $html .= html_writer::table($table); + $info['cachesused'] = "$hits / $misses / $sets"; $info['html'] .= $html; $info['txt'] .= $text.'. ';