Erreur PostgreSQL : sorry, too many clients already
FATAL: sorry, too many clients already
Chaque slot de connexion du serveur est occupé, donc PostgreSQL refuse le nouveau. Le serveur est par ailleurs en bonne santé — il est simplement plein. Une variante proche du même problème est le message concernant « remaining connection slots are reserved » : les derniers slots sont réservés aux superutilisateurs afin qu'un administrateur puisse toujours entrer et corriger les choses.
Ce que signifie cette erreur
PostgreSQL accepte au plus max_connections connexions concurrentes (par défaut : 100), et en réserve une poignée (superuser_reserved_connections, par défaut : 3) pour les superutilisateurs. Lorsqu'un client ordinaire demande une connexion au-delà de la limite non réservée, il obtient ce FATAL au moment de la connexion — la connexion est refusée, rien d'autre sur le serveur n'est affecté.
Chaque connexion est un processus serveur dédié avec sa propre mémoire, donc la limite existe pour une raison : PostgreSQL ne gère pas des milliers de connexions directes avec grâce. C'est pourquoi la solution durable n'est presque jamais « augmenter le nombre ».
Causes fréquentes
- Aucun pooler de connexions devant la base de données, avec de nombreuses instances applicatives ouvrant chacune leur propre pool. Dix services × 20 connexions chacun = 200 avant même tout pic de trafic.
- Fuites de connexions : des chemins de code qui ouvrent des connexions et ne les rendent jamais au pool.
- Des sessions bloquées en
idle in transactiondétenant des slots (et des verrous, et la progression de vacuum) indéfiniment. - Des workers serverless / à autoscaling, ouvrant chacun de nouvelles connexions à chaque invocation.
- Un ralentissement ailleurs : si les requêtes prennent soudain 10× plus longtemps, les connexions s'accumulent à mesure que les appelants expirent et réessayent. L'erreur est le symptôme ; la requête lente est la maladie.
Comment la diagnostiquer
Connectez-vous en tant que superutilisateur (c'est à cela que servent les slots réservés) et regardez qui détient les slots :
SHOW max_connections;
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state ORDER BY count(*) DESC;
SELECT usename, application_name, count(*)
FROM pg_stat_activity
GROUP BY 1, 2 ORDER BY count(*) DESC;
-- Sessions en transaction depuis le plus longtemps (candidates aux fuites) :
SELECT pid, usename, state, now() - xact_start AS xact_age, left(query, 60)
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_start
LIMIT 10;
Beaucoup de connexions idle pointent vers des pools applicatifs surdimensionnés ou qui fuient ; d'anciennes sessions idle in transaction pointent vers du code applicatif qui a ouvert une transaction et est parti ailleurs.
Comment la corriger
- Placez un pooler devant (PgBouncer en mode transaction est la réponse standard) : des milliers de connexions clientes se multiplexent sur quelques dizaines de connexions serveur. C'est la solution qui passe à l'échelle ; vérifiez d'abord que votre charge de travail ne repose pas sur un état de session (
SETau niveau session, verrous consultatifs, requêtes préparées entre transactions) que le pooling en mode transaction ne préserve pas. - Corrigez les fuites : bornez chaque pool (taille max, durée de vie max, timeout d'inactivité) et assurez-vous que les connexions sont rendues dans un nettoyage de type
finally. - Limitez les dégâts côté serveur :
ALTER ROLE app_user CONNECTION LIMIT 50; ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min'; SELECT pg_reload_conf(); - N'augmentez
max_connectionsque délibérément (nécessite un redémarrage) : chaque slot est un processus au coût mémoire réel, et des centaines de backends actifs ont généralement de moins bonnes performances que quelques dizaines poolés.
idle in transaction ne font pas que dévorer des slots : elles retiennent le vacuum pour toute la base de données.
