00 - Ton code marche, mais est-ce qu'il tient la charge ?
Ce que tu vas apprendre
- La différence entre "ca marche" et "ca tient la charge"
- Les 4 types de tests de performance et quand les utiliser
- Les metriques a connaître : latence, throughput, percentiles
Prerequisites
Avoir un backend qui tourne (n'importe quelle techno). Pour les exemples de la serie, on utilisera Bun/Elysia et k6.
L'API qui marchait tres bien
Février 2025. Je venais de déployer une nouvelle API sur paltemps.fr pour gerer les flux RSS. En dev, tout fonctionnait parfaitement. Les tests unitaires passaient (j'en parle dans la serie Tests fondamentaux). Les tests d'intégration aussi. J'etais confiant.
Le lundi matin, 47 utilisateurs se connectent en meme temps. Le temps de réponse passe de 80ms a 12 secondes. Puis les timeouts arrivent. Puis les erreurs 502. Caddy renvoyait des pages d'erreur parce que Elysia ne repondait plus du tout.
Le problème ? Une requête SQL non optimisee qui faisait un full scan sur une table de 200 000 lignes. A 1 utilisateur, ca prend 80ms. A 50 utilisateurs simultanes, le pool de connexions PostgreSQL sature, les requêtes s'empilent, et tout s'effondre.
Un test de charge de 2 minutes aurait montre le problème. Je ne l'avais pas fait.
"Ca marche" n'est pas "ca tient"
Les tests fonctionnels verifient que ton code fait ce qu'il doit faire. Tu envoies une requête, tu verifies la réponse. Un utilisateur a la fois.
Mais en prod, c'est jamais un utilisateur a la fois. C'est 10, 50, 200 personnes qui tapent sur la meme API au meme moment. Et la, des problèmes invisibles apparaissent : contention sur les connexions base de donnees, allocation mémoire qui explose, CPU qui sature sur du JSON.parse() en boucle.
Les tests de performance repondent a une question différente : pas "est-ce que ca marche ?", mais "est-ce que ca marche quand c'est sollicite ?".
Les 4 types de tests de performance
Benchmark (micro-benchmark) : tu mesures une fonction isolee. Combien de temps met JSON.parse() sur un objet de 10 KB ? Combien de mémoire consomme ta fonction de tri ? C'est du test au niveau du code, pas du réseau. On verra ca dans l'article 01 avec Bun et sur benchmark.paltemps.fr.
Test de charge (load test) : tu simules le trafic attendu en production. Si tu attends 100 utilisateurs simultanes, tu lances 100 virtual users pendant 10 minutes et tu regardes ce qui se passe. L'objectif : vérifier que ton système tient le trafic normal.
Test de stress : tu vas au-delà des limites. Tu augmentes la charge jusqu'a ce que ca casse. L'objectif n'est pas que ca tienne, c'est de trouver le point de rupture. A combien de requêtes par seconde ton serveur commence a renvoyer des erreurs ? Quand est-ce que la latence explose ?
Test d'endurance (soak test) : tu maintiens une charge moderee pendant longtemps. 2 heures, 12 heures, 24 heures. L'objectif : détecter les fuites lentes. Une fuite mémoire de 500 KB par heure, tu ne la vois pas en 5 minutes. Mais en 48 heures, c'est 24 MB de perdu, et ton process finit par etre kill par l'OOM killer.
Les metriques a connaître
Latence : le temps entre l'envoi de la requête et la reception de la réponse. En millisecondes. C'est ce que l'utilisateur ressent directement.
Throughput : le nombre de requêtes traitees par seconde (req/s ou RPS). Un serveur qui fait 1 000 req/s avec 50ms de latence moyenne, ca va. Un serveur qui fait 100 req/s avec 3 secondes de latence, ca va beaucoup moins.
Taux d'erreur : le pourcentage de requêtes qui echouent (5xx, timeouts). En dessous de 0.1%, c'est acceptable. Au-dessus de 1%, il y a un problème.
Percentiles : la latence moyenne ment. Si 99 requêtes prennent 50ms et 1 requête prend 10 secondes, la moyenne est a 149ms. Ca a l'air correct. Mais 1% de tes utilisateurs attendent 10 secondes. C'est pour ca qu'on utilise les percentiles :
- p50 (mediane) : 50% des requêtes sont en dessous de cette valeur
- p95 : 95% des requêtes sont en dessous. C'est la metrique la plus utile au quotidien.
- p99 : 99% des requêtes. Le "pire cas raisonnable".
Quand quelqu'un te dit "mon API répond en 50ms", demande-lui "p50 ou p99 ?". La différence est souvent un facteur 10.
Les outils de cette serie
Pour le micro-benchmarking, on utilise Bun et performance.now(). Pour les tests de charge HTTP, on utilise k6 par Grafana Labs. k6 est open source, scriptable en JavaScript, et donne des résultats propres avec les percentiles directement dans le terminal.
On n'utilisera pas JMeter (trop lourd, interface Java des annees 2000). On n'utilisera pas ab (Apache Bench) parce qu'il ne supporte pas les scénarios complexes. k6 fait tout ce dont on a besoin.
Ce que couvre cette serie
| # | Article | Contenu |
|---|---|---|
| 00 | Introduction | Ce que tu lis maintenant |
| 01 | Benchmark Bun | Mesurer CPU et mémoire d'une fonction |
| 02 | k6 : premier test | Installer k6, écrire un test de charge |
| 03 | Scénarios k6 | Ramping, spike, soak avec k6 |
| 04 | Stress test | Trouver le point de rupture |
| 05 | Profiling mémoire | Détecter les fuites |
| 06 | Timeouts et resilience | Gerer les pannes réseau |
| 07 | Soak tests | Endurance longue duree |
| 08 | Comparatif outils | k6 vs Artillery vs Autocannon vs wrk |
| 09 | Glossaire | Tous les termes expliques |
Si tu viens de la serie Tests fondamentaux, tu connais deja les bases du testing. Les tests de performance, c'est la couche suivante : pas "est-ce que ca marche ?", mais "est-ce que ca tient ?".
Article suivant : 01 - Benchmark CPU et mémoire avec Bun