devops
Ce qu'un test de charge te dit vraiment
Ce qu'un test de charge te dit vraiment
Sans lui, « est-ce que ça encaisse du vrai trafic » est une supposition basée sur le ressenti de l'application quand je clique — ce qui ne me dit rien du comportement en simultané ni d'où la latence commence vraiment à grimper. k6 scripte une montée d'utilisateurs virtuels contre de vrais endpoints et rapporte des latences par centile — p95/p99, pas une moyenne qui cache la queue que les gens vivent réellement — plus le débit et le taux d'échec.
export const options = {
scenarios: { ramp: { executor: "ramping-vus", stages: [
{ duration: "30s", target: 20 }, { duration: "1m", target: 100 },
]}},
thresholds: { "http_req_duration{endpoint:api_get}": ["p(95)<300"] },
};
L'erreur qui n'est pas évidente jusqu'à ce qu'elle arrive
Tout script avec un setup() non trivial — connecter un pool de comptes de test — voit ces requêtes de connexion atterrir dans la même métrique de latence que les requêtes réellement testées, par défaut.
Une connexion fait un vrai bcrypt.compare, délibérément lent. Si le setup connecte 20 comptes et que le vrai test lance quelques centaines de requêtes tôt, ces 20 connexions lentes tirent le p95 agrégé vers un nombre qui a l'air alarmant et ne veut rien dire sur l'endpoint que je mesure. Ma première vraie campagne montrait un p95 de 229 ms pour de simples GET sur une stack sans charge notable, et il m'a fallu une minute pour comprendre pourquoi.
Le correctif : étiquette séparément les requêtes de setup et les requêtes de test, cadre les seuils sur l'étiquette qui compte. Peu coûteux à faire, facile à oublier, et le premier symptôme est un nombre déroutant, pire que la réalité.
Quand une charge plus réaliste a empiré les chiffres
Retirer le temps de réflexion artificiel entre les itérations devrait naïvement juste signifier plus de requêtes et des chiffres proportionnellement pires. Ce qui s'est réellement passé : le débit a chuté pendant que la latence montait, simultanément. Pas un échange proportionnel — proche d'une empreinte pour un goulet d'étranglement mono-thread saturé, où les requêtes font la queue derrière un seul thread qui ne peut pas y arriver plus vite peu importe combien attendent.
Je l'ai confirmé directement avec docker stats pendant la campagne : un cœur collé près de 100 %, tout le reste inactif. C'est ça qui a transformé « la latence a empiré » en « ce processus précis est le goulet d'étranglement » — la seule version du constat qui te dit quoi corriger. (C'est aussi ce qui a mené au clustering PM2.)
Ce que ce n'est pas
Faire tourner le générateur de charge et le système testé sur la même machine biaise le résultat — ils se disputent les mêmes cœurs. Les chiffres absolus en millisecondes sont pessimistes ; la forme du constat — où la latence commence à grimper, si retirer le rythme aide ou nuit — reste informative. Ce n'est pas un substitut à un vrai benchmark de capacité sur du matériel équivalent à la production, ni à définir d'abord ce que « simultané » signifie pour ton vrai trafic. Mille onglets ouverts inactifs et mille personnes qui soumettent le même formulaire en même temps partagent un chiffre de titre et rien d'autre.