Une approche systématique des tests de charge : des échecs de jetons aux métriques stables
Le backend, agissant comme un proxy entre le frontend et un service externe, mettait en cache un jeton d'accès via opcache. Sous charge, plusieurs requêtes parallèles détectaient simultanément un jeton expiré et tentaient de le rafraîchir. L'ancien jeton écrasait le nouveau dans le cache, entraînant des erreurs 403.
Solution : Nous avons ajouté un verrouillage pour le rafraîchissement du jeton et déplacé le cache vers memcached en cluster pour un état unifié entre les processus. Les tests dans Postman avec des requêtes parallèles ont confirmé que la condition de concurrence était éliminée.
Métriques avant : Le temps de réponse augmentait de façon exponentielle, le taux de réussite tombait en dessous de 90 % à 300 RPS. Après : Taux de réussite 100 %, erreurs 5xx <0,05 %, temps de réponse moyen 99-104 ms à 226 RPS.
Préparation du backend : Checklist d'optimisation
Avant les tests de charge (LT), un développeur backend suit une checklist systématique. Cela minimise les goulots d'étranglement au niveau du code et de l'infrastructure.
Optimisation de la base de données
- Indexer tous les champs dans les clauses WHERE et JOIN.
- Analyser EXPLAIN pour les requêtes lentes.
- Réplication maître-esclave, pooling de connexions (par exemple, pgbouncer ou équivalent).
Mise en cache
- Memcached pour les données répétitives.
- Cache des réponses HTTP dans Varnish ou nginx.
- Cache des modèles Twig.
Traitement asynchrone
- Files d'attente RabbitMQ ou Redis pour les tâches en arrière-plan.
- Déléguez l'envoi d'e-mails, la génération de PDF à des tâches en arrière-plan.
Code PHP
- Éliminer les requêtes N+1.
- Optimiser les boucles, éviter la récursion profonde.
- Libérer la mémoire avec unset() pour les gros objets.
Infrastructure
- php-fpm : pm.max_children > 100, pm.max_requests = 500.
- MinIO pour le stockage de fichiers.
- HPA dans Kubernetes basé sur CPU/RAM.
- Limiteur de débit (nginx ou Traefik) pour la limitation.
Frontend : Réduire la charge entrante
Le frontend génère le trafic principal. Les optimisations ici peuvent réduire le RPS backend de 30 à 50 %.
- Actifs statiques : cache nginx pour JS/CSS/images.
- Cache API côté client : localStorage ou memcached pour les données rarement modifiées (par exemple, catalogues, configurations).
- Pagination : Utiliser limit/offset ou le défilement infini au lieu des chargements complets de données.
- SSG/ISR : Next.js pour le rendu de pages statiques.
- SSE/Webhooks : Remplacer le polling par des notifications push.
Workflow des tests de charge
Les tests de charge sont obligatoires pour les fonctionnalités qui modifient la logique serveur, la mise en cache ou les bases de données. Effectuez-les sur staging/production la nuit pendant une charge utilisateur minimale.
- Planification (une semaine à l'avance) : Lead + PM + DevOps estiment le RPS cible (N requêtes/sec).
- Rôle DevOps : Configurer les pods (CPU/RAM), HPA, surveillance en temps réel.
- Exécution : Utiliser k6 ou similaire, avec des scénarios pour les endpoints API.
- Surveillance : Grafana (RPS, latence, taux d'erreur), Graylog pour les logs.
- Analyse : Identifier les points de dégradation, corriger les goulots d'étranglement, répéter.
Critères de réussite : Taux de réussite >99 %, latence <150 ms au RPS cible avec une marge de 20 %.
Points clés à retenir
- Les conditions de concurrence (par exemple, jetons, sessions) ne sont révélées que sous charge parallèle.
- Le cache en cluster (memcached) au lieu de l'opcache local empêche les conditions de concurrence.
- Tests de charge sur des environnements de type prod : réplication de base de données, services externes.
- DevOps dans la boucle : Ajuster dynamiquement les ressources pendant les tests.
- Les optimisations frontend réduisent la charge plus efficacement que les correctifs backend.
Métriques de réussite et benchmarks
Exemple de Grafana après les correctifs :
| Métrique | Moyenne | Pic | Cible |
|--------|---------|------|--------|
| Taux de réussite | 100% | 100% | >99% |
| Erreurs 5xx | 0,017% | 0,042% | <0,1% |
| RPS | 226 | 321 | 300+ |
| Latence (ms) | 99-104 | 123 | <150 |
Stabilisation à 226 RPS sans dégradation. Point de rupture : ~450 RPS (100 % de marge).
Recommandations pour les développeurs intermédiaires/seniors
- Intégrer les tests de charge dans CI/CD : tests automatisés lors de la fusion.
- Surveiller la latence p99, pas seulement la moyenne.
- Tester les scénarios du pire cas : 80 % du trafic sur un seul endpoint.
- Logger les IDs de requête pour le traçage (par exemple, Jaeger/OpenTelemetry).
Cette approche identifie les problèmes pendant le développement, réduisant les incidents en production.
— Editorial Team
Aucun commentaire pour le moment.