Ir al contenido
mcpd
sin daemon
Código

Muestreo

Conservar una fracción de las trazas, sin conservar una fracción de cada una.

La decisión se toma una vez, en la raíz

Una traza se conserva o se descarta entera. La decisión se toma cuando arranca el span raíz y la heredan todos sus hijos, así que una ejecución nunca queda registrada a medias.

Esta es la regla de la que sale todo lo demás. Una traza a la que le falta un 30% de sus spans al azar no se lee como una traza muestreada. Se lee como un error del sistema que estás observando: una llamada a herramienta que al parecer nunca ocurrió, una llamada al modelo sin padre, un hueco donde debería estar la recuperación. Vas a perder una tarde en ello antes de averiguar que el hueco es tu herramienta de observabilidad y no tu agente.

El valor por defecto lo conserva todo

Sin muestreo configurado la política es always, y ese es el valor por defecto correcto hasta que el volumen diga otra cosa. Muestrear es renunciar a la ejecución que vas a querer leer. Paga eso solo cuando te obliguen el almacenamiento o el ancho de banda.

Las cinco estrategias

EstrategiaComportamiento
always Conservarlo todo. Es lo que hay por defecto.
never No conservar nada.
ratio Conservar una fracción fija, sorteada al azar una vez por traza en la raíz.
deterministic Conservar una fracción fija, decidida aplicando un hash al id de la traza. Cada colector llega a la misma respuesta sin hablar con ningún otro.
tail Aplazar la decisión hasta que la traza termina, y entonces conservar todos los errores y todas las ejecuciones lentas, diga lo que diga la tasa.

Definir una tasa

--sample, o MCPD_SAMPLE, toma una fracción de 0.0 a 1.0 y configura la estrategia determinista. Un valor fuera de ese rango detiene el daemon al arrancar con un mensaje que nombra lo que pasaste. No lo recorta al rango, porque una tasa mal tecleada que se convierte en silencio en 1.0 se descubre un mes después por la factura del almacenamiento.

conserva una traza de cada diez

mcpd --sample 0.1

Todos los SDK leen la misma fracción, como sample en la configuración del cliente o MCPD_SAMPLE en el entorno. Muestrear en el cliente sale más barato: un span que no se muestreó no se encola nunca y no se exporta nunca, así que no cuesta nada de ancho de banda. Muestrear en el daemon te cuesta el viaje de ida y vuelta, pero mantiene la tasa en un solo sitio, que es lo que quieres cuando una docena de servicios reportan a una misma instancia.

De las cinco estrategias, el daemon alcanza always y deterministic: --sample y el sample de un proyecto en el archivo de configuración fijan ambos la estrategia determinista. never, ratio y tail están implementadas en el tipo de política y no se alcanzan desde una opción ni desde el archivo de configuración en esta compilación.

Por qué el hash deja que una flota coincida

Ejecuta varias instancias de mcpd detrás de un balanceador de carga y los spans de una misma traza no llegan todos a la misma. Un muestreador que lanza una moneda por instancia, o por span, conservaría parte de una traza y descartaría el resto, que es justo el fallo que la primera regla existe para evitar.

La estrategia determinista proyecta el id de la traza sobre el rango completo de 64 bits y conserva la traza cuando ese valor cae por debajo del umbral que implica la tasa. Cada instancia calcula el mismo número a partir del mismo id, así que todas coinciden, sin estado compartido, sin protocolo de coordinación y sin nada que se pueda desincronizar. El mismo id da además la misma respuesta en un reintento, así que un lote reenviado no puede llegar a medias.

Dos detalles hacen honesta esa proyección. Lee los dieciséis caracteres hexadecimales finales del id: OpenTelemetry exige que los ids de traza sean aleatorios, pero algunos productores derivan los bytes iniciales de una marca de tiempo, y un muestreador que leyera esos bytes se correlacionaría con la hora del día en lugar de muestrear. Cuando el id no es hexadecimal en absoluto, recurre a un hash de verdad con una mezcla final en lugar de degenerar en conservarlo todo, así que la tasa real se mantiene cerca de la tasa que pediste.

En los SDKs, una decisión aguas arriba gana

Cuando un SDK continúa una traza a partir de un traceparent de W3C entrante, toma el flag de muestreo junto con los ids y no aplica su propia tasa. Esto vale en los dos sentidos: el flag puesto conserva la traza sea cual sea la tasa local, y el flag quitado la descarta sea cual sea la tasa local.

El daemon no hace esto en esta versión. Cuando un span llega por OTLP, el daemon guarda su byte de trace-flags en el atributo mcpd.otlp.flags, pero su muestreador no lo lee. Un daemon con una tasa aplica esa tasa a todas las trazas, diga lo que diga el flag.

La alternativa es peor de lo que suena. Si cada servicio aplicara su propia tasa, una traza que cruzara cuatro de ellos la conservarían dos y la descartarían dos, y lo que tendrías al final no es una traza distribuida. Es un conjunto de huecos con la forma de una.

El muestreo de cola conserva las ejecuciones que vale la pena leer

Un muestreador de cabecera decide antes de saber nada. Sale barato, y es ciego: a una décima parte, conserva también una décima parte de los fallos. Las trazas que de verdad abres son las raras, y la rareza es exactamente lo que peor se le da a un muestreador por proporción.

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

Así se ve un registro de latencia: una línea base plana y un puñado de picos. En la línea base está el volumen y en los picos están las respuestas. El muestreo de cola retiene la traza hasta que termina, y entonces la conserva si algo dentro dio error, la conserva si tardó más que el umbral que definiste, y en cualquier otro caso aplica el mismo hash determinista de antes. Conservar los errores está activado por defecto, porque las trazas que más necesitas son las raras y un muestreador por proporción es el que más probablemente las tire.

El costo es el búfer. Un muestreador de cola tiene que retener en memoria cada traza sin terminar hasta poder decidir, que es la razón de que sea una elección deliberada y no lo que hay por defecto. Como arriba, la estrategia de cola está implementada en el tipo de política y no se alcanza desde una opción ni desde el archivo de configuración en esta compilación; la ruta de ingesta del daemon aplica la decisión de cabecera.

Saber qué se descartó

Cada span que el muestreador descarta incrementa mcpd_traces_sampled_out_total, exportado en /metrics en formato de texto de Prometheus. Anota la tasa en algún sitio que tu equipo lea. Una traza que falta con una tasa de muestreo documentada es una explicación; una traza que falta sin explicación es un ticket de soporte, y quien lo abre sueles ser tú.

A continuación