MDL-72596 core_cache: Track cache I/O size in perfdebug

For cache types which mean this information can be obtained without a
significant performance cost (i.e. just by calling strlen and not
having to serialize something that wasn't serialized already),
this change calculates the size of data read from or written to cache
in each request and includes it in the perfdebug table at bottom of
output (when that is turned on).

This supports the following cache types:

* File store
* Redis (only if caching is enabled)
This commit is contained in:
sam marshall
2021-10-19 17:00:48 +01:00
parent 1a9bee69e6
commit 9c29979b8b
10 changed files with 286 additions and 26 deletions
+21 -2
View File
@@ -381,6 +381,7 @@ class cache_helper {
'hits' => 0,
'misses' => 0,
'sets' => 0,
'iobytes' => cache_store::IO_BYTES_NOT_SUPPORTED,
)
)
);
@@ -390,6 +391,7 @@ class cache_helper {
'hits' => 0,
'misses' => 0,
'sets' => 0,
'iobytes' => cache_store::IO_BYTES_NOT_SUPPORTED,
);
}
}
@@ -429,8 +431,9 @@ class cache_helper {
* @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)
* @param int $readbytes Number of bytes read from the cache or cache_store::IO_BYTES_NOT_SUPPORTED
*/
public static function record_cache_hit($store, $definition, $hits = 1) {
public static function record_cache_hit($store, $definition, int $hits = 1, int $readbytes = cache_store::IO_BYTES_NOT_SUPPORTED): void {
$storeclass = '';
if ($store instanceof cache_store) {
$storeclass = get_class($store);
@@ -439,6 +442,13 @@ class cache_helper {
list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition);
self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode);
self::$stats[$definitionstr]['stores'][$store]['hits'] += $hits;
if ($readbytes !== cache_store::IO_BYTES_NOT_SUPPORTED) {
if (self::$stats[$definitionstr]['stores'][$store]['iobytes'] === cache_store::IO_BYTES_NOT_SUPPORTED) {
self::$stats[$definitionstr]['stores'][$store]['iobytes'] = $readbytes;
} else {
self::$stats[$definitionstr]['stores'][$store]['iobytes'] += $readbytes;
}
}
}
/**
@@ -479,8 +489,10 @@ class cache_helper {
* @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)
* @param int $writebytes Number of bytes written to the cache or cache_store::IO_BYTES_NOT_SUPPORTED
*/
public static function record_cache_set($store, $definition, $sets = 1) {
public static function record_cache_set($store, $definition, int $sets = 1,
int $writebytes = cache_store::IO_BYTES_NOT_SUPPORTED) {
$storeclass = '';
if ($store instanceof cache_store) {
$storeclass = get_class($store);
@@ -489,6 +501,13 @@ class cache_helper {
list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition);
self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode);
self::$stats[$definitionstr]['stores'][$store]['sets'] += $sets;
if ($writebytes !== cache_store::IO_BYTES_NOT_SUPPORTED) {
if (self::$stats[$definitionstr]['stores'][$store]['iobytes'] === cache_store::IO_BYTES_NOT_SUPPORTED) {
self::$stats[$definitionstr]['stores'][$store]['iobytes'] = $writebytes;
} else {
self::$stats[$definitionstr]['stores'][$store]['iobytes'] += $writebytes;
}
}
}
/**
+31 -14
View File
@@ -445,7 +445,8 @@ class cache implements cache_loader {
}
$setaftervalidation = ($result !== false);
} else if ($this->perfdebug) {
cache_helper::record_cache_hit($this->store, $this->definition);
$readbytes = $this->store->get_last_io_bytes();
cache_helper::record_cache_hit($this->store, $this->definition, 1, $readbytes);
}
// 5. Validate strictness.
if ($strictness === MUST_EXIST && $result === false) {
@@ -492,6 +493,7 @@ class cache implements cache_loader {
$resultpersist = array();
$resultstore = array();
$keystofind = array();
$readbytes = cache_store::IO_BYTES_NOT_SUPPORTED;
// First up check the persist cache for each key.
$isusingpersist = $this->use_static_acceleration();
@@ -515,6 +517,9 @@ class cache implements cache_loader {
// Next assuming we didn't find all of the keys in the persist cache try loading them from the store.
if (count($keystofind)) {
$resultstore = $this->store->get_many(array_keys($keystofind));
if ($this->perfdebug) {
$readbytes = $this->store->get_last_io_bytes();
}
// Process each item in the result to "unwrap" it.
foreach ($resultstore as $key => $value) {
if ($value instanceof cache_ttl_wrapper) {
@@ -599,7 +604,7 @@ class cache implements cache_loader {
$hits++;
}
}
cache_helper::record_cache_hit($this->store, $this->definition, $hits);
cache_helper::record_cache_hit($this->store, $this->definition, $hits, $readbytes);
cache_helper::record_cache_miss($this->store, $this->definition, $misses);
}
@@ -625,9 +630,6 @@ class cache implements cache_loader {
* @return bool True on success, false otherwise.
*/
public function set($key, $data) {
if ($this->perfdebug) {
cache_helper::record_cache_set($this->store, $this->definition);
}
if ($this->loader !== false) {
// We have a loader available set it there as well.
// We have to let the loader do its own parsing of data as it may be unique.
@@ -654,7 +656,12 @@ class cache implements cache_loader {
}
$parsedkey = $this->parse_key($key);
return $this->store->set($parsedkey, $data);
$success = $this->store->set($parsedkey, $data);
if ($this->perfdebug) {
cache_helper::record_cache_set($this->store, $this->definition, 1,
$this->store->get_last_io_bytes());
}
return $success;
}
/**
@@ -781,7 +788,8 @@ class cache implements cache_loader {
}
$successfullyset = $this->store->set_many($data);
if ($this->perfdebug && $successfullyset) {
cache_helper::record_cache_set($this->store, $this->definition, $successfullyset);
cache_helper::record_cache_set($this->store, $this->definition, $successfullyset,
$this->store->get_last_io_bytes());
}
return $successfullyset;
}
@@ -1845,6 +1853,9 @@ class cache_session extends cache {
if ($result instanceof cache_cached_object) {
$result = $result->restore_object();
}
if ($this->perfdebug) {
$readbytes = $this->get_store()->get_last_io_bytes();
}
}
// 4. Load if from the loader/datasource if we don't already have it.
if ($result === false) {
@@ -1864,7 +1875,7 @@ class cache_session extends cache {
$this->set($key, $result);
}
} else if ($this->perfdebug) {
cache_helper::record_cache_hit($this->get_store(), $this->get_definition());
cache_helper::record_cache_hit($this->get_store(), $this->get_definition(), 1, $readbytes);
}
// 5. Validate strictness.
if ($strictness === MUST_EXIST && $result === false) {
@@ -1907,9 +1918,6 @@ class cache_session extends cache {
// We have to let the loader do its own parsing of data as it may be unique.
$loader->set($key, $data);
}
if ($this->perfdebug) {
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);
} else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($data)) {
@@ -1923,7 +1931,12 @@ class cache_session extends cache {
if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
$data = new cache_ttl_wrapper($data, $this->get_definition()->get_ttl());
}
return $this->get_store()->set($this->parse_key($key), $data);
$success = $this->get_store()->set($this->parse_key($key), $data);
if ($this->perfdebug) {
cache_helper::record_cache_set($this->get_store(), $this->get_definition(), 1,
$this->get_store()->get_last_io_bytes());
}
return $success;
}
/**
@@ -1971,6 +1984,9 @@ class cache_session extends cache {
$keymap[$parsedkey] = $key;
}
$result = $this->get_store()->get_many($parsedkeys);
if ($this->perfdebug) {
$readbytes = $this->get_store()->get_last_io_bytes();
}
$return = array();
$missingkeys = array();
$hasmissingkeys = false;
@@ -2038,7 +2054,7 @@ class cache_session extends cache {
$hits++;
}
}
cache_helper::record_cache_hit($this->get_store(), $this->get_definition(), $hits);
cache_helper::record_cache_hit($this->get_store(), $this->get_definition(), $hits, $readbytes);
cache_helper::record_cache_miss($this->get_store(), $this->get_definition(), $misses);
}
return $return;
@@ -2116,7 +2132,8 @@ class cache_session extends cache {
}
$successfullyset = $this->get_store()->set_many($data);
if ($this->perfdebug && $successfullyset) {
cache_helper::record_cache_set($this->get_store(), $this->get_definition(), $successfullyset);
cache_helper::record_cache_set($this->get_store(), $this->get_definition(), $successfullyset,
$this->get_store()->get_last_io_bytes());
}
return $successfullyset;
}
+24
View File
@@ -149,6 +149,11 @@ abstract class cache_store implements cache_store_interface {
*/
const STATIC_ACCEL = '** static accel. **';
/**
* Returned from get_last_io_bytes if this cache store doesn't support counting bytes read/sent.
*/
const IO_BYTES_NOT_SUPPORTED = -1;
/**
* Constructs an instance of the cache store.
*
@@ -392,4 +397,23 @@ abstract class cache_store implements cache_store_interface {
public static function ready_to_be_used_for_testing() {
return false;
}
/**
* Gets the number of bytes read from or written to cache as a result of the last action.
*
* This includes calls to the functions get(), get_many(), set(), and set_many(). The number
* is reset by calling any of these functions.
*
* This should be the actual number of bytes of the value read from or written to cache,
* giving an impression of the network or other load. It will not be exactly the same amount
* as netowrk traffic because of protocol overhead, key text, etc.
*
* If not supported, returns IO_BYTES_NOT_SUPPORTED.
*
* @return int Bytes read (or 0 if none/not supported)
* @since Moodle 4.0
*/
public function get_last_io_bytes(): int {
return self::IO_BYTES_NOT_SUPPORTED;
}
}