antonio leandro

sistemas distribuídos

Dapper, a Large-Scale Distributed Systems Tracing Infrastructure

paper · núcleo · Benjamin H. Sigelman, Luiz André Barroso, Mike Burrows, Pat Stephenson, Manoj Plakal, Donald Beaver, Saul Jaspan, Chandan Shanbhag · · ~43 min de leitura do original

a tese

rastrear um sistema distribuído inteiro sai barato se você desistir de rastrear tudo: uma amostra de 1 em 1.024 requisições, colhida dentro das bibliotecas de RPC e threading, basta — e o autor da aplicação nem fica sabendo

o que fica

  1. Amostrar uma requisição em mil não perde o que importa: num serviço de alto volume, se um padrão de execução aparece uma vez, ele aparece milhares de vezes.
  2. Rastrear todas as requisições da busca web custou 16,3% de latência média, contra 2,12% a cada 1/16 e nada mensurável a 1/1.024 — o overhead do tracing é real, e é a amostragem que o esconde.
  3. A transparência para o desenvolvedor veio de instrumentar três bibliotecas comuns (threading, controle de fluxo e RPC), não as aplicações: menos de 1.000 linhas de C++ cobriram quase toda a produção do Google.
  4. A coleta é out-of-band porque devolver o trace dentro da resposta do RPC faria a telemetria ser maior que o dado da aplicação, e porque nem todo RPC é perfeitamente aninhado.
  5. Dapper diz onde o sistema está lento, não por quê: fila atrás de outras requisições e escritas coalescidas ficam invisíveis no modelo de span, e os autores dizem isso.
  6. Abrir o repositório de traces por uma API simples gerou mais ferramentas do que o time do Dapper produziria sozinho, e a maior parte desses usos não foi prevista pelos autores.

o problema

Uma busca no Google em 2010 não é uma requisição, são milhares. O front-end espalha a query por centenas de servidores de índice, e ainda chama anúncios, corretor ortográfico, imagens, vídeo, notícias — o que o paper chama de universal search. Quando a página demora, o engenheiro vê o número agregado e não tem como adivinhar quem atrasou. Três razões, todas estruturais: ele não sabe exatamente quais serviços entraram na requisição, porque isso muda de semana em semana; ele não é especialista nas entranhas de cada um, porque cada um tem outro dono; e os serviços são compartilhados, então a lentidão pode ter vindo do tráfego de outro cliente do mesmo Bigtable.

Havia duas famílias de solução, e as duas cobravam caro. As black-box inferem a causalidade por regressão estatística sobre os logs de mensagem que já existem — são portáteis, mas imprecisas, e precisam de muito volume para acertar. As baseadas em anotação pedem que a aplicação carimbe cada registro com um identificador global — são exatas, e quebram no primeiro time que esquece de instrumentar. Some a isso o requisito de que o monitoramento esteja sempre ligado, porque comportamento estranho em produção quase nunca se reproduz sob demanda.

a ideia

Dapper fica na segunda família, mas move o ponto de instrumentação: em vez de pedir colaboração das aplicações, instrumenta o punhado de bibliotecas que todas elas usam de qualquer jeito. No ambiente do Google isso significa um modelo de threading, uma biblioteca de controle de fluxo e um framework de RPC. Instrumentados esses três, a busca web inteira é rastreável sem uma linha de código novo em nenhum time.

O segundo movimento é aceitar não ver quase nada. Dapper amostra: na primeira versão de produção, uma requisição em 1.024. A aposta é que num serviço de alto volume qualquer padrão que interesse vai reaparecer milhares de vezes — e ela se sustentou.

como funciona

O trace é uma árvore de spans. Cada span guarda nome legível, span id, parent id, trace id (inteiros de 64 bits, únicos com alta probabilidade), início, fim, tempos de RPC e zero ou mais anotações da aplicação. Span sem parent é raiz. O caso comum é um span por RPC, com anotações dos dois lados: cliente e servidor. Como os relógios são de máquinas diferentes, as ferramentas de análise usam a única garantia disponível — o cliente envia antes de o servidor receber — para limitar o skew por cima e por baixo.

A propagação é o truque: o contexto do trace vive em thread-local storage; a biblioteca de controle de fluxo carrega esse contexto para dentro de callbacks agendados em thread pool, de modo que caminhos assíncronos continuam ligados; e o framework de RPC transmite trace id e span id pela rede. O núcleo disso tudo tem menos de 1.000 linhas em C++ e menos de 800 em Java.

A coleta é fora de banda. O span vai para arquivo local, um daemon puxa, e o dado termina numa célula Bigtable regional — um trace por linha, um span por coluna, aproveitando o layout esparso. A latência mediana até o repositório fica abaixo de 15 segundos. Nada disso viaja no header de resposta do RPC, e por duas razões: um trace pode ter milhares de spans enquanto a resposta tem menos de dez kilobytes, e há middleware que responde ao chamador antes de os próprios backends terminarem, o que quebra o aninhamento perfeito que a coleta in-band pressupõe.

Há ainda uma segunda amostragem, no coletor: hash do trace id num escalar entre 0 e 1, guarda se ficar abaixo do coeficiente. Como a decisão depende do trace id, descarta-se o trace inteiro, nunca spans soltos — e o time controla a vazão de escrita no Bigtable mexendo num parâmetro de configuração.

o que isso custou

O custo do tracing completo é medido e é grande: amostrando tudo, a latência média da busca web sobe 16,3%. Só a partir de 1/16 o efeito cai para dentro do erro experimental. Ou seja, a barateza do Dapper é comprada jogando dado fora, e a taxa fixa penaliza exatamente os serviços de baixo tráfego, que precisariam amostrar mais. A amostragem adaptativa, parametrizada por traces por unidade de tempo, ainda estava em implantação quando o paper saiu.

A frescura do dado também é irregular: o percentil 98 da latência de coleta é bimodal — abaixo de dois minutos em cerca de 75% do tempo, e muitas horas nos outros 25%. Para apagar incêndio em serviços de armazenamento compartilhado, que precisam de agregado em dez minutos, os autores admitem que Dapper não serve.

E há o que o modelo simplesmente não enxerga. Escritas coalescidas fazem um trace levar a culpa por trabalho de vários. Cargas batch não têm onde pendurar um trace id. Dapper aponta onde está lento, não por quê: uma requisição que ficou na fila atrás de outra parece lenta sozinha. Informação de kernel não se amarra bem ao contexto de usuário. Payload de RPC não é registrado, por privacidade. O índice custa quase o mesmo que os dados. E a transparência depende da homogeneidade do parque — mesmo assim, 40 aplicações C++ e 33 Java precisaram de propagação manual.

onde isso aparece hoje

O vocabulário venceu. Trace, span, parent id, annotation, sampling rate: é assim que se fala de tracing distribuído hoje, e Zipkin, Jaeger e o modelo de dados do OpenTelemetry são reconhecíveis linha a linha a partir deste paper. A parte menos copiada é a mais interessante do relato: o resultado que os autores não previram foi Dapper virar plataforma. Abrir o repositório por uma API — acesso por trace id, acesso em massa via MapReduce, acesso indexado por serviço e máquina — rendeu inferência automática de dependências entre serviços, um console de tráfego entre clusters construído em menos de duas semanas e o cruzamento com o sistema de exceções pelo id do trace.

O repositório em si é Bigtable, o que dá a medida de como a infraestrutura do Google se alimenta de si mesma. E o achado da seção sobre cauda longa — degradação momentânea de rede no caminho crítico não mexe na vazão, mas domina o outlier de latência — é a semente do problema que The Tail at Scale trata como assunto próprio três anos depois.

lido na íntegra por pipeline de llm, revisado por antonio leandro antes de publicar ·