Coordinated omission : ce que c'est et comment l'éviter
7 min de lecture
La coordinated omission est une erreur de mesure : un générateur de charge attend chaque réponse lente avant d’envoyer sa requête suivante, il envoie donc moins de requêtes au moment même où le système ralentit et enregistre trop peu des mauvais moments. Les percentiles que vous lisez ensuite paraissent alors meilleurs que ce que les utilisateurs ont vécu. Pour l’éviter, envoyez les requêtes selon un calendrier qui ignore les temps de réponse et mesurez chaque requête à partir du moment où elle aurait dû partir.
Les informations sur les outils viennent de la documentation ou du dépôt de chaque projet, vérifiées le 9 octobre 2026.
| Outil | Ce qu’il propose | Ce qu’il faut vérifier |
|---|---|---|
| wrk2 | Un taux constant (-R), latence mesurée depuis l’envoi prévu | --u_latency ajoute la vue non corrigée |
| HdrHistogram | Deux méthodes qui comblent les mesures manquantes | En appliquer une seule, jamais les deux |
| k6 | Les executors à taux d’arrivée | Le compteur dropped_iterations |
| Gatling | Des étapes injectOpen comme constantUsersPerSec | Les étapes sont dans injectOpen |
| Locust | constant_throughput cadence chaque utilisateur | Le taux atteint par rapport à la cible |
| JMeter | Open Model Thread Group (5.5 et plus, expérimental) | Un Thread Group classique est un pool fermé |
Qu’est-ce que la coordinated omission ?
C’est l’erreur qui survient quand le côté qui mesure cesse de prendre des mesures parce que le système est lent, si bien que les mesures qui manquent sont précisément les lentes. Perdre des mesures au hasard bouge à peine un percentile ; les perdre au rythme d’un ralentissement les fausse tous.
Le nom vient de Gil Tene, dont un message de 2013 sur une liste de diffusion trace exactement cette frontière. Il est l’auteur d’origine d’HdrHistogram, wrk2 se trouve sur son compte GitHub, et la documentation des deux projets explique l’effet (vérifié le 9 octobre 2026). La documentation de k6 emploie le terme pour un modèle de charge fermé qui freine sa propre charge, une cause fréquente. Notre article sur le modèle de charge ouvert ou fermé traite ce choix ; celui-ci s’en tient à la mesure.
Comment un test de charge finit-il par masquer les réponses lentes ?
Un générateur en boucle fermée n’envoie la requête suivante sur une connexion qu’après l’arrivée de la réponse précédente. Si une réponse prend une seconde, cette connexion n’envoie rien pendant une seconde, et les requêtes qu’elle aurait envoyées n’obtiennent jamais de temps de réponse.
Le README de wrk2 le dit du modèle de wrk, qui mesure chaque requête du premier octet envoyé à la réponse complète : comme une connexion n’envoie qu’après une réponse, le générateur se coordonne avec le serveur et évite de mesurer pendant les périodes de forte latence (vérifié le 9 octobre 2026).
Le README d’HdrHistogram propose une expérience de pensée. Un système mesuré toutes les 10 ms répond en 1 ms pendant 100 secondes, puis se fige pendant 100 secondes, ce qui donne une seule mesure de 100 secondes. L’histogramme brut place environ 99,99 % des résultats à 1 ms ou moins ; corrigé des mesures bloquées, il n’en place qu’environ la moitié.
Comment savoir si vos résultats souffrent de coordinated omission ?
Comparez le taux envoyé au taux prévu. Si le taux de requêtes a baissé pendant que les temps de réponse montaient, le générateur a levé le pied et la période lente est sous-représentée.
- La charge était définie en utilisateurs ou en connexions, sans taux. N’importe quelle boucle fermée peut produire l’erreur.
- Le taux de requêtes baisse là où les temps de réponse montent. Un creux du taux pendant un pic signifie que des requêtes ne sont jamais parties.
- Le maximum écrase le 99e percentile. Quelques mesures énormes parmi des milliers de rapides, c’est l’exemple ci-dessus en miniature. Lisez toute la distribution des temps de réponse, en gardant à l’esprit qu’elle ne compte que les requêtes envoyées.
- Le générateur de charge manquait de CPU. LoadFocus documente qu’un moteur proche de 100 % de CPU ne peut pas envoyer au taux configuré, et ses résultats affichent un avertissement de saturation du générateur de charge quand un moteur a atteint ou dépassé 80 % de CPU en pointe.
Comment wrk2 évite-t-il la coordinated omission ?
Il envoie à taux constant et mesure chaque requête à partir du moment où le plan prévoyait son départ. Une requête partie en retard parce que sa connexion était occupée se voit compter l’attente.
wrk2 ajoute à wrk un argument de taux (-R, requêtes par seconde au total) et enregistre les latences dans un HdrHistogram. --u_latency ajoute au rapport l’histogramme non corrigé, marqué CO, comme dans l’exemple du README :
wrk -t2 -c100 -d30s -R2000 --u_latency http://127.0.0.1:80/index.html
Dans le second exemple du README, une pause du serveur de 1,4 seconde pendant un test de 30 secondes rend le 99e percentile corrigé 200 fois plus élevé que le non corrigé. Le README qualifie aussi le projet d’expérimental et prévient que des tests de moins de 10 à 20 secondes risquent de ne rien apprendre (vérifié le 9 octobre 2026). Notre revue des outils de benchmark HTTP compare wrk2 à hey, oha et d’autres.
Comment HdrHistogram corrige-t-il les latences enregistrées ?
Si vous connaissez l’intervalle auquel les requêtes devaient partir, il ajoute les mesures qu’une longue réponse a bloquées. La correction se fait à l’enregistrement ou après coup, sur une copie.
recordValueWithExpectedInterval enregistre une valeur et, si elle dépasse l’intervalle, ajoute des valeurs qui décroissent par pas de cet intervalle. copyCorrectedForCoordinatedOmission renvoie une copie corrigée d’un histogramme terminé. La JavaDoc présente les deux méthodes comme mutuellement exclusives (vérifié le 9 octobre 2026).
// intervalle prévu : 10 ms, valeurs en microsecondes
Histogram h = new Histogram(3);
h.recordValueWithExpectedInterval(latencyUs, 10_000);
// ou, après le test, sur un histogramme brut :
Histogram fixed = raw.copyCorrectedForCoordinatedOmission(10_000);
La correction ne vaut que ce que vaut l’intervalle que vous lui passez. Elle convient donc à un générateur censé envoyer à un rythme régulier.
Comment k6, Gatling, Locust et JMeter s’y prennent-ils ?
k6, Gatling et JMeter peuvent lancer le travail selon un calendrier qui n’attend pas les réponses. Locust cadence chaque utilisateur mais ne peut pas lancer d’utilisateurs supplémentaires pour tenir un taux.
k6. Dans les executors à taux d’arrivée, les itérations démarrent indépendamment de la réponse du système tant qu’un utilisateur virtuel est libre. Quand aucun ne l’est, k6 saute l’itération et la compte dans dropped_iterations, ce qui, tard dans un test, peut signifier que le système se dégrade (vérifié le 9 octobre 2026). Une itération abandonnée n’a pas de temps de réponse : lisez ce compteur à côté des percentiles. Sur LoadFocus, les scénarios à taux d’arrivée des scripts k6 tournent avec un nombre fixe d’utilisateurs, soit un modèle fermé. Lancez k6 vous-même quand le taux d’arrivée est l’objet du test.
Gatling. Des étapes comme constantUsersPerSec dans injectOpen lancent de nouveaux utilisateurs virtuels à un taux fixé, que les précédents aient terminé ou non (vérifié le 9 octobre 2026).
Locust. constant_throughput ajuste l’attente de chaque utilisateur pour qu’il exécute une tâche au plus un certain nombre de fois par seconde. La documentation précise que le temps d’attente ne peut que limiter le taux et ne lance pas de nouveaux utilisateurs, et la référence de l’API ajoute qu’une tâche qui dépasse son intervalle n’est suivie d’aucune attente. Une réponse lente retarde donc quand même cet utilisateur (vérifié le 9 octobre 2026).
JMeter. Un Thread Group est un pool de threads qui bouclent, donc une boucle fermée. JMeter 5.5 a ajouté l’Open Model Thread Group, expérimental, qui suit un calendrier comme rate(1/sec) random_arrivals(2 min) et, selon les notes de version, crée des threads au besoin (vérifié le 9 octobre 2026).
Un modèle de charge ouvert supprime-t-il la coordinated omission ?
Il supprime la cause principale, un générateur qui attend avant d’envoyer. Deux failles restent : un générateur à court d’utilisateurs ou de CPU prend quand même du retard sur son calendrier, et chaque utilisateur d’un parcours en plusieurs étapes attend toujours ses propres réponses.
La première se voit sous forme d’itérations abandonnées ou d’un moteur saturé ; dans les deux cas, relancez le test avec plus de marge. La seconde est inhérente à la plupart des tests HTTP : le README de wrk2 note qu’envoyer sans attendre ne convient qu’aux protocoles non bloquants ou à une requête par connexion, et c’est pourquoi wrk2 corrige plutôt la mesure du temps.
FAQ
Qui a nommé la coordinated omission ? Gil Tene, l’auteur d’origine d’HdrHistogram, dont le fork wrk2 de wrk est conçu pour l’éviter. Son message de 2013 sur la liste de diffusion mechanical-sympathy la décrit comme un problème qu’il appelle ainsi.
La coordinated omission est-elle la même chose qu’un modèle de charge fermé ? Les deux sont liés. Un modèle fermé en est une cause fréquente, puisque chaque utilisateur virtuel attend sa réponse avant de renvoyer, et le README de wrk2 retrouve aussi l’effet dans le code de supervision.
Faut-il corriger tous les histogrammes de latence ? Seulement si vous connaissez l’intervalle auquel les requêtes étaient prévues, car HdrHistogram en a besoin pour combler les mesures manquantes. Appliquez à un jeu de données une seule de ses deux méthodes de correction, jamais les deux.
À lire aussi
- Modèle de charge ouvert ou fermé : lequel choisir ?
- Outils de benchmark HTTP : wrk, hey, oha, ab et autres
- Critères de réussite d’un test de charge : comment les fixer
- Débit vs latence : comment ils interagissent sous charge
- Latence de queue : définition et mesure sous charge
- YCSB : charges de travail A-F, exécution et limites
Les affirmations techniques de cet article n'ont pas été relues de façon indépendante.