Coordinated omission: o que é e como evitar
7 min de leitura
A coordinated omission é um erro de medição: um gerador de carga espera cada resposta lenta antes de enviar a próxima requisição, então envia menos justamente quando o sistema está lento e registra poucos dos piores momentos. Os percentis que você lê depois parecem melhores do que aquilo que os usuários viveram. Você evita o erro enviando as requisições num cronograma que ignora os tempos de resposta e medindo cada requisição a partir do momento em que ela deveria ter saído.
Os fatos sobre as ferramentas vêm da documentação ou do repositório de cada projeto, conferidos em 9 de outubro de 2026.
| Ferramenta | O que oferece | O que conferir |
|---|---|---|
| wrk2 | Uma taxa constante (-R), latência medida desde o envio previsto | --u_latency mostra também a visão sem correção |
| HdrHistogram | Dois métodos que preenchem as amostras que faltam | Aplicar só um deles, nunca os dois |
| k6 | Os executors de taxa de chegada | O contador dropped_iterations |
| Gatling | Passos de injectOpen como constantUsersPerSec | Os passos ficam dentro de injectOpen |
| Locust | constant_throughput dá o ritmo de cada usuário | A taxa atingida em relação à meta |
| JMeter | Open Model Thread Group (5.5 ou posterior, experimental) | Um Thread Group comum é um pool fechado |
O que é coordinated omission?
É o erro que aparece quando o lado que mede para de coletar amostras porque o sistema está lento, de modo que as amostras que faltam são justamente as lentas. Perder amostras ao acaso quase não mexe num percentil; perdê-las no compasso de uma lentidão distorce todos eles.
O nome vem de Gil Tene, cuja mensagem de 2013 numa lista de discussão traça essa mesma linha. Ele é o autor original do HdrHistogram, o wrk2 está na conta dele no GitHub, e a documentação dos dois projetos explica o efeito (conferido em 9 de outubro de 2026). A documentação do k6 usa o termo para um modelo de carga fechado que freia a própria carga, uma causa frequente. Nosso artigo sobre o modelo de carga aberto vs fechado trata dessa escolha; este fica com a medição.
Como um teste de carga acaba escondendo as respostas lentas?
Um gerador em loop fechado só envia a próxima requisição numa conexão depois que a resposta anterior chega. Se uma resposta leva um segundo, essa conexão não envia nada por um segundo, e as requisições que ela teria enviado nunca recebem um tempo de resposta.
O README do wrk2 diz isso sobre o modelo do wrk, que mede cada requisição do primeiro byte enviado até a resposta completa: como uma conexão só envia depois de uma resposta, o gerador se coordena com o servidor e deixa de medir nos períodos de alta latência (conferido em 9 de outubro de 2026).
O README do HdrHistogram propõe um experimento mental. Um sistema amostrado a cada 10 ms responde em 1 ms por 100 segundos e depois trava por 100 segundos, o que gera uma única amostra de 100 segundos. O histograma bruto põe cerca de 99,99% dos resultados em 1 ms ou menos; corrigido pelas amostras bloqueadas, só cerca da metade fica ali.
Como saber se os seus resultados têm coordinated omission?
Compare a taxa enviada com a planejada. Se a taxa de requisições caiu enquanto os tempos de resposta subiam, o gerador recuou e o período lento ficou sub-representado.
- A carga foi definida em usuários ou conexões, sem taxa. Qualquer loop fechado pode causar o erro.
- A taxa de requisições cai onde os tempos de resposta sobem. Uma queda na taxa durante um pico significa que houve requisições que nunca saíram.
- O máximo é muito maior que o percentil 99. Algumas amostras enormes entre milhares de rápidas são o exemplo acima em miniatura. Leia toda a distribuição dos tempos de resposta, lembrando que ela só conta requisições enviadas.
- Faltou CPU ao gerador de carga. A LoadFocus documenta que um motor perto de 100% de CPU não consegue enviar na taxa configurada, e os resultados mostram um aviso de saturação do gerador de carga quando algum motor atingiu ou passou de 80% de CPU no pico.
Como o wrk2 evita a coordinated omission?
Ele envia a uma taxa constante e mede cada requisição a partir do momento em que o plano dizia que ela deveria sair. Uma requisição que sai atrasada porque a conexão estava ocupada carrega a espera.
O wrk2 acrescenta ao wrk um argumento de taxa (-R, requisições por segundo no total) e registra as latências num HdrHistogram. --u_latency acrescenta ao relatório o histograma sem correção, marcado como CO, como no exemplo do próprio README:
wrk -t2 -c100 -d30s -R2000 --u_latency http://127.0.0.1:80/index.html
No segundo exemplo do README, uma pausa de 1,4 segundo no servidor durante um teste de 30 segundos deixou o percentil 99 corrigido 200 vezes maior que o sem correção. O README também chama o projeto de experimental e avisa que testes com menos de 10 a 20 segundos podem não ser úteis (conferido em 9 de outubro de 2026). Nossa análise de ferramentas de benchmark HTTP compara o wrk2 com hey, oha e outras.
Como o HdrHistogram corrige as latências registradas?
Se você sabe o intervalo em que as requisições deveriam sair, ele acrescenta as amostras que uma resposta longa bloqueou. A correção pode ser feita durante a gravação ou depois, numa cópia.
recordValueWithExpectedInterval registra um valor e, quando ele passa do intervalo, acrescenta valores que descem em passos desse intervalo. copyCorrectedForCoordinatedOmission devolve uma cópia corrigida de um histograma pronto. O JavaDoc descreve os dois métodos como mutuamente exclusivos (conferido em 9 de outubro de 2026).
// intervalo planejado: 10 ms, valores em microssegundos
Histogram h = new Histogram(3);
h.recordValueWithExpectedInterval(latencyUs, 10_000);
// ou, depois do teste, num histograma bruto:
Histogram fixed = raw.copyCorrectedForCoordinatedOmission(10_000);
A correção só é tão boa quanto o intervalo que você passa. Ela serve, portanto, para um gerador que deveria enviar num ritmo constante.
Como k6, Gatling, Locust e JMeter lidam com isso?
k6, Gatling e JMeter podem iniciar trabalho num cronograma que não espera respostas. O Locust dá o ritmo de cada usuário, mas não consegue iniciar usuários extras para manter uma taxa.
k6. Nos executors de taxa de chegada, as iterações começam independentemente da resposta do sistema enquanto houver um usuário virtual livre. Quando não há, o k6 pula a iteração e a conta em dropped_iterations, o que, já perto do fim de um teste, pode indicar que o sistema está se degradando (conferido em 9 de outubro de 2026). Uma iteração descartada não tem tempo de resposta, então leia esse contador ao lado dos percentis. Na LoadFocus, os cenários de taxa de chegada dos scripts k6 rodam com um número fixo de usuários, ou seja, como modelo fechado. Rode o k6 por conta própria quando a taxa de chegada for o objetivo do teste.
Gatling. Passos como constantUsersPerSec dentro de injectOpen iniciam novos usuários virtuais a uma taxa fixa, tenham os anteriores terminado ou não (conferido em 9 de outubro de 2026).
Locust. constant_throughput ajusta a espera de cada usuário para que ele execute uma tarefa no máximo um certo número de vezes por segundo. A documentação diz que o tempo de espera só consegue limitar a taxa e não inicia novos usuários, e a referência da API acrescenta que uma tarefa que passa do intervalo não é seguida de espera. Uma resposta lenta atrasa esse usuário mesmo assim (conferido em 9 de outubro de 2026).
JMeter. Um Thread Group é um pool de threads que repetem o plano em loop, ou seja, um loop fechado. O JMeter 5.5 trouxe o Open Model Thread Group, experimental, que segue um cronograma como rate(1/sec) random_arrivals(2 min) e, segundo as notas de versão, cria threads conforme a necessidade (conferido em 9 de outubro de 2026).
Um modelo de carga aberto elimina a coordinated omission?
Ele elimina a causa principal, um gerador que espera antes de enviar. Ficam duas brechas: um gerador sem usuários ou CPU suficientes ainda fica para trás no cronograma, e cada usuário de uma jornada com várias etapas continua esperando as próprias respostas.
A primeira aparece como iterações descartadas ou como um motor saturado; nos dois casos, repita o teste com mais folga. A segunda vem com a maioria dos testes HTTP: o README do wrk2 observa que enviar sem esperar só funciona com protocolos não bloqueantes ou com uma requisição por conexão, e por isso o wrk2 corrige a medição do tempo.
FAQ
Quem deu nome à coordinated omission? Gil Tene, o autor original do HdrHistogram, cujo fork wrk2 do wrk foi pensado para evitá-la. A mensagem dele de 2013 na lista de discussão mechanical-sympathy a descreve como um problema que ele chama por esse nome.
Coordinated omission é a mesma coisa que um modelo de carga fechado? Os dois estão ligados. Um modelo fechado é uma causa frequente, porque cada usuário virtual espera a resposta antes de enviar de novo, e o README do wrk2 também encontra o efeito em código de monitoramento.
É preciso corrigir todo histograma de latência? Só quando você conhece o intervalo em que as requisições estavam planejadas, porque o HdrHistogram precisa dele para preencher as amostras que faltam. Aplique a um conjunto de dados um dos dois métodos de correção, nunca os dois.
Leituras relacionadas
- Modelo de carga aberto vs fechado: qual usar e quando
- Ferramentas de benchmark HTTP: wrk, hey, oha, ab e mais
- Critérios de aprovação de um teste de carga: como definir
- Vazão vs latência: como se relacionam sob carga
- Latência de cauda: o que é e como medir
- YCSB: cargas de trabalho A-F, execução e limites
As afirmações técnicas deste artigo não foram revisadas de forma independente.