Aller au contenu

RDS « too many connections » : régler la crise maintenant et prévenir pour de bon

L'erreur RDS too many connections signifie que le moteur a atteint max_connections. Voici comment libérer des connexions immédiatement, trouver la fuite et prévenir la récidive avec du pooling.

ERROR 1040: Too many connections sur MySQL ou FATAL: remaining connection slots are reserved sur Postgres signifie que l’instance a atteint son plafond max_connections et refuse toute nouvelle session. Le correctif immédiat : entrer par une session encore fonctionnelle et tuer les connexions inactives; le vrai correctif se trouve presque toujours dans l’application — un pool de connexions mal dimensionné, une fuite, ou trop d’instances applicatives qui tiennent chacune leur propre pool. Augmenter max_connections achète du temps, mais au prix de la mémoire.

Ce qui se passe

Chaque moteur RDS a une limite max_connections, dérivée par défaut de la mémoire de l’instance. Sur RDS MySQL, la formule par défaut est {DBInstanceClassMemory/12582912}; sur RDS Postgres, c’est LEAST({DBInstanceClassMemory/9531392}, 5000). Une db.t3.medium atterrit donc dans les basses centaines, et une application occupée avec un pool mal configuré peut épuiser ça en quelques secondes. RDS réserve quelques places pour ses propres connexions de gestion, ce qui explique pourquoi Postgres vous refuse légèrement avant le chiffre que vous aviez calculé.

Le plafond existe pour une raison : chaque connexion coûte de la mémoire. Sur MySQL, les tampons par session (sort_buffer_size, join_buffer_size, piles de threads) se multiplient par le nombre de connexions. Sur Postgres, chaque connexion est un processus OS complet. Monter max_connections à 5000 sur une petite instance ne vous donne pas 5000 connexions utilisables — ça vous donne un plantage par manque de mémoire sous charge au lieu d’une erreur de connexion, ce qui est pire.

Les sources habituelles d’épuisement, dans l’ordre de fréquence où on les voit : un pool applicatif avec un max trop élevé multiplié par de nombreuses instances applicatives ou par la concurrence Lambda; des connexions ouvertes à chaque requête et jamais fermées (une fuite); des transactions longues qui retiennent des sessions, que le pool contourne en en ouvrant d’autres; et une pointe de trafic qui a fait grossir la couche applicative pendant que la limite de la base de données restait fixe.

Quoi vérifier en premier

  • Pouvez-vous encore entrer? Essayez de vous connecter comme utilisateur maître. Si même ça échoue, un redémarrage sera peut-être nécessaire — mais tentez d’abord les vérifications ci-dessous depuis n’importe quelle session déjà ouverte.
  • La métrique DatabaseConnections dans RDS > Databases > votre-instance > Monitoring — est-ce une montée lente sur plusieurs jours (fuite) ou un pic vertical (déploiement, scale-out, tempête de nouvelles tentatives)?
  • Ce qui a été déployé récemment. Une nouvelle version de l’application, un déclencheur Lambda ou un événement d’autoscaling qui a ajouté des instances applicatives est le déclencheur le plus fréquent.
  • Inactives vs actives. Des centaines de sessions inactives pointent vers le dimensionnement du pool ou une fuite; des centaines de sessions actives pointent vers des requêtes lentes qui s’empilent.

Comment vérifier

Tirez le compte de connexions récent depuis CloudWatch :

aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name DatabaseConnections \
  --dimensions Name=DBInstanceIdentifier,Value=prod-db \
  --start-time 2026-07-04T00:00:00Z --end-time 2026-07-04T12:00:00Z \
  --period 300 --statistics Maximum

Vérifiez la limite réelle dans votre groupe de paramètres :

aws rds describe-db-parameters \
  --db-parameter-group-name prod-mysql8 \
  --query "Parameters[?ParameterName=='max_connections'].[ParameterValue,Source]"

Puis, depuis une session de base de données, regardez qui occupe les places :

-- MySQL : compter les sessions par utilisateur, hôte et état
SELECT user, SUBSTRING_INDEX(host, ':', 1) AS host, command, COUNT(*)
FROM information_schema.processlist
GROUP BY user, host, command ORDER BY COUNT(*) DESC;

-- Postgres : compter les sessions par application et état
SELECT usename, application_name, state, COUNT(*)
FROM pg_stat_activity
GROUP BY usename, application_name, state ORDER BY COUNT(*) DESC;

Une pile de sessions Sleep (MySQL) ou idle (Postgres) provenant d’un seul utilisateur applicatif, c’est votre preuve.

Le runbook

  1. Libérer des places sans redémarrer. Sur MySQL, tuez les sessions inactives avec CALL mysql.rds_kill(<thread_id>); — RDS n’accorde pas SUPER, donc l’instruction KILL ordinaire échouera sur les threads des autres utilisateurs. Sur Postgres, exécutez SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND state_change < now() - interval '10 minutes';. Le service revient en quelques secondes.
  2. Arrêter l’hémorragie à la source. Si un déploiement précis ou un Lambda a causé le pic, faites une restauration arrière ou plafonnez sa concurrence. Tuer des sessions ne sert à rien tant que la source continue d’en ouvrir.
  3. Redimensionner les pools applicatifs. Le total des maximums de pool sur toutes les instances applicatives doit rester confortablement sous max_connections, moins une marge de 10-15 % pour l’administration, les migrations et le monitoring. Un pool de 20 par instance sur 15 instances, c’est 300 connexions — faites ce calcul explicitement.
  4. Configurer des délais d’inactivité des deux côtés. Délai d’inactivité du pool dans l’application, plus wait_timeout (MySQL) ou idle_in_transaction_session_timeout et idle_session_timeout (Postgres 14+) dans le groupe de paramètres, pour que les sessions fuyantes meurent d’elles-mêmes.
  5. Augmenter max_connections seulement après avoir fait le calcul mémoire. Une hausse modeste via un groupe de paramètres personnalisé est acceptable si la mémoire libérable le permet; le paramètre est dynamique sur les deux moteurs, donc aucun redémarrage requis. Mais c’est un coussin, pas un correctif.
  6. Placer RDS Proxy devant les charges en pointes ou serverless. Le proxy multiplexe des milliers de connexions clientes sur un petit pool de connexions de base de données et les maintient à travers les basculements. On a couvert quand ça aide et quand ça ajoute de la latence dans RDS Proxy et le pooling de connexions.

Quand c’est la mauvaise approche

Si les connexions s’épuisent parce que les requêtes sont lentes — les sessions s’empilent en attendant des verrous ou des parcours complets de table — tuer les sessions et hausser les limites ne fait que remettre le compteur à zéro. Réglez d’abord le problème de requêtes; le compte de connexions n’est que le symptôme. De même, grossir la classe d’instance uniquement pour hériter d’un max_connections par défaut plus élevé est une façon coûteuse d’éviter une configuration de pool que vous pourriez faire aujourd’hui gratuitement. La documentation RDS officielle détaille les formules de paramètres par moteur si vous voulez vérifier les valeurs par défaut de votre classe.

Erreurs fréquentes

  • Fixer max_connections à un énorme chiffre statique, puis subir des plantages par manque de mémoire sous vraie charge — une panne plus dure que celle qu’on venait de régler.
  • Compter un seul pool au lieu de tous les pools : chaque instance applicative, worker, tâche cron et copie Lambda tient son propre pool.
  • Redémarrer l’instance comme premier geste — ça coupe toutes les sessions, interrompt les écritures en cours, et la fuite remplit à nouveau les places en quelques minutes.
  • Ignorer les sessions Sleep/idle parce qu’« elles ne font rien » — une session inactive occupe une place et sa mémoire quand même.

Prévention

  • Alarme sur DatabaseConnections à environ 80 % de votre valeur réelle de max_connections, pas d’un chiffre rond.
  • Documentez le budget de connexions : limite, marge et maximum de pool par service, révisés chaque fois que la couche applicative grossit.
  • Utilisez RDS Proxy pour Lambda et toute charge à concurrence élastique.
  • Testez en charge le chemin des connexions, pas seulement le débit des requêtes — les pools se comportent différemment à saturation.
  • Gardez un chemin de session administrateur qui ne passe pas par le pool applicatif, pour pouvoir toujours entrer pendant un incident.

L’épuisement des connexions est un des incidents RDS les plus récupérables quand quelqu’un connaît le runbook, et un des plus récurrents quand personne ne possède le calcul des pools. Si personne dans votre équipe ne possède vraiment l’environnement AWS de bout en bout, c’est la racine de la plupart de ces problèmes — Stack Harbor opère des comptes AWS au quotidien sous Opérations AWS, et vous pouvez réserver une revue AWS.