Présentation de PgBouncer
PgBouncer est un pooler de connexions PostgreSQL single-threadé. Un seul processus utilise un seul cœur CPU, quelle que soit la quantité de cœurs disponibles sur la machine. Sur une machine avec 16 cœurs virtuels, un seul cœur gère toutes les connexions, tandis que les 15 autres restent inactifs, et le pooler atteint rapidement sa limite de débit avant même que PostgreSQL ne soit sollicité.
Architecture de la mise à l'échelle
Dans ClickHouse Managed Postgres, nous exécutons une flotte de processus PgBouncer, dimensionnés en fonction des cœurs disponibles. Chaque processus de la flotte se lie au même port avec l'option so_reuseport activée, permettant au noyau de répartir les connexions entrantes entre les processus. Les clients se connectent toujours à un seul point de terminaison et ne sont pas conscients de la présence de plusieurs processus PgBouncer derrière.
so_reuseport
Cependant, cette approche présente un problème : l'annulation des requêtes. Lorsqu'une demande d'annulation de requête PostgreSQL arrive sur une nouvelle connexion, elle peut être acheminée vers un processus différent de celui qui gère la session. Le processus qui reçoit la demande d'annulation ne connaît pas la requête et ne peut donc pas l'annuler.
Résolution et performances
La mise en place d'un mécanisme de peering entre les processus résout ce problème. Les processus sont ainsi informés les uns des autres, ce qui permet de transférer une demande d'annulation à celui qui gère réellement la session. L'annulation des requêtes fonctionne alors sur l'ensemble de la flotte, même si n'importe quelle requête peut arriver n'importe où.
Nous avons testé les deux configurations sur des instances AWS EC2 identiques : une instance c7i.4xlarge de 16 cœurs virtuels pour le pooler, une instance distincte pour PostgreSQL et une troisième pour générer la charge avec pgbench. La configuration avec un seul processus PgBouncer a atteint un débit maximum d'environ 87 000 transactions par seconde, alors que la flotte de 16 processus a atteint un débit de environ 336 000 transactions par seconde, soit environ 4 fois plus.
La flotte de processus a ainsi pu utiliser davantage de cœurs, atteignant environ 8 cœurs occupés, tandis que le processus unique n'a jamais dépassé 1 cœur de travail. Les résultats montrent clairement que la mise à l'échelle de PgBouncer permet d'augmenter considérablement le débit et de mieux utiliser les ressources disponibles.