Ir para o conteúdo
mcpd
sem daemon
Código

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égiaComportamento
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.

A strip chart recording model latency over the last hour: a baseline near 420 milliseconds with five occasional excursions towards 800 milliseconds.

É 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