Skip to content

Instantly share code, notes, and snippets.

@jcchavezs
Last active March 3, 2019 21:58
Show Gist options
  • Save jcchavezs/9064b9168cb2b02590c57f677ab5dd4c to your computer and use it in GitHub Desktop.
Save jcchavezs/9064b9168cb2b02590c57f677ab5dd4c to your computer and use it in GitHub Desktop.
Benchmark B3 Single header parsing
package b3
import (
"testing"
"github.com/openzipkin/zipkin-go"
)
func BenchmarkParseSingleHeader(b *testing.B) {
// run the Fib function b.N times
cases := []struct {
name string
header string
}{
{"sampling", "d"},
{"traceID_spanID", "000000000000007b00000000000001c8-000000000000007b"},
{"traceID_spanID_sampling", "000000000000007b00000000000001c8-000000000000007b-1"},
{"traceID_spanID_sampling_parentID", "000000000000007b00000000000001c8-000000000000007b-1-00000000000001c"},
}
b.ResetTimer()
for _, c := range cases {
b.Run(c.name, func(b *testing.B) {
for n := 0; n < b.N; n++ {
ParseSingleHeader(c.header)
}
})
}
}
func BenchmarkParseSingleHeaderWithSplit(b *testing.B) {
// run the Fib function b.N times
cases := []struct {
name string
header string
}{
{"sampling", "d"},
{"traceID_spanID", "000000000000007b00000000000001c8-000000000000007b"},
{"traceID_spanID_sampling", "000000000000007b00000000000001c8-000000000000007b-1"},
{"traceID_spanID_sampling_parentID", "000000000000007b00000000000001c8-000000000000007b-1-00000000000001c"},
}
b.ResetTimer()
for _, c := range cases {
b.Run(c.name, func(b *testing.B) {
for n := 0; n < b.N; n++ {
ParseSingleHeaderWithSplit(c.header)
}
})
}
}
package b3
import (
"strconv"
"strings"
"github.com/openzipkin/zipkin-go/model"
)
func ParseSingleHeader(contextHeader string) (*model.SpanContext, error) {
if contextHeader == "" {
return nil, ErrEmptyContext
}
var (
sc = model.SpanContext{}
sampling string
)
headerLen := len(contextHeader)
if headerLen == 1 {
sampling = contextHeader
} else if headerLen == 16 || headerLen == 32 {
return nil, ErrInvalidScope
} else if headerLen >= 16+16+1 {
var high, low uint64
pos := 0
if string(contextHeader[16]) != "-" {
// traceID must be 128 bits
var err error
high, err = strconv.ParseUint(contextHeader[0:16], 16, 64)
if err != nil {
return nil, ErrInvalidTraceIDValue
}
pos = 16
}
low, err := strconv.ParseUint(contextHeader[pos+1:pos+16], 16, 64)
if err != nil {
return nil, ErrInvalidTraceIDValue
}
sc.TraceID = model.TraceID{High: high, Low: low}
rawID, err := strconv.ParseUint(contextHeader[pos+16+1:pos+16+1+16], 16, 64)
if err != nil {
return nil, ErrInvalidSpanIDValue
}
sc.ID = model.ID(rawID)
if headerLen > pos+16+1+16 {
if headerLen == pos+16+1+16+1 {
return nil, ErrInvalidSampledByte
}
if headerLen == pos+16+1+16+1+1 {
sampling = string(contextHeader[pos+16+1+16+1])
} else if headerLen == pos+16+1+16+1+16 {
return nil, ErrInvalidScopeParentSingle
} else if headerLen == pos+16+1+16+1+1+1+16 {
sampling = string(contextHeader[pos+16+1+16+1])
rawParentID, err := strconv.ParseUint(contextHeader[pos+16+1+16+1+1+1:], 16, 64)
if err != nil {
return nil, ErrInvalidParentSpanIDValue
}
parentID := model.ID(rawParentID)
sc.ParentID = &parentID
} else {
return nil, ErrInvalidParentSpanIDValue
}
}
} else {
return nil, ErrInvalidTraceIDValue
}
switch sampling {
case "d":
sc.Debug = true
case "1":
trueVal := true
sc.Sampled = &trueVal
case "0":
falseVal := false
sc.Sampled = &falseVal
case "":
default:
return nil, ErrInvalidSampledByte
}
return &sc, nil
}
func ParseSingleHeader(contextHeader string) (*model.SpanContext, error) {
if contextHeader == "" {
return nil, ErrEmptyContext
}
chunks := strings.SplitN(contextHeader, "-", 4)
var (
traceID string
spanID string
sampling string
parentSpanID string
sc = model.SpanContext{}
)
if len(chunks) == 1 {
if len(chunks[0]) == 1 {
sampling = chunks[0]
} else {
return nil, ErrInvalidScope
}
}
if len(chunks) == 2 {
traceID = chunks[0]
spanID = chunks[1]
}
if len(chunks) == 3 {
traceID = chunks[0]
spanID = chunks[1]
sampling = chunks[2]
if chunks[2] == "" {
return nil, ErrInvalidSampledByte
} else if len(chunks[2]) > 1 {
return nil, ErrInvalidScopeParentSingle
}
}
if len(chunks) == 4 {
traceID = chunks[0]
spanID = chunks[1]
sampling = chunks[2]
parentSpanID = chunks[3]
}
switch sampling {
case "d":
sc.Debug = true
case "1":
trueVal := true
sc.Sampled = &trueVal
case "0":
falseVal := false
sc.Sampled = &falseVal
case "":
default:
return nil, ErrInvalidSampledByte
}
if traceID == "" {
return &sc, nil
}
if spanID == "" {
return nil, ErrInvalidScope
}
var err error
pos := 0
if len(traceID) > 32 {
return nil, ErrInvalidTraceIDValue
}
if len(traceID) == 32 {
sc.TraceID.High, err = strconv.ParseUint(traceID[0:16], 16, 64)
if err != nil {
return nil, ErrInvalidTraceIDValue
}
pos = 16
} else if len(traceID) != 16 {
return nil, ErrInvalidTraceIDValue
}
sc.TraceID.Low, err = strconv.ParseUint(traceID[pos+1:pos+16], 16, 64)
if err != nil {
return nil, ErrInvalidTraceIDValue
}
if len(spanID) != 0 && len(spanID) != 16 {
return nil, ErrInvalidSpanIDValue
}
rawID, err := strconv.ParseUint(spanID, 16, 64)
if err != nil {
return nil, ErrInvalidSpanIDValue
}
sc.ID = model.ID(rawID)
if len(parentSpanID) != 0 && len(parentSpanID) != 16 {
return nil, ErrInvalidParentSpanIDValue
}
if parentSpanID != "" {
rawParentID, err := strconv.ParseUint(parentSpanID, 16, 64)
if err != nil {
return nil, ErrInvalidParentSpanIDValue
}
parentID := model.ID(rawParentID)
sc.ParentID = &parentID
}
return &sc, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment