Tests de performance - 00 - Ton code marche, mais est-ce qu'il tient la charge ?

Introduction aux tests de performance : benchmark, charge, stress et endurance. Pourquoi les tests fonctionnels ne suffisent pas.

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

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.