Head-Based SDK Sampling vs. Collector Tail Sampling: Where Should the Keep/Drop Decision Live?
0 reputation · 30 Apr 2022, 04:51 UTC
0 reputation · 30 Apr 2022, 04:51 UTC
I'm designing the tracing pipeline for a set of services that will export OpenTelemetry spans through a Collector, and I need to pick a sampling strategy before rollout. The two documented options pull in opposite directions.
With head-based sampling (for example a ParentBased sampler wrapping TraceIdRatioBased in the SDK), the decision happens at span creation, so dropped traces never hit the network or storage. The cost is that the decision is blind: a trace that later turns out to contain an error may already be gone, and there is no way to recover it.
With tail-based sampling in the Collector's tail_sampling processor, I could keep only traces matching policies like "contains an error" or "exceeds a latency threshold." But this requires buffering whole traces in memory, a decision wait window, and trace-ID-aware routing so all spans of a trace reach the same Collector replica.
Assume current stable SDK and Collector releases; exact sampler and processor configuration is version-sensitive.
Given a multi-replica Collector deployment, which trade-off is more defensible: cheap but blind head sampling, or informed but stateful tail sampling? If I combine a low head rate with tail policies, how do I reason about effective sampling probability and trace completeness? What breaks operationally if the tail buffer is undersized under load?
31275 reputation · 30 Apr 2022, 06:45 UTC
The most defensible trade-off depends on your primary goal: cost and performance predictability (Head-based) or high-fidelity error capture (Tail-based). For most high-traffic production environments, a hybrid approach is the most sustainable, but it requires a specific architectural prerequisite: trace-ID-aware routing.
Decisions are made at the start of the trace. This is the most stable operational choice because it eliminates network and memory overhead for dropped traces before they ever leave the application process.
Decisions are made after all spans for a trace have been collected. This allows you to keep 100% of errors and high-latency traces while dropping 99% of healthy ones.
When combining a low head-sampling rate (e.g., 10%) with tail policies, your effective sampling probability is the product of the two. If your head sampler is set to 0.1 and your tail policy keeps all errors, you are not keeping all errors—you are keeping 10% of all errors.
The Logic: Head sampling acts as a "pre-filter" to protect the Collector's memory. Tail sampling then acts as a "refiner" to ensure the remaining budget is spent on the most valuable traces.
If the tail buffer is undersized under heavy load, the Collector typically handles overflow by dropping the oldest traces in the buffer to make room for new ones. This leads to trace fragmentation: you may see the end of a trace (the error) but lose the beginning (the root cause), rendering the trace nearly useless for debugging.
(Traces per second) × (Average spans per trace) × (Average span size) × (Decision window in seconds).otelcol_processor_tail_sampling_buffer_occupancy metric. If it consistently hits the limit, increase memory or tighten head-based sampling.Missing Diagnostic: To provide a specific memory recommendation, what is your average span count per trace and your target decision window (e.g., 30 seconds)?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.