How to use persistent connections with Redis for Symfony Cache with PHP-FPM

Sylvain Fabre
AssoConnect
Published in
2 min readMay 13, 2019

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 and popen 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.

--

--

Sylvain Fabre
AssoConnect

Happy CTO at https://www.assoconnect.com a SaaS product to help non-profits focus on their missions. We’re hiring!