Le WordPress d'aujourd'hui, décodé pour les développeurs

Performance

Un multisite de 500 sites sature max_connections aux inscriptions

Retour d'expérience sur un pic de connexions MySQL provoqué par une campagne d'inscription simultanée sur 500 sites, résolu par un pool de connexions partagé.

Par Clément Hadrot • 29 juin 2024 • 4 min de lecture • Aucun commentaire
Un multisite de 500 sites sature max_connections aux inscriptions

« Too many connections » — ce message MySQL, remonté dans les journaux d’erreur de plusieurs sites du réseau en même temps, a marqué le début d’un incident déclenché par une campagne d’inscription périscolaire ouverte simultanément sur les cinq cents sites d’un multisite WordPress mutualisé par une collectivité pour ses établissements scolaires.

Chaque établissement disposait de son propre site dans le réseau multisite, partageant la même base de données physique mais avec des tables préfixées séparément (wp_2_options, wp_3_options, etc.). L’ouverture simultanée du formulaire d’inscription à 8h précises, heure officielle de lancement communiquée aux familles, a concentré un afflux de connexions sur une fenêtre de quelques minutes.

Le mécanisme qui a mené à la saturation

Un multisite WordPress partage la même instance MySQL entre tous ses sites, mais chaque requête HTTP traitée par PHP-FPM ouvre sa propre connexion à la base de données, indépendamment du site du réseau qu’elle sert. Avec plusieurs centaines de familles connectées simultanément à des sites différents du réseau, chacune déclenchant plusieurs requêtes Ajax de vérification de disponibilité de créneau, le nombre de connexions simultanées a grimpé jusqu’à 512, dépassant la limite max_connections fixée à 400 dans la configuration MySQL du serveur.

Une fois cette limite atteinte, MySQL refuse purement et simplement toute nouvelle connexion, y compris pour des sites du réseau qui n’étaient pas directement concernés par la campagne d’inscription, provoquant un effet de bord sur l’ensemble du multisite plutôt qu’un ralentissement localisé.

Pourquoi augmenter simplement max_connections n’était pas suffisant

L'essentiel à retenir : Chaque site d'un multisite ouvre potentiellement sa propre connexion MySQL simultanée ; max_connections fixe une limite dure indépendante du nombre de sites du réseau ; Un pool de connexions partagé absorbe les pics sans augmenter la mémoire allouée à MySQL

Le réflexe immédiat, augmenter max_connections dans my.cnf, a été envisagé puis écarté après calcul : chaque connexion MySQL consomme de la mémoire pour ses buffers de travail (tri, jointures temporaires), de l’ordre de plusieurs mégaoctets selon les réglages de sort_buffer_size et join_buffer_size. Multiplier la limite par deux ou trois aurait risqué de faire déborder la mémoire disponible du serveur en cas de nouveau pic, transformant un refus de connexion propre en un plantage du service MySQL entier.

La solution retenue : un pool de connexions partagé

ProxySQL a été installé en frontal de l’instance MySQL, avec un pool de connexions limité côté base de données mais capable de mettre en attente les requêtes excédentaires plutôt que de les rejeter brutalement :

# Extrait de configuration ProxySQL (mysql_servers et mysql_query_rules)
INSERT INTO mysql_servers(hostgroup_id, hostname, port, max_connections)
VALUES (0, '127.0.0.1', 3306, 350);

UPDATE global_variables SET variable_value='600'
WHERE variable_name='mysql-max_connections';

Ce réglage autorise jusqu’à 600 connexions côté applicatif (PHP-FPM), tout en limitant à 350 le nombre de connexions réellement ouvertes vers MySQL. Les requêtes excédentaires sont mises en file d’attente par ProxySQL, avec une latence supplémentaire de quelques dizaines à quelques centaines de millisecondes en cas de pic, plutôt qu’un rejet immédiat par un message d’erreur visible du visiteur.

Résultat lors de la campagne suivante

MesureSans ProxySQLAvec ProxySQL
Connexions MySQL simultanées au pic512 (rejets au-delà de 400)348 (plafonné, file d’attente ProxySQL)
Erreurs « Too many connections »Nombreuses, sur plusieurs sites0
Latence médiane pendant le picNon mesurable (erreurs)640 ms

Prévention pour les campagnes suivantes

  • Un plafond de connexions MySQL doit être dimensionné en fonction de la mémoire disponible, jamais augmenté sans recalcul de la consommation mémoire totale possible.
  • Un proxy de connexions absorbe un pic ponctuel sans changer la capacité réelle de la base ; il ne dispense pas d’un dimensionnement raisonnable en amont.
  • Communiquer une heure de lancement précise à un grand nombre de familles concentre mécaniquement la charge ; étaler l’ouverture par ordre alphabétique d’établissement a été envisagé comme mesure complémentaire pour les campagnes suivantes.

Un pic de charge prévisible mérite un plan de charge, pas seulement une limite technique relevée au dernier moment.

En résumé

Ce dossier ne traite pas de la sécurité des formulaires d’inscription eux-mêmes, uniquement de la couche de connexion à la base de données mise sous tension par la concentration du trafic. Le pool de connexions partagé via ProxySQL a permis d’absorber un pic prévisible, structurel à ce type d’usage multisite, sans que chaque établissement scolaire n’ait eu à modifier quoi que ce soit à son propre site.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi