Amostragem
Guardar uma fração dos traces, sem guardar uma fração de cada um deles.
A decisão é tomada uma vez, na raiz
Um trace é guardado ou descartado por inteiro. A decisão é tomada quando o span raiz começa e é herdada por todos os filhos, então uma execução nunca fica registrada pela metade.
Essa é a regra da qual todo o resto decorre. Um trace com 30% aleatórios dos seus spans faltando não se lê como um trace amostrado. Ele se lê como um defeito no sistema que você está observando: uma chamada de ferramenta que aparentemente nunca aconteceu, uma chamada de modelo sem pai, um vão onde a recuperação deveria estar. Você vai gastar uma tarde nisso antes de concluir que o vão é a sua ferramenta de observabilidade e não o seu agente.
O padrão guarda tudo
Sem amostragem configurada, a política é always, e esse é o
padrão certo até o volume dizer o contrário. A amostragem troca fora
justamente a execução que você vai querer ler. Pague isso só quando o
armazenamento ou a banda obrigarem.
As cinco estratégias
| Estratégia | Comportamento |
|---|---|
always | Guarda tudo. O padrão. |
never | Não guarda nada. |
ratio | Guarda uma fração fixa, sorteada uma vez por trace, na raiz. |
deterministic | Guarda uma fração fixa, decidida pelo hash do id do trace. Todo coletor chega à mesma resposta sem falar com nenhum outro. |
tail | Adia a decisão até o trace terminar, e então guarda todo erro e toda execução lenta, seja qual for a taxa. |
Definir uma taxa
O --sample, ou MCPD_SAMPLE, recebe uma fração de
0.0 a 1.0 e configura a estratégia determinística. Um valor fora dessa faixa
faz o daemon parar na inicialização, com uma mensagem que diz o que você
passou.
Ele não limita o valor à faixa, porque uma taxa digitada errado que vira 1.0
em silêncio é descoberta um mês depois, pela conta do armazenamento.
guardar um trace em cada dez
mcpd --sample 0.1
Todo SDK lê a mesma fração, como sample na configuração do
cliente ou MCPD_SAMPLE no ambiente. Amostrar no cliente sai
mais barato: um span que não foi amostrado nunca entra na fila e nunca é
exportado, então ele não custa banda nenhuma. Amostrar no daemon custa a ida
e volta, mas mantém a taxa em um lugar só, que é o que você quer quando uma
dúzia de serviços reporta para uma mesma instância.
Das cinco estratégias, o daemon alcança always e
deterministic: --sample e o sample
de um projeto no arquivo de configuração definem, os dois, a estratégia
determinística. never, ratio e tail
estão implementadas no tipo de política e não são alcançáveis por uma opção
nem pelo arquivo de configuração nesta versão.
Por que o hash faz uma frota concordar
Rode várias instâncias do mcpd atrás de um balanceador de carga e os spans de um mesmo trace não chegam todos na mesma instância. Um amostrador que jogasse uma moeda por instância, ou por span, guardaria parte de um trace e descartaria o resto, que é justamente a falha que a primeira regra existe para evitar.
A estratégia determinística mapeia o id do trace sobre a faixa inteira de 64 bits e guarda o trace quando esse valor cai abaixo do limiar que a taxa implica. Toda instância calcula o mesmo número a partir do mesmo id, então todas concordam, sem estado compartilhado, sem protocolo de coordenação e sem nada que possa sair de sincronia. O mesmo id também dá a mesma resposta em uma repetição, então um lote reenviado não pode chegar pela metade.
Dois detalhes tornam esse mapeamento honesto. Ele lê os dezesseis caracteres hexadecimais finais do id: o OpenTelemetry exige que ids de trace sejam aleatórios, mas alguns produtores derivam os bytes iniciais de um horário, e um amostrador que os lesse ficaria correlacionado com a hora do dia em vez de amostrar. Quando o id não é hexadecimal, ele recua para um hash de verdade, com uma mistura final, em vez de degenerar em guardar tudo, de modo que a taxa efetiva fica perto da taxa que você pediu.
Nos SDKs, uma decisão vinda de cima vence
Quando um SDK continua um trace a partir de um traceparent do
W3C recebido, ele herda o sinalizador de amostragem junto com os ids e não
aplica a própria taxa. Isso vale nos dois sentidos: o sinalizador ligado
guarda o trace qualquer que seja a taxa local, e o sinalizador desligado o
descarta qualquer que seja a taxa local.
O daemon não faz isso nesta versão. Quando um span chega por OTLP, o daemon
guarda o byte de trace-flags dele no atributo mcpd.otlp.flags,
mas o amostrador do daemon não o lê. Um daemon com uma taxa aplica essa taxa
a todo trace, diga o sinalizador o que disser.
A alternativa é pior do que parece. Se cada serviço aplicasse a própria taxa, um trace que atravessasse quatro deles seria guardado por dois e descartado por dois, e o que você teria no fim não é um trace distribuído. É um conjunto de buracos com o formato de um.
A amostragem de cauda guarda as execuções que valem a leitura
Um amostrador de cabeça decide antes de saber qualquer coisa. Ele é barato e é cego: a um décimo, ele guarda um décimo das falhas também. Os traces que você de fato abre são os raros, e a raridade é exatamente aquilo em que um amostrador por proporção é pior.
É assim que um registro de latência se parece: uma linha de base plana e um punhado de excursões. A linha de base é onde está o volume, e as excursões são onde estão as respostas. A amostragem de cauda segura o trace até ele terminar, e então o guarda se qualquer coisa nele deu erro, guarda se ele durou mais que o limiar que você definiu, e caso contrário aplica o mesmo hash determinístico de antes. Guardar os erros vem ligado por padrão, porque os traces de que você mais precisa são os raros, e um amostrador por proporção é o mais propenso a jogá-los fora.
O custo é o buffer. Um amostrador de cauda precisa segurar em memória todo trace inacabado até conseguir decidir, e é por isso que ele é uma escolha deliberada e não o padrão. Como acima, a estratégia de cauda está implementada no tipo de política e não é alcançável por uma opção nem pelo arquivo de configuração nesta versão; o caminho de ingestão do daemon aplica a decisão de cabeça.
Saber o que foi descartado
Todo span que o amostrador descarta incrementa
mcpd_traces_sampled_out_total, exportado em
/metrics no formato de texto do Prometheus. Anote a taxa em
algum lugar que o seu time leia. Um trace ausente com uma taxa de amostragem
documentada é uma explicação; um trace ausente sem explicação é um chamado
de suporte, e quem abre esse chamado costuma ser você.
A seguir
- O outro jeito de armazenar menos: Ocultação e retenção.
- Cada opção e variável de ambiente: Configuração.
-
Ler o
/metricse o resto dos sinais do próprio daemon: Como executar.