Component(s)
exporter/loadbalancing
Is your feature request related to a problem? Please describe.
Linked-trace workloads (AI agent conversations, browser sessions, multi-step workflows) produce many traces that form one unit of interest. OTEP 235 anticipated sampling these together via a shared explicit randomness value: "There are circumstances where trace-id randomness is inadequate (for example, sampling a group of traces together); in these cases, an rv value is required." The dynamic_sampling processor honours a producer-set shared ot=rv so every trace in a group gets the same sampling decision (#49311, #49520).
For tail-based decisions this only works if the whole group is visible to one collector instance. Today the loadbalancing exporter can co-locate a single trace (routing_key: traceID), but traces sharing an ot=rv still scatter across backends, so any group-level decision needs cross-node coordination.
Describe the solution you'd like
A new routing_key: randomness that routes by the OTel randomness value:
- For each span, parse the W3C tracestate with
pkg/sampling and use the 14-hex rv value as the routing identifier when present.
- When
rv is absent or the tracestate is unparseable, fall back to sampling.TraceIDToRandomness(traceID), the spec's definition of implicit randomness. Traffic without rv therefore keeps per-trace locality with the same semantics as traceID routing.
- Consumption path mirrors the existing
consumeTracesByID optimisation (per-span key, per-key exporter cache), since spans of one trace always share a key.
Scope is traces-only initially. A counter for present-but-unparseable tracestate would make instrumentation problems visible instead of silently falling back. I'd like to implement this; the change is small and follows the same shape as previous routing key additions.
Describe alternatives you've considered
Enhancing the existing traceID key to honour rv when present, on the argument that rv is the spec-correct randomness the key should hash anyway. Two downsides made me prefer an opt-in key: it silently changes routing on upgrade for any deployment whose traffic already carries ot=rv, and care is needed to avoid changing the hash input for non-rv traffic, which would remap every key on the ring. Open to this direction if maintainers prefer it.
Also considered routing_key: attributes with a session/conversation attribute. This only works when the group key is present as a span/resource attribute on every span, which isn't the case for tracestate-propagated grouping, and it doesn't fall back to trace locality for ungrouped traffic.
Additional context
Related: #49311 (dynamic_sampling alpha tracker), #49520 (honour incoming ot=th/ot=rv), open-telemetry/opentelemetry-specification#2918 (consistent sampling across linked traces). On naming: proposing randomness because the key always routes by the trace randomness value on both the rv and fallback paths; rv (matches the tracestate sub-key) or tracestateRV (matches traceID/streamID casing) would also work if preferred.
Component(s)
exporter/loadbalancing
Is your feature request related to a problem? Please describe.
Linked-trace workloads (AI agent conversations, browser sessions, multi-step workflows) produce many traces that form one unit of interest. OTEP 235 anticipated sampling these together via a shared explicit randomness value: "There are circumstances where trace-id randomness is inadequate (for example, sampling a group of traces together); in these cases, an
rvvalue is required." The dynamic_sampling processor honours a producer-set sharedot=rvso every trace in a group gets the same sampling decision (#49311, #49520).For tail-based decisions this only works if the whole group is visible to one collector instance. Today the loadbalancing exporter can co-locate a single trace (
routing_key: traceID), but traces sharing anot=rvstill scatter across backends, so any group-level decision needs cross-node coordination.Describe the solution you'd like
A new
routing_key: randomnessthat routes by the OTel randomness value:pkg/samplingand use the 14-hexrvvalue as the routing identifier when present.rvis absent or the tracestate is unparseable, fall back tosampling.TraceIDToRandomness(traceID), the spec's definition of implicit randomness. Traffic withoutrvtherefore keeps per-trace locality with the same semantics astraceIDrouting.consumeTracesByIDoptimisation (per-span key, per-key exporter cache), since spans of one trace always share a key.Scope is traces-only initially. A counter for present-but-unparseable tracestate would make instrumentation problems visible instead of silently falling back. I'd like to implement this; the change is small and follows the same shape as previous routing key additions.
Describe alternatives you've considered
Enhancing the existing
traceIDkey to honourrvwhen present, on the argument thatrvis the spec-correct randomness the key should hash anyway. Two downsides made me prefer an opt-in key: it silently changes routing on upgrade for any deployment whose traffic already carriesot=rv, and care is needed to avoid changing the hash input for non-rv traffic, which would remap every key on the ring. Open to this direction if maintainers prefer it.Also considered
routing_key: attributeswith a session/conversation attribute. This only works when the group key is present as a span/resource attribute on every span, which isn't the case for tracestate-propagated grouping, and it doesn't fall back to trace locality for ungrouped traffic.Additional context
Related: #49311 (dynamic_sampling alpha tracker), #49520 (honour incoming ot=th/ot=rv), open-telemetry/opentelemetry-specification#2918 (consistent sampling across linked traces). On naming: proposing
randomnessbecause the key always routes by the trace randomness value on both thervand fallback paths;rv(matches the tracestate sub-key) ortracestateRV(matchestraceID/streamIDcasing) would also work if preferred.