-
Notifications
You must be signed in to change notification settings - Fork 288
All spans of a trace share sampling state #443
Changes from 4 commits
86c8398
3673079
d889df3
a1c7bb7
8010c58
f6cbdd4
4183593
df4bee4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,14 +17,16 @@ package jaeger | |
import ( | ||
"errors" | ||
"fmt" | ||
"go.uber.org/atomic" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
const ( | ||
flagSampled = byte(1) | ||
flagDebug = byte(2) | ||
flagFirehose = byte(8) | ||
flagUnsampled = 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
flagSampled = 1 | ||
flagDebug = 2 | ||
flagFirehose = 8 | ||
) | ||
|
||
var ( | ||
|
@@ -56,9 +58,6 @@ type SpanContext struct { | |
// Should be 0 if the current span is a root span. | ||
parentID SpanID | ||
|
||
// flags is a bitmap containing such bits as 'sampled' and 'debug'. | ||
flags byte | ||
|
||
// Distributed Context baggage. The is a snapshot in time. | ||
baggage map[string]string | ||
|
||
|
@@ -67,6 +66,65 @@ type SpanContext struct { | |
// | ||
// See JaegerDebugHeader in constants.go | ||
debugID string | ||
|
||
// samplingState is shared across all spans | ||
samplingState *samplingState | ||
} | ||
|
||
type samplingState struct { | ||
_flags atomic.Int32 // Only lower 8 bits are used. We use an int32 instead of a byte to use CAS operations | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we never use underscores for field names, what's the reason to start now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used it to avoid a conflict with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oic. Usually collisions are avoided by making methods public, like Flags. Let's keep the underscore. |
||
} | ||
|
||
func (s *samplingState) setFlag(newFlag int32) { | ||
swapped := false | ||
for !swapped { | ||
old := s._flags.Load() | ||
swapped = s._flags.CAS(old, old|newFlag) | ||
} | ||
} | ||
|
||
func (s *samplingState) resetFlag(newFlag int32) { | ||
vprithvi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
swapped := false | ||
for !swapped { | ||
old := s._flags.Load() | ||
swapped = s._flags.CAS(old, old&^newFlag) | ||
} | ||
} | ||
|
||
func (s *samplingState) setSampled() { | ||
s.setFlag(flagSampled) | ||
} | ||
|
||
func (s *samplingState) resetSampled() { | ||
s.resetFlag(flagSampled) | ||
} | ||
|
||
func (s *samplingState) setDebugAndSampled() { | ||
s.setFlag(flagDebug | flagSampled) | ||
} | ||
|
||
func (s *samplingState) setFirehose() { | ||
s.setFlag(flagFirehose) | ||
} | ||
|
||
func (s *samplingState) setFlags(flags byte) { | ||
s._flags.Store(int32(flags)) | ||
} | ||
|
||
func (s *samplingState) flags() byte { | ||
return byte(s._flags.Load()) | ||
} | ||
|
||
func (s *samplingState) isSampled() bool { | ||
return s._flags.Load()&flagSampled == flagSampled | ||
} | ||
|
||
func (s *samplingState) isDebug() bool { | ||
return s._flags.Load()&flagDebug == flagDebug | ||
} | ||
|
||
func (s *samplingState) isFirehose() bool { | ||
return s._flags.Load()&flagFirehose == flagFirehose | ||
} | ||
|
||
// ForeachBaggageItem implements ForeachBaggageItem() of opentracing.SpanContext | ||
|
@@ -81,17 +139,17 @@ func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) { | |
// IsSampled returns whether this trace was chosen for permanent storage | ||
// by the sampling mechanism of the tracer. | ||
func (c SpanContext) IsSampled() bool { | ||
return (c.flags & flagSampled) == flagSampled | ||
return c.samplingState.isSampled() | ||
} | ||
|
||
// IsDebug indicates whether sampling was explicitly requested by the service. | ||
func (c SpanContext) IsDebug() bool { | ||
return (c.flags & flagDebug) == flagDebug | ||
return c.samplingState.isDebug() | ||
} | ||
|
||
// IsFirehose indicates whether the firehose flag was set | ||
func (c SpanContext) IsFirehose() bool { | ||
return (c.flags & flagFirehose) == flagFirehose | ||
return c.samplingState.isFirehose() | ||
} | ||
|
||
// IsValid indicates whether this context actually represents a valid trace. | ||
|
@@ -101,9 +159,9 @@ func (c SpanContext) IsValid() bool { | |
|
||
func (c SpanContext) String() string { | ||
if c.traceID.High == 0 { | ||
return fmt.Sprintf("%x:%x:%x:%x", c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags) | ||
return fmt.Sprintf("%x:%x:%x:%x", c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.samplingState._flags.Load()) | ||
} | ||
return fmt.Sprintf("%x%016x:%x:%x:%x", c.traceID.High, c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags) | ||
return fmt.Sprintf("%x%016x:%x:%x:%x", c.traceID.High, c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.samplingState._flags.Load()) | ||
} | ||
|
||
// ContextFromString reconstructs the Context encoded in a string | ||
|
@@ -130,7 +188,8 @@ func ContextFromString(value string) (SpanContext, error) { | |
if err != nil { | ||
return emptyContext, err | ||
} | ||
context.flags = byte(flags) | ||
context.samplingState = &samplingState{} | ||
context.samplingState.setFlags(byte(flags)) | ||
return context, nil | ||
} | ||
|
||
|
@@ -151,16 +210,17 @@ func (c SpanContext) ParentID() SpanID { | |
|
||
// NewSpanContext creates a new instance of SpanContext | ||
func NewSpanContext(traceID TraceID, spanID, parentID SpanID, sampled bool, baggage map[string]string) SpanContext { | ||
flags := byte(0) | ||
samplingState := &samplingState{} | ||
if sampled { | ||
flags = flagSampled | ||
samplingState.setSampled() | ||
} | ||
|
||
return SpanContext{ | ||
traceID: traceID, | ||
spanID: spanID, | ||
parentID: parentID, | ||
flags: flags, | ||
baggage: baggage} | ||
traceID: traceID, | ||
spanID: spanID, | ||
parentID: parentID, | ||
samplingState: samplingState, | ||
baggage: baggage} | ||
} | ||
|
||
// CopyFrom copies data from ctx into this context, including span identity and baggage. | ||
|
@@ -169,7 +229,7 @@ func (c *SpanContext) CopyFrom(ctx *SpanContext) { | |
c.traceID = ctx.traceID | ||
c.spanID = ctx.spanID | ||
c.parentID = ctx.parentID | ||
c.flags = ctx.flags | ||
c.samplingState = ctx.samplingState | ||
if l := len(ctx.baggage); l > 0 { | ||
c.baggage = make(map[string]string, l) | ||
for k, v := range ctx.baggage { | ||
|
@@ -193,7 +253,7 @@ func (c SpanContext) WithBaggageItem(key, value string) SpanContext { | |
newBaggage[key] = value | ||
} | ||
// Use positional parameters so the compiler will help catch new fields. | ||
return SpanContext{c.traceID, c.spanID, c.parentID, c.flags, newBaggage, ""} | ||
return SpanContext{c.traceID, c.spanID, c.parentID, newBaggage, "", c.samplingState} | ||
} | ||
|
||
// isDebugIDContainerOnly returns true when the instance of the context is only | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flagUnsampled
is unused (fromvarcheck
)