cache_monitor-1.0.x-dev/src/Metrics/Aggregator.php
src/Metrics/Aggregator.php
<?php
namespace Drupal\cache_monitor\Metrics;
use ArrayObject;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Aggregates cache operation metrics during a request.
*
* Each operation is recorded with:
* - bin: The cache bin name.
* - op: The operation name (e.g. get, set, delete).
* - calls: Number of calls made.
* - items: Number of items affected (e.g. for getMulti/setMulti).
* - ms: Total time spent in milliseconds.
*
* Metrics are stored in the current HTTP request attributes, or in a fallback
* bucket for non-HTTP (CLI) requests.
*/
final class Aggregator {
/**
* Request attribute key for storing metrics.
*/
private const ATTR = '_cache_monitor_metrics';
/**
* Fallback bucket for non-HTTP (CLI) requests.
* @var \ArrayObject
*/
private ArrayObject $cliBucket;
/**
* The request stack to access the current request.
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
private RequestStack $stack;
/**
* Constructs a new Aggregator instance.
*
* @param \Symfony\Component\HttpFoundation\RequestStack $stack
* The request stack service.
*/
public function __construct(RequestStack $stack) {
$this->stack = $stack;
$this->cliBucket = new ArrayObject();
}
/**
* Gets the current metrics bucket.
*
* Uses the current HTTP request attributes if available, otherwise falls
* back to a CLI bucket.
*
* @return \ArrayObject
* The metrics bucket.
*/
private function bucket(): ArrayObject {
$req = $this->stack->getCurrentRequest();
if (!$req) {
return $this->cliBucket;
}
$bag = $req->attributes->get(self::ATTR);
if (!$bag instanceof ArrayObject) {
$bag = new ArrayObject();
$req->attributes->set(self::ATTR, $bag);
}
return $bag;
}
/**
* Adds a cache operation metric.
*
* @param string $bin
* The cache bin name.
* @param string $op
* The operation name (e.g. get, set, delete).
* @param int $count
* The number of items affected (e.g. for getMulti/setMulti).
* @param float $ms
* The time taken in milliseconds.
*/
public function add(string $bin, string $backend, string $op, int $count, float $ms): void {
$b = $this->bucket();
$key = "$bin|$op";
if (!isset($b[$key])) {
$b[$key] = ['bin' => $bin, 'backend' => $backend, 'op' => $op, 'calls' => 0, 'items' => 0, 'ms' => 0.0];
}
$row = $b[$key];
$row['calls'] += 1;
$row['items'] += max(0, $count);
$row['ms'] += $ms;
$b[$key] = $row; // re-set
}
/**
* Drains and returns all collected metrics, resetting the bucket.
*
* @return array<int,array{bin:string,op:string,calls:int,items:int,ms:float}>
*/
public function drain(): array {
$b = $this->bucket();
$out = array_values((array) $b);
$b->exchangeArray([]);
return $out;
}
/**
* Checks if there are any collected metrics.
*
* @return bool
* TRUE if no metrics have been collected, FALSE otherwise.
*/
public function isEmpty(): bool {
return count((array) $this->bucket()) === 0;
}
}
