Symfony cache is a very powerful tool for caching resource-intensive time-consuming operations, like a report or anything that makes sense to have it cached. In my case, I use to cache in advance results I know in advance the users will be asking for, using a cron job that updates the cached data every hour, comparing the new version of the data with the cached version and deciding if refreshing the cache.
This way, the users will always have fresh updated data loaded very fast. Some of these reports take more than 15 min in getting processed. I also use to have a replica dedicated only to run these cron jobs so to not impact workload on the servers used by the clients.
For most use cases, the standard file system cache is good enough to provide fast page loads (take a look at my article How to implement caching in Symfony 5+), however, sometimes is much better to have this cache handled by a redis pool. To implement Symfony cache using Redis, just follow these simple steps:
Install the Redis extension for PHP
pecl install redis
Install the Redis adapter for Symfony
composer require symfony/cache
composer require symfony/cache-adapter
composer require symfony/cache-adapter-redis
Configure Redis in your Symfony application
Open the config/packages/framework.yaml
file and add the following configuration:
# config/packages/cache.yaml
when@dev:
framework:
cache:
pools:
client_portal_pool_dev:
default_lifetime: 3600 #10 min in Dev
adapter: cache.adapter.redis
provider: 'redis://redisKey@ip:6380'
when@prod:
framework:
cache:
pools:
client_portal_pool_prod:
default_lifetime: 10800 #3 Hr in Prod
adapter: cache.adapter.redis
provider: 'redis://redisKey@ip:6380'
Also, notice how I'm using 2 different Redis pools for each environment. This is very important to keep in mind if you are using a cache provider like Redis, where you need to keep a clear separation of caching pools and not mess with production data.
Create a CacheService to handle the logic for get / set / delete
from cache.
<?php
namespace App\Services;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\Cache\CacheInterface;
class CoolCacheService
{
/**
* @var CacheInterface
*/
private CacheInterface $cachePool;
/**
* @param CacheInterface $cachePool
*/
public function __construct(CacheInterface $cachePool)
{
$this->cachePool = $cachePool;
}
/**
* @return CacheInterface
*/
public function getCachePool(): CacheInterface
{
return $this->cachePool;
}
/**
* @param CacheInterface $cachePool
* @param string $key
* @return CacheItem
*/
public function getCachedItem(CacheInterface $cachePool, string $key): CacheItem
{
return $cachePool->getItem($key);
}
/**
* @param CacheInterface $cachePool
* @param CacheItem $item
* @param array $data
* @return bool
*/
public function setCachedItemData(CacheInterface $cachePool, CacheItem $item, array $data): bool
{
$item->set(json_encode(['data' => $data, 'last_updated' => time()]));
return $cachePool->save($item);
}
/**
* @param Request $request
* @return string
* @throws InvalidArgumentException
*/
public function deleteItemFromCachePool(Request $request): string
{
$keyName = hash( 'sha256', $request->query->get('key'));
$item = $this->getCachedItem($this->getCachePool(), $keyName);
if ($item->isHit()) {
$this->getCachePool()->delete($keyName);
$html = '<pre>' . $keyName . '</pre> has been deleted';
} else {
$html = '<pre>' . $keyName . '</pre> not found in cache pool';
}
return $html;
}
/**
* @param Request $request
* @return string
*/
public function getHashedKeyName(Request $request): string
{
return hash( 'sha256', $request->getPathInfo());
}
}
Once you have this service class, every where you need to process heavy stuff do this:
<?php
namespace App\Services;
use http\Exception\RuntimeException;
use Symfony\Component\HttpFoundation\Request;
class DefaultService
{
/**
* @var CoolCacheService
*/
private CoolCacheService $cacheService;
/**
* @param CoolCacheService $cacheService
*/
public function __construct(CoolCacheService $cacheService)
{
$this->cacheService = $cacheService;
}
/**
* @param Request $request
* @return array
*/
public function getHeavyStuff(Request $request): array
{
try {
$keyName = $this->cacheService->getHashedKeyName($request);
$item = $this->cacheService->getCachedItem($this->cacheService->getCachePool(), $keyName);
if (!$item->isHit()) {
$data = $this->processHeavyStuff($args);
$ok = $this->cacheService->setCachedItemData($this->cacheService->getCachePool(), $item, $data);
}
$item = json_decode($this->clientPortalCacheService->getCachedItem($this->cacheService->getCachePool(), $keyName)->get(), true);
} catch (Throwable $e) {
throw new RuntimeException($e->getMessage());
}
return $item['data'] ?? [];
}
private function processHeavyStuff($args)
{
//...execute all heavy calculations here
return $data;
}
}
That's it! You have now implemented Symfony cache using Redis.
Data will be stored in Redis, and subsequent requests can retrieve the data from the cache instead of fetching it from the source, improving performance and reducing load on your application.