🔝 Retour au Sommaire
Dans les architectures serverless, la gestion des connexions à la base de données représente un défi majeur. Chaque fonction serverless qui s'exécute a tendance à ouvrir sa propre connexion, ce qui peut rapidement saturer PostgreSQL.
Ce chapitre explique pourquoi le connection pooling est indispensable en environnement serverless, et comment utiliser les deux solutions les plus répandues : PgBouncer et AWS RDS Proxy.
Pour comprendre le problème, commençons par les bases. Quand une application se connecte à PostgreSQL :
- Le client envoie une demande de connexion
- PostgreSQL crée un nouveau processus dédié à cette connexion (fork)
- L'authentification est vérifiée (mot de passe, certificat, etc.)
- La connexion est établie et prête à recevoir des requêtes
- À la déconnexion, le processus est détruit
Chaque connexion consomme des ressources :
| Ressource | Consommation par connexion |
|---|---|
| Mémoire RAM | ~5-10 MB minimum |
| Processus système | 1 processus dédié |
| File descriptors | Plusieurs par connexion |
| Temps CPU | Pour la création/destruction |
Dans une architecture serverless (AWS Lambda, Google Cloud Functions, Azure Functions, Vercel, etc.), le comportement est fondamentalement différent d'une application traditionnelle :
Architecture Traditionnelle (Serveur)
─────────────────────────────────────
┌─────────────────────┐
│ Application │
│ (1 instance) │
│ │
│ ┌───────────────┐ │
│ │ Pool de 10 │ │──────► PostgreSQL (10 connexions)
│ │ connexions │ │
│ └───────────────┘ │
└─────────────────────┘
Architecture Serverless
───────────────────────
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Lambda │ │ Lambda │ │ Lambda │ │ Lambda │
│ #1 │ │ #2 │ │ #3 │ │ ... │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────┐
│ PostgreSQL │
│ (potentiellement 100+ connexions) │
└─────────────────────────────────────────┘
Les fonctions serverless présentent plusieurs caractéristiques qui amplifient le problème :
Lors d'un pic de trafic, des dizaines ou centaines de fonctions peuvent démarrer simultanément :
Trafic normal : 10 fonctions → 10 connexions
Pic de trafic : 500 fonctions → 500 connexions !
Chaque fonction :
- Démarre (cold start)
- Ouvre une connexion
- Exécute 1-2 requêtes
- Se termine (ou reste "warm" quelques minutes)
Ce cycle constant de création/destruction est très coûteux.
PostgreSQL a une limite configurable de connexions simultanées :
| Configuration | Valeur typique |
|---|---|
| max_connections (défaut) | 100 |
| RDS db.t3.micro | ~85 |
| RDS db.r5.large | ~1 000 |
Dépasser cette limite provoque des erreurs :
FATAL: too many connections for role "myuser"
FATAL: sorry, too many clients already
Même sans atteindre la limite, trop de connexions dégradent les performances :
- Mémoire saturée : Moins de RAM pour le cache (shared_buffers)
- Contention CPU : Trop de processus concurrents
- Context switching : Le système passe son temps à jongler entre processus
Un connection pooler est un intermédiaire entre les applications et la base de données qui :
- Maintient un pool de connexions vers PostgreSQL
- Accepte les connexions des clients (en nombre illimité ou très élevé)
- Réutilise les connexions existantes au lieu d'en créer de nouvelles
- Réduit la charge sur PostgreSQL
SANS Connection Pooler
──────────────────────
┌─────────┐
│ Client 1│────┐
└─────────┘ │
┌─────────┐ │ ┌────────────┐
│ Client 2│────┼────►│ PostgreSQL │ 100 connexions directes
└─────────┘ │ │ │ = 100 processus
┌─────────┐ │ └────────────┘
│ Client N│────┘
└─────────┘
AVEC Connection Pooler
──────────────────────
┌─────────┐
│ Client 1│────┐
└─────────┘ │ ┌──────────────┐ ┌────────────┐
┌─────────┐ │ │ │ │ │
│ Client 2│────┼────►│ PgBouncer │─────►│ PostgreSQL │
└─────────┘ │ │ (pooler) │ │ │
┌─────────┐ │ │ │ └────────────┘
│ Client N│────┘ └──────────────┘
└─────────┘ 1000 clients 20 connexions
→ 20 connexions = 20 processus
| Aspect | Sans pooler | Avec pooler |
|---|---|---|
| Connexions à PG | = nombre de clients | Fixe et contrôlé |
| Temps de connexion | ~50-200ms | ~1-5ms |
| Mémoire PostgreSQL | Élevée | Optimisée |
| Stabilité | Risque de saturation | Prévisible |
PgBouncer est le connection pooler le plus populaire pour PostgreSQL. Créé en 2007, il est léger, fiable et éprouvé en production par des milliers d'entreprises.
- Léger : Consomme très peu de ressources (~2 KB par connexion)
- Rapide : Écrit en C, performances optimales
- Configurable : Trois modes de pooling différents
- Mature : Plus de 15 ans de développement
PgBouncer propose trois modes de fonctionnement, chacun avec ses avantages et contraintes :
┌────────────────────────────────────────────────────────────┐
│ SESSION POOLING │
├────────────────────────────────────────────────────────────┤
│ │
│ Client A ═══════════════════════════════► Connexion PG 1 │
│ (toute la session) │
│ │
│ Client B ═══════════════════════════════► Connexion PG 2 │
│ (toute la session) │
│ │
│ La connexion PG est réservée tant que le client │
│ est connecté à PgBouncer │
│ │
└────────────────────────────────────────────────────────────┘
Fonctionnement : Une connexion PostgreSQL est assignée à un client pour toute la durée de sa session.
| Avantages | Inconvénients |
|---|---|
| Toutes les fonctionnalités PG | Peu d'économie de connexions |
| Prepared statements | Pas adapté au serverless |
| Variables de session | Limite = pool_size |
Cas d'usage : Applications traditionnelles qui utilisent des fonctionnalités de session.
┌────────────────────────────────────────────────────────────┐
│ TRANSACTION POOLING │
├────────────────────────────────────────────────────────────┤
│ │
│ Client A ──[BEGIN...COMMIT]──► Connexion PG 1 │
│ │ │
│ Client B ──[requête simple]─────────┘ (réutilise PG 1) │
│ │ │
│ Client A ──[BEGIN...COMMIT]─────────┘ (réutilise PG 1) │
│ │
│ La connexion PG est libérée après chaque transaction │
│ │
└────────────────────────────────────────────────────────────┘
Fonctionnement : Une connexion PostgreSQL n'est réservée que pendant une transaction. Entre les transactions, elle peut servir à d'autres clients.
| Avantages | Inconvénients |
|---|---|
| Excellent ratio clients/connexions | Pas de prepared statements* |
| Idéal pour serverless | Pas de variables de session |
| Très efficace | Pas de LISTEN/NOTIFY |
*Note : PostgreSQL 14+ supporte les prepared statements en mode transaction avec protocol_native_password.
Cas d'usage : Applications serverless, microservices, API REST.
┌────────────────────────────────────────────────────────────┐
│ STATEMENT POOLING │
├────────────────────────────────────────────────────────────┤
│ │
│ Client A ──[SELECT 1]──► Connexion PG 1 │
│ │ │
│ Client B ──[SELECT 2]──────────┘ (immédiatement) │
│ │ │
│ Client A ──[SELECT 3]──────────┘ (immédiatement) │
│ │
│ La connexion est libérée après CHAQUE requête │
│ ⚠️ Les transactions multi-requêtes sont INTERDITES │
│ │
└────────────────────────────────────────────────────────────┘
Fonctionnement : La connexion est libérée après chaque requête individuelle.
| Avantages | Inconvénients |
|---|---|
| Ratio maximal | Pas de transactions ! |
| Ultra-efficace | Usage très limité |
Cas d'usage : Workloads très spécifiques avec uniquement des requêtes autonomes.
| Fonctionnalité | Session | Transaction | Statement |
|---|---|---|---|
| Transactions | ✅ | ✅ | ❌ |
| Prepared Statements | ✅ | ❌ | |
| SET / Variables | ✅ | ❌ | ❌ |
| LISTEN/NOTIFY | ✅ | ❌ | ❌ |
| Advisory Locks | ✅ | ❌ | ❌ |
| Curseurs WITH HOLD | ✅ | ❌ | ❌ |
| Efficacité pooling | Faible | Haute | Maximale |
| Adapté serverless | ❌ | ✅ |
PgBouncer peut être déployé de plusieurs façons :
┌─────────────────────────────────┐
│ Serveur App │
│ ┌───────────┐ ┌────────────┐ │ ┌────────────┐
│ │ App │─►│ PgBouncer │──┼─────►│ PostgreSQL │
│ └───────────┘ └────────────┘ │ └────────────┘
│ localhost:6432 │
└─────────────────────────────────┘
Avantages : Latence minimale, simple à configurer.
┌─────────┐
│ App 1 │────┐
└─────────┘ │ ┌────────────┐ ┌────────────┐
┌─────────┐ ├────►│ PgBouncer │─────►│ PostgreSQL │
│ App 2 │────┤ │ (central) │ └────────────┘
└─────────┘ │ └────────────┘
┌─────────┐ │
│ App N │────┘
└─────────┘
Avantages : Gestion centralisée, un seul point de configuration.
Les plateformes serverless PostgreSQL intègrent souvent PgBouncer :
┌─────────────────────────────────────────────────────┐
│ Neon / Supabase │
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ PgBouncer │─────►│ PostgreSQL │ │
│ │ (intégré) │ │ │ │
│ └────────────┘ └────────────┘ │
│ ▲ │
└────────┼────────────────────────────────────────────┘
│
Connexion poolée automatique
Voici les paramètres les plus importants du fichier pgbouncer.ini :
[databases]
; Format : nom_logique = connexion_string
mydb = host=localhost port=5432 dbname=mydb
; Avec authentification
mydb = host=db.example.com port=5432 dbname=production user=app_user password=secret[pgbouncer]
; Adresse d'écoute
listen_addr = 0.0.0.0
listen_port = 6432
; Mode de pooling (session, transaction, statement)
pool_mode = transaction
; Taille du pool par utilisateur/base
default_pool_size = 20
; Connexions supplémentaires si le pool est plein
reserve_pool_size = 5
; Temps d'attente max pour une connexion du pool (secondes)
reserve_pool_timeout = 3
; Nombre max de connexions clients
max_client_conn = 1000
; Nombre max de connexions vers PostgreSQL (toutes bases)
max_db_connections = 50
; Authentification
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
; Logs
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1 ; Format : "utilisateur" "mot_de_passe_hashé_ou_clair"
"app_user" "scram-sha-256$4096:..."
"readonly_user" "scram-sha-256$4096:..."
La question cruciale : combien de connexions configurer ?
pool_size = (nombre_coeurs_cpu × 2) + nombre_disques
Pour un serveur avec 4 cœurs et 1 SSD :
pool_size = (4 × 2) + 1 = 9 connexions
| Scénario | pool_size recommandé |
|---|---|
| Petit projet (1-2 vCPU) | 10-20 |
| Application moyenne (4 vCPU) | 20-50 |
| Charge importante (8+ vCPU) | 50-100 |
| Serverless intense | 20-30 (+ reserve) |
"Plus de connexions = meilleures performances"
FAUX ! Trop de connexions parallèles créent de la contention. PostgreSQL est souvent plus performant avec 20 connexions actives qu'avec 200.
PgBouncer expose des statistiques via une base virtuelle pgbouncer :
-- Se connecter à la base admin
psql -h localhost -p 6432 -U admin pgbouncer
-- Voir les pools actifs
SHOW POOLS;
-- Statistiques par base
SHOW STATS;
-- Connexions clients
SHOW CLIENTS;
-- Connexions serveur (vers PostgreSQL)
SHOW SERVERS;
-- Configuration actuelle
SHOW CONFIG;| Métrique | Description | Seuil d'alerte |
|---|---|---|
| cl_active | Clients actifs | - |
| cl_waiting | Clients en attente | > 0 prolongé |
| sv_active | Connexions PG actives | Proche de pool_size |
| sv_idle | Connexions PG inactives | Devrait être > 0 |
| maxwait | Temps d'attente max | > 1 seconde |
AWS RDS Proxy est un service de connection pooling entièrement géré par Amazon Web Services. Il est conçu spécifiquement pour les architectures serverless AWS.
AWS a créé RDS Proxy pour résoudre le problème spécifique des Lambda + RDS :
PROBLÈME CLASSIQUE AWS
──────────────────────
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Lambda │ │ Lambda │ │ Lambda │ Pic de trafic :
│ #1 │ │ #2 │ │ #100 │ 100 Lambdas
└────┬────┘ └────┬────┘ └────┬────┘ = 100 connexions
│ │ │
└───────────┴───────────┘
│
▼
┌───────────────────────┐
│ RDS PostgreSQL │ ← ERREUR: too many connections
│ (max_connections: │
│ 87) │
└───────────────────────┘
SOLUTION AVEC RDS PROXY
───────────────────────
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Lambda │ │ Lambda │ │ Lambda │ 1000+ Lambdas
│ #1 │ │ #2 │ │ #1000 │ supportées
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└───────────┴───────────┘
│
▼
┌───────────────────────┐
│ RDS Proxy │ ← Gère la file d'attente
│ (pooling + queue) │
└───────────┬───────────┘
│
▼
┌───────────────────────┐
│ RDS PostgreSQL │ ← Seulement 20-50 connexions
└───────────────────────┘
- Pas de serveur à provisionner
- Scaling automatique
- Haute disponibilité native (multi-AZ)
- Maintenance automatique
- IAM Authentication : Authentification via rôles IAM (pas de mots de passe)
- Secrets Manager : Rotation automatique des credentials
- VPC : Fonctionne dans votre réseau privé
- CloudWatch : Métriques et logs intégrés
RDS Proxy améliore la résilience :
SANS RDS Proxy (Failover classique)
───────────────────────────────────
1. Instance primaire tombe
2. Failover vers standby (~30-60s)
3. Applications : connexions cassées
4. Applications : doivent reconnecter
5. Erreurs pendant la transition
AVEC RDS Proxy
──────────────
1. Instance primaire tombe
2. RDS Proxy détecte la panne
3. RDS Proxy maintient les connexions clients
4. Failover vers standby
5. RDS Proxy reconnecte automatiquement
6. Applications : voient un léger délai, pas d'erreur
- Une instance RDS PostgreSQL ou Aurora PostgreSQL
- Un secret dans AWS Secrets Manager avec les credentials
- Un VPC avec les security groups appropriés
-
Accéder à RDS → Proxies → Create proxy
-
Configuration de base :
- Nom du proxy
- Engine : PostgreSQL
- Require TLS : Recommandé
-
Target group :
- Base de données cible (votre RDS/Aurora)
- Connection pool configuration :
- Max connections : % de max_connections de la DB
-
Authentification :
- Secret ARN depuis Secrets Manager
- IAM authentication : Optionnel mais recommandé
-
Connectivity :
- VPC et subnets
- Security groups
resource "aws_db_proxy" "main" {
name = "my-postgres-proxy"
debug_logging = false
engine_family = "POSTGRESQL"
idle_client_timeout = 1800
require_tls = true
vpc_subnet_ids = var.private_subnet_ids
vpc_security_group_ids = [aws_security_group.proxy.id]
auth {
auth_scheme = "SECRETS"
iam_auth = "REQUIRED"
secret_arn = aws_secretsmanager_secret.db_credentials.arn
}
}
resource "aws_db_proxy_default_target_group" "main" {
db_proxy_name = aws_db_proxy.main.name
connection_pool_config {
max_connections_percent = 50
max_idle_connections_percent = 10
connection_borrow_timeout = 120
}
}
resource "aws_db_proxy_target" "main" {
db_proxy_name = aws_db_proxy.main.name
target_group_name = aws_db_proxy_default_target_group.main.name
db_instance_identifier = aws_db_instance.main.identifier
}RDS Proxy fournit un endpoint dédié :
my-proxy.proxy-xxxxxxxxxxxx.region.rds.amazonaws.com
import psycopg2
conn = psycopg2.connect(
host="my-proxy.proxy-xxxx.us-east-1.rds.amazonaws.com",
port=5432,
database="mydb",
user="app_user",
password="from_secrets_manager",
sslmode="require"
)import boto3
import psycopg2
# Générer un token d'authentification IAM
client = boto3.client('rds')
token = client.generate_db_auth_token(
DBHostname='my-proxy.proxy-xxxx.us-east-1.rds.amazonaws.com',
Port=5432,
DBUsername='iam_user'
)
conn = psycopg2.connect(
host="my-proxy.proxy-xxxx.us-east-1.rds.amazonaws.com",
port=5432,
database="mydb",
user="iam_user",
password=token, # Le token IAM comme mot de passe
sslmode="require"
)| Paramètre | Description | Valeur recommandée |
|---|---|---|
| max_connections_percent | % de max_connections de la DB à utiliser | 50-90% |
| max_idle_connections_percent | % de connexions idle à maintenir | 10-50% |
| connection_borrow_timeout | Temps max d'attente pour une connexion (sec) | 120 |
| idle_client_timeout | Timeout des connexions client idle (sec) | 1800 |
RDS Proxy expose des métriques dans CloudWatch :
| Métrique | Description | Action si problème |
|---|---|---|
| ClientConnections | Nombre de connexions clients | Normal, informatif |
| ClientConnectionsNoTLS | Connexions sans TLS | Devrait être 0 |
| DatabaseConnections | Connexions vers RDS | Surveiller vs max |
| DatabaseConnectionsBorrowLatency | Temps pour obtenir une connexion | < 100ms idéal |
| QueryRequests | Nombre de requêtes | Baseline du trafic |
| QueryDatabaseResponseLatency | Latence des requêtes | Détecter les lenteurs |
# Exemple CloudWatch Alarm (pseudo-YAML)
Alarms:
- Name: HighBorrowLatency
Metric: DatabaseConnectionsBorrowLatency
Threshold: 1000 # ms
Action: Augmenter pool ou optimiser requêtes
- Name: PoolExhaustion
Metric: DatabaseConnections
Threshold: 90% # de max_connections_percent
Action: Augmenter % ou scaler la DBRDS Proxy est facturé à l'heure par vCPU provisionné :
| Élément | Coût approximatif |
|---|---|
| Par vCPU/heure | ~$0.015 |
| Proxy minimal | 2 vCPU = ~$22/mois |
| Données transférées | Inclus |
Le coût dépend de la région AWS et peut évoluer. Consultez la page de tarification officielle.
| Aspect | PgBouncer | RDS Proxy |
|---|---|---|
| Type | Open source, self-managed | Service managé AWS |
| Coût | Gratuit (+ infra) | Payant (~$22+/mois) |
| Modes de pooling | Session, Transaction, Statement | Transaction-like |
| Configuration | Fichier texte, très flexible | Console/API, options limitées |
| Haute disponibilité | À configurer soi-même | Intégrée (multi-AZ) |
| Authentification | Fichier userlist, LDAP | IAM, Secrets Manager |
| Intégration Lambda | Possible mais manuel | Native et optimisée |
| Failover | Manuel ou via orchestrateur | Automatique et transparent |
| Métriques | SHOW commands, custom | CloudWatch natif |
| Prepared statements | Configurable | Supportés |
| Support | Communauté | Support AWS |
✅ Choisissez PgBouncer si :
- Vous n'êtes pas sur AWS ou utilisez plusieurs clouds
- Vous voulez un contrôle total sur la configuration
- Le budget est serré (PgBouncer est gratuit)
- Vous avez l'expertise pour gérer l'infrastructure
- Vous avez besoin du mode
sessionoustatement - Votre PostgreSQL n'est pas sur RDS/Aurora
✅ Choisissez RDS Proxy si :
- Vous êtes sur AWS avec RDS ou Aurora PostgreSQL
- Vous utilisez intensivement AWS Lambda
- Vous voulez une solution sans maintenance
- L'authentification IAM est importante pour vous
- Vous avez besoin d'un failover transparent
- Le coût supplémentaire est acceptable
Dans certains cas, vous pouvez combiner les deux :
┌─────────────────────────────────────────────────────────────┐
│ Architecture Hybride │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Lambda │────────────┐ │
│ │ Functions │ │ │
│ └─────────────┘ ▼ │
│ ┌────────────┐ ┌──────────────┐ │
│ ┌─────────────┐ │ RDS Proxy │─────►│ │ │
│ │ ECS / │────►│ │ │ PostgreSQL │ │
│ │ EKS │ └────────────┘ │ (RDS) │ │
│ └─────────────┘ │ │ │
│ └──────────────┘ │
│ ┌─────────────┐ ┌────────────┐ ▲ │
│ │ EC2 / │────►│ PgBouncer │────────────┘ │
│ │ On-prem │ │ (self-mgd) │ │
│ └─────────────┘ └────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
- Alternative moderne à PgBouncer
- Multi-threading (meilleure utilisation CPU)
- Développé par Yandex
- Open source
- Plus qu'un pooler : load balancing, réplication
- Plus complexe à configurer
- Fonctionnalités avancées (cache de requêtes)
| Plateforme | Solution intégrée |
|---|---|
| Neon | PgBouncer intégré + Neon Proxy |
| Supabase | Supavisor (PgBouncer-based) |
| Azure | Azure Database Proxy (preview) |
| Google Cloud | Cloud SQL Proxy (+ Auth Proxy) |
Même pour de petits projets, un pooler évite les surprises lors des montées en charge.
# PgBouncer
server_connect_timeout = 15
server_idle_timeout = 600
query_timeout = 300 Les fonctions serverless ont des timeouts courts ; configurez le pooler en conséquence.
# ❌ MAUVAIS : Connexion globale qui peut expirer
conn = psycopg2.connect(...)
def handler(event, context):
cursor = conn.cursor() # Peut échouer si connexion morte
...
# ✅ BON : Connexion par invocation avec gestion d'erreur
def handler(event, context):
conn = psycopg2.connect(...)
try:
cursor = conn.cursor()
...
finally:
conn.close()Le mode transaction offre le meilleur équilibre efficacité/compatibilité.
Configurez des alertes pour :
- Connexions en attente
- Temps d'obtention de connexion
- Erreurs de connexion
Avant la production, simulez des pics de trafic pour valider le dimensionnement.
Le connection pooling est indispensable dans les architectures serverless. Sans lui, vous risquez des erreurs de connexion, des performances dégradées et une instabilité générale.
En résumé :
-
PgBouncer est la solution universelle, gratuite et flexible. Idéale si vous gérez votre infrastructure ou n'êtes pas sur AWS.
-
RDS Proxy est la solution "clé en main" pour l'écosystème AWS. Plus coûteuse mais sans maintenance et parfaitement intégrée.
Le choix dépend de votre contexte : cloud provider, budget, expertise interne et besoins spécifiques. Dans tous les cas, l'investissement dans un connection pooler sera rapidement rentabilisé par la stabilité et les performances gagnées.
- PgBouncer : Configuration avancée et tuning
- RDS Proxy : Best practices pour Lambda
- Comparaison des poolers PostgreSQL
- pgbench : Tests de charge PostgreSQL
- Artillery : Tests de charge applicatifs
- CloudWatch : Monitoring RDS Proxy