Cache Warmers
Pre-populate critical cache entries on deployment with CacheWarmerInterface — discovered via vortos.cache_warmer tag.
Cache Warmers
Cache warmers pre-populate the cache before traffic arrives. Run them after every deployment once the cache is cleared. Each warmer is a PHP class implementing CacheWarmerInterface — discovered automatically via the vortos.cache_warmer tag.
CacheWarmerInterface
interface CacheWarmerInterface
{
public function warmUp(): void;
}warmUp() must be:
- Idempotent — safe to run multiple times, same result every time
- Non-throwing for recoverable errors — log and continue, don't abort the warmup run
- Reasonably fast — it blocks the deployment pipeline
Write a Warmer
use Vortos\Cache\Contract\CacheWarmerInterface;
use Vortos\Cache\Contract\TaggedCacheInterface;
final class PermissionMatrixCacheWarmer implements CacheWarmerInterface
{
public function __construct(
private PermissionRepository $permissions,
private TaggedCacheInterface $cache,
) {}
public function warmUp(): void
{
// Load everything from DB once
$matrix = $this->permissions->getFullMatrix();
// Store tagged — invalidated when roles change
$this->cache->setWithTags(
'auth:permission_matrix',
$matrix,
['permissions', 'roles'],
ttl: 3600,
);
}
}Register a Warmer
Tag the service with vortos.cache_warmer in config/services.php:
$services->set(PermissionMatrixCacheWarmer::class)
->tag('vortos.cache_warmer');
$services->set(ActiveCompetitionsCacheWarmer::class)
->tag('vortos.cache_warmer');CacheWarmupCommand uses a tagged iterator — it discovers all vortos.cache_warmer services automatically.
Run Warmers
# Typical deployment sequence:
php bin/console vortos:cache:clear # 1. clear stale entries
php bin/console vortos:cache:warmup # 2. pre-populate critical entriesOutput:
Vortos Cache Warmup
✔ App\Cache\PermissionMatrixCacheWarmer
✔ App\Cache\ActiveCompetitionsCacheWarmer
✘ App\Cache\ExternalApiCacheWarmer: Connection timed out
Done. 2 warmer(s) ran successfully.Failed warmers are logged and skipped — they do not abort the warmup run. The application starts with warm cache where possible.
Practical Warmer Examples
Configuration Warmer
final class AppConfigCacheWarmer implements CacheWarmerInterface
{
public function __construct(
private ConfigRepository $config,
private TaggedCacheInterface $cache,
) {}
public function warmUp(): void
{
$settings = $this->config->loadAll();
foreach ($settings as $key => $value) {
$this->cache->setWithTags(
"config:{$key}",
$value,
['config'],
3600,
);
}
}
}Ranking Cache Warmer
final class RankingsCacheWarmer implements CacheWarmerInterface
{
public function __construct(
private RankingRepository $rankings,
private TaggedCacheInterface $cache,
) {}
public function warmUp(): void
{
// Cache top 100 per category
foreach (['overall', 'junior', 'senior'] as $category) {
$top = $this->rankings->getTop(category: $category, limit: 100);
$this->cache->setWithTags(
"rankings:{$category}:top100",
$top,
['rankings', "rankings:{$category}"],
1800,
);
}
}
}External API Warmer with Error Handling
final class ExchangeRateCacheWarmer implements CacheWarmerInterface
{
public function __construct(
private ExchangeRateClient $client,
private TaggedCacheInterface $cache,
private \Psr\Log\LoggerInterface $logger,
) {}
public function warmUp(): void
{
try {
$rates = $this->client->getLatestRates();
$this->cache->setWithTags('exchange_rates', $rates, ['exchange_rates'], 900);
} catch (\Throwable $e) {
// Log but don't throw — warmup continues with other warmers
$this->logger->warning('Failed to warm exchange rates: ' . $e->getMessage());
}
}
}Warmers Run in Registration Order
Warmers run in the order they are tagged in config/services.php. If one warmer depends on data populated by another, register the dependency first.