@@ -113,6 +113,7 @@ static class PTags extends PropagationTags {
113113 private volatile TagValue orgPropagationMarkerTagValue ;
114114
115115 private volatile OtelTraceState otelTraceState ;
116+ private volatile OtelSamplingDecision otelSamplingDecision ;
116117
117118 // Static cache for the most-recently-seen rate → TagValue. In steady state a service uses one
118119 // rate, so this eliminates the char[] + String allocation on every new PTags instance.
@@ -267,17 +268,15 @@ private void doUpdateTraceSamplingPriority(int samplingPriority, int samplingMec
267268 }
268269 decisionMakerTagValue = null ;
269270 }
270- if (removeOtelProbability && otelTraceState != null ) {
271- setOtelTraceState ( otelTraceState . removeForNonProbabilityDecision () );
271+ if (removeOtelProbability ) {
272+ setOtelSamplingDecision ( OtelSamplingDecision . NON_PROBABILITY );
272273 }
273274 }
274275
275276 @ Override
276- public void updateOtelTraceState (
277- long traceIdLowOrderBits , double sampleRate , boolean sampled , int samplingPriority ) {
278- setOtelTraceState (
279- OtelTraceState .updateProbability (
280- otelTraceState , traceIdLowOrderBits , sampleRate , sampled , samplingPriority ));
277+ public void updateOtelTraceState (long traceIdLowOrderBits , double sampleRate , boolean sampled ) {
278+ setOtelSamplingDecision (
279+ OtelSamplingDecision .probability (traceIdLowOrderBits , sampleRate , sampled ));
281280 }
282281
283282 @ Override
@@ -442,7 +441,13 @@ public CharSequence getLastParentId() {
442441 public String headerValue (HeaderType headerType ) {
443442 String header = getCachedHeader (headerType );
444443 if (header == null ) {
445- header = PTagsCodec .headerValue (factory .getDecoderEncoder (headerType ), this );
444+ header =
445+ PTagsCodec .headerValue (
446+ factory .getDecoderEncoder (headerType ),
447+ this ,
448+ null ,
449+ resolveOtelTraceState (headerType , samplingPriority ),
450+ samplingPriority );
446451 if (header != null ) {
447452 setCachedHeader (headerType , header );
448453 } else {
@@ -464,7 +469,28 @@ public String headerValue(HeaderType headerType, CharSequence lastParentIdOverri
464469 // Inject-time path: encode fresh with the override; do NOT cache — the W3C `p:` is
465470 // per-injecting-span and these tags may be shared across sibling spans.
466471 String header =
467- PTagsCodec .headerValue (factory .getDecoderEncoder (headerType ), this , lastParentIdOverride );
472+ PTagsCodec .headerValue (
473+ factory .getDecoderEncoder (headerType ),
474+ this ,
475+ lastParentIdOverride ,
476+ resolveOtelTraceState (headerType , samplingPriority ),
477+ samplingPriority );
478+ return (header == null || header .isEmpty ()) ? null : header ;
479+ }
480+
481+ @ Override
482+ public String headerValue (
483+ HeaderType headerType , CharSequence lastParentIdOverride , int samplingPriority ) {
484+ if (lastParentIdOverride == null && samplingPriority == this .samplingPriority ) {
485+ return headerValue (headerType );
486+ }
487+ String header =
488+ PTagsCodec .headerValue (
489+ factory .getDecoderEncoder (headerType ),
490+ this ,
491+ lastParentIdOverride ,
492+ resolveOtelTraceState (headerType , samplingPriority ),
493+ samplingPriority );
468494 return (header == null || header .isEmpty ()) ? null : header ;
469495 }
470496
@@ -560,16 +586,13 @@ public void updateW3CTracestate(String tracestate) {
560586 }
561587
562588 @ Override
563- public void updateW3CTracestateFrom (PropagationTags source , int samplingPriority ) {
564- String sourceTracestate = source .getW3CTracestate ();
565- OtelTraceState sourceOtelTraceState =
566- source instanceof PTags
567- ? ((PTags ) source ).getOtelTraceState ()
568- : W3CPTagsCodec .extractOtelTraceState (sourceTracestate );
569- setW3CTracestate (sourceTracestate , sourceOtelTraceState );
570- if (samplingPriority != PrioritySampling .UNSET ) {
571- this .samplingPriority = samplingPriority ;
589+ public void updateW3CTracestateFrom (PropagationTags source ) {
590+ if (!(source instanceof PTags )) {
591+ super .updateW3CTracestateFrom (source );
592+ return ;
572593 }
594+ PTags sourcePTags = (PTags ) source ;
595+ setW3CTracestate (sourcePTags .tracestate , sourcePTags .getOtelTraceState ());
573596 }
574597
575598 private void setW3CTracestate (String tracestate , OtelTraceState otelTraceState ) {
@@ -589,6 +612,26 @@ void setOtelTraceState(OtelTraceState otelTraceState) {
589612 }
590613 }
591614
615+ private OtelTraceState resolveOtelTraceState (HeaderType headerType , int samplingPriority ) {
616+ OtelTraceState current = otelTraceState ;
617+ if (headerType != W3C ) {
618+ return current ;
619+ }
620+ OtelSamplingDecision decision = otelSamplingDecision ;
621+ OtelTraceState resolved =
622+ decision == null ? current : decision .resolve (current , samplingPriority );
623+ return resolved == null || samplingPriority == PrioritySampling .UNSET
624+ ? resolved
625+ : resolved .reconcileSamplingDecision (samplingPriority > 0 );
626+ }
627+
628+ private void setOtelSamplingDecision (OtelSamplingDecision decision ) {
629+ if (otelSamplingDecision != decision ) {
630+ otelSamplingDecision = decision ;
631+ clearCachedHeader (W3C );
632+ }
633+ }
634+
592635 String getError () {
593636 return this .error ;
594637 }
@@ -618,5 +661,62 @@ private static boolean isProbabilitySamplingMechanism(int samplingMechanism) {
618661 return false ;
619662 }
620663 }
664+
665+ private static final class OtelSamplingDecision {
666+ private static final OtelSamplingDecision NON_PROBABILITY =
667+ new OtelSamplingDecision (0 , 0 , false , false );
668+
669+ private final long traceIdLowOrderBits ;
670+ private final double sampleRate ;
671+ private final boolean sampled ;
672+ private final boolean probability ;
673+ private volatile Resolution resolution ;
674+
675+ private OtelSamplingDecision (
676+ long traceIdLowOrderBits , double sampleRate , boolean sampled , boolean probability ) {
677+ this .traceIdLowOrderBits = traceIdLowOrderBits ;
678+ this .sampleRate = sampleRate ;
679+ this .sampled = sampled ;
680+ this .probability = probability ;
681+ }
682+
683+ private static OtelSamplingDecision probability (
684+ long traceIdLowOrderBits , double sampleRate , boolean sampled ) {
685+ return new OtelSamplingDecision (traceIdLowOrderBits , sampleRate , sampled , true );
686+ }
687+
688+ private OtelTraceState resolve (OtelTraceState current , int samplingPriority ) {
689+ Resolution cached = resolution ;
690+ if (cached != null
691+ && cached .source == current
692+ && cached .samplingPriority == samplingPriority ) {
693+ return cached .resolved ;
694+ }
695+ OtelTraceState resolved ;
696+ if (!probability ) {
697+ resolved = current == null ? null : current .removeForNonProbabilityDecision ();
698+ } else if (current != null ) {
699+ resolved = current ;
700+ } else {
701+ resolved =
702+ OtelTraceState .updateProbability (
703+ null , traceIdLowOrderBits , sampleRate , sampled , samplingPriority );
704+ }
705+ resolution = new Resolution (current , samplingPriority , resolved );
706+ return resolved ;
707+ }
708+
709+ private static final class Resolution {
710+ private final OtelTraceState source ;
711+ private final int samplingPriority ;
712+ private final OtelTraceState resolved ;
713+
714+ private Resolution (OtelTraceState source , int samplingPriority , OtelTraceState resolved ) {
715+ this .source = source ;
716+ this .samplingPriority = samplingPriority ;
717+ this .resolved = resolved ;
718+ }
719+ }
720+ }
621721 }
622722}
0 commit comments