How to use persistent connections with Redis for Symfony Cache with PHP-FPM
In this article, we will learn why using persistent connections is a good practice and how to setup Symfony framework to use persistent connections on a Redis server.
Why use persistent connections?
A Redis server is designed to handle a lot of operations per second making it a very good candidate as a data cache storing engine.
But connecting to the server is an expensive operation and thus connections rate should be kept as low as possible.
A solution is to use a proxy like Twemproxy or you may go for persistent connections so they are reused by PHP to serve multiple requests.
Using persistent connections with PHP only works if:
- you’re using a non-thread safe PHP
- the PHP process is not killed once the request has been served
Is my PHP thread safe or not?
This is required if you are using phpredis:
This feature is not available in threaded versions.
pconnect
andpopen
then work like their non persistent equivalents.
This one is easy to check. You can use CLI or the phpinfo()
method to find out. This StackOverflow article describe these methods: https://stackoverflow.com/questions/5799843/find-if-the-installed-php-is-threadsafe-or-nonthreadsafe
Is my PHP process killed once the request has been served?
This depends on your setup. Basically, with Apache, you may be using:
- mod_php (default one):
- CGI
- PHP-FPM
- …
PHP-FPM is my favorite one. With this setup, a pool of PHP processes are started and each process waits for Apache to send requests to handle. Once the request has been served, then the process waits for another one. So it’s not killed and thus can use persistent connections to Redis.
How to set Symfony
Symfony allows you to easily use Redis as a cache adapter. You only need to set a Redis DSN as explained in the documentation: https://symfony.com/doc/current/reference/configuration/framework.html#default-redis-provider
But this adapter’s default setting uses non-persistent connections as per the default value of persistent according to the documentation: https://symfony.com/doc/current/components/cache/adapters/redis_adapter.html
So you need to use a custom adapter: https://symfony.com/doc/current/cache.html#custom-provider-options
services:
app.my_custom_redis_provider:
class: Redis
factory: [‘Symfony\Component\Cache\Adapter\RedisAdapter’, ‘createConnection’]
arguments:
— ‘%env(REDIS_URL)%’
— {persistent: 1, timeout: 15}
A 15 seconds timeout is considered to be a good one but you may change it to fit your needs. Be careful with the 0 timeout as it can create issues (https://github.com/phpredis/phpredis/issues/1265).
And then create a pool using this provider:
framework:
cache:
pools:
cache.my_redis:
adapter: cache.adapter.redis
provider: app.my_custom_redis_provider
If you want to use this setup for cache.app
and cache.system
default pools then you need:
framework:
cache:
app: cache.my_redis
system: cache.my_redis
Be careful!
Using persistent connections to Redis means that the connection is not closed until the PHP process dies. So if your PHP-FPM pool creates a lot of PHP processes then theses ones can create more connections than your Redis server is able to handle and this will trigger maximum number of clients reached errors.