Symfony DoctrineBundle persistent connections and PHP-FPM worker limits: safe to enable?
0 reputation · 24 Aug 2021, 18:24 UTC
0 reputation · 24 Aug 2021, 18:24 UTC
The goal is to decide whether enabling the DoctrineBundle persistent option (which sets PDO::ATTR_PERSISTENT) is advisable when running Symfony under PHP‑FPM, weighing the benefit of reduced connection churn against the risk of exhausting the database’s max_connections due to each PHP‑FPM worker maintaining its own persistent connection.
Constraints include the fact that Doctrine DBAL does not provide its own connection pool, so persistence relies solely on PDO or external proxies; each worker can hold a connection that may outlive a request and become stale if wait_timeout or interactive_timeout is low. The unresolved decision centers on whether the increased connection count from workers × persistent connections regularly exceeds the database limit, and how stale connections affect reliability.
Does enabling persistent connections raise the likelihood of hitting max_connections given typical PHP‑FPM worker counts?
How can stale connections be identified and mitigated when using PDO::ATTR_PERSISTENT with DoctrineBundle?
Is there a recommended upper bound for PHP‑FPM workers when persistent connections are enabled for a specific database connection limit?
30275 reputation · 24 Aug 2021, 18:59 UTC
Enabling the persistent option in DoctrineBundle is generally not recommended for standard PHP-FPM environments unless you have a strict 1:1 mapping between your maximum worker count and your database's available connection slots. Because Doctrine does not implement a shared connection pool, PDO::ATTR_PERSISTENT causes each PHP-FPM worker to hold its own dedicated connection to the database for the duration of the worker's lifecycle.
When persistent connections are active, the number of open database connections scales linearly with the number of active PHP-FPM workers. If pm.max_children exceeds the database's max_connections limit, the database will reject new connection attempts, leading to application-wide failures (typically 500 or 502 errors) once the limit is hit.
Persistent connections can become "stale" if the database server closes the connection due to a wait_timeout while the PHP-FPM worker remains idle. Since the worker retains the PDO object, the next request may attempt to use a closed socket, resulting in errors such as SQLSTATE[HY000] [2006] MySQL server has gone away.
To mitigate this, you can implement the following strategies:
pm.max_requests in your PHP-FPM pool configuration. This forces workers to restart after processing a set number of requests, effectively flushing persistent connections.SELECT 1) to verify the connection is alive before executing business logic.To avoid hitting max_connections, follow this formula for your PHP-FPM configuration:
pm.max_children < (Database max_connections - Reserved Connections for Admins)
If your infrastructure requires more workers than your database can support in concurrent sessions, you should avoid PDO::ATTR_PERSISTENT and instead implement a database proxy such as PgBouncer (for PostgreSQL) or ProxySQL (for MySQL) to handle connection multiplexing.
To verify the impact of persistent connections on your environment, use the following commands during peak traffic:
SHOW PROCESSLIST; or SELECT COUNT(*) FROM information_schema.processlist;SELECT count(*) FROM pg_stat_activity;Compare these counts against your pm.max_children setting to see if connections are scaling 1:1 with workers.
Missing Diagnostic: To provide a more precise recommendation, please specify the current max_connections value of your database and the pm.max_children setting of your PHP-FPM pool.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.