Caching is off by default. When enabled, a metric’s computed result is remembered for a TTL in a cache store, so busy dashboards don’t recompute the same aggregates on every request.
Enable globally
// config/metrics.php
'cache' => [
'enabled' => env('METRICS_CACHE_ENABLED', false), // master switch
'store' => env('METRICS_CACHE_STORE'), // null = default store
'ttl' => env('METRICS_CACHE_TTL', 300), // seconds
'prefix' => 'metrics', // cache-key prefix
],Set METRICS_CACHE_ENABLED=true to cache every metric.
Per-metric control
Each metric can opt in or out at runtime, regardless of the config switch:
| Method | Effect |
|---|---|
cache(?int $ttl = null) | Enable caching; optionally override the TTL in seconds. |
cacheFor(DateTimeInterface|int $ttl) | Enable caching until a moment, or for N seconds. |
cacheKey(string $key) | Use an explicit cache key instead of the generated one. |
dontCache() | Disable caching for this metric even when config enables it. |
use RoundlyConsulting\Metrics\Facades\Metrics;
Metrics::value()->count(User::query())->range('TODAY')->cacheFor(now()->addHour())->toArray();
Metrics::value()->count(User::query())->cache()->toArray(); // config TTL
Metrics::value()->count(User::query())->cache(600)->toArray(); // 10 minutes
Metrics::value()->count(User::query())->cacheFor(now()->endOfDay())->toArray(); // until midnight
Metrics::value()->count(User::query())->cache()->cacheKey('users:all')->toArray(); // explicit key
Metrics::value()->count(User::query())->dontCache()->toArray(); // always freshCache keys
Without an explicit cacheKey(), the key is {prefix}:{md5} over the metric class, its registry key, the range and its custom start/end, the reporting and app timezones, the precision and rounding mode, an inline builder’s query and aggregate, and type-specific discriminators — a trend’s unit, gap filling and series column; a value metric’s comparison settings, plus the target for progress metrics; a partition’s limit and translated “Other” label. Differently configured calls to the same metric — and inline builders versus registered metrics of the same class — cache separately and never collide.
A custom cacheKey() is used verbatim — the prefix is not applied and range changes don’t alter it, so keep it unique yourself. Only the result portion is cached, as plain data — result() rebuilds the typed object from it, and the envelope around it is rebuilt on every call. Presentation is applied on every read and never cached — formatUsing() and a partition’s labelUsing() labels.
Forgetting cached results
Metrics::forget('revenue'); // every cached range/timezone/option of one registered metric
Metrics::forget($metric); // an unregistered metric instance (by its class)
Metrics::flushCache(); // every cached metric resultInvalidation works on every cache store, with or without tags: each entry records the cache generation it was written under, and forgetting starts a new generation, so older entries are recalculated on their next read and overwritten in place. It covers custom cacheKey() entries too. forget() throws UnknownMetricException for a key that isn’t registered.
TTL from .env
METRICS_CACHE_ENABLED accepts true, false, 1, 0, on, off, yes and no; METRICS_CACHE_TTL is a whole number of seconds between 1 and 31536000 (one year) — the string .env produces is honoured. An unusable TTL or switch value (say METRICS_CACHE_ENABLED=disabled) throws InvalidConfigurationException instead of silently falling back to the default; a blank one (METRICS_CACHE_TTL=) is not set and takes it.
The MetricCalculated event’s fromCache flag tells you whether a resolution was a cache hit — see Events.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.