Skip to content

Commit

Permalink
Store span warnings as tags in Cassandra (jaegertracing#4313)
Browse files Browse the repository at this point in the history
## Which problem is this PR solving?
- Resolves jaegertracing#705
- Resolves jaegertracing#704 

## Short description of the changes
- Instead of creating a separate column in the table, [we encode
warnings as tags, with a magic string
prefix](jaegertracing#4217 (comment))
so that they can be parsed and converted back to warnings during reads.

---------

Signed-off-by: Utsav Oza <utsavoza96@gmail.com>
  • Loading branch information
utsavoza authored Mar 19, 2023
1 parent 43c29a0 commit b994aed
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 3 deletions.
49 changes: 47 additions & 2 deletions plugin/storage/cassandra/spanstore/dbmodel/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ package dbmodel

import (
"fmt"
"strings"

"github.com/jaegertracing/jaeger/model"
)

const (
// warningStringPrefix is a magic string prefix for tag names to store span warnings.
warningStringPrefix = "$$span.warning."
)

var (
dbToDomainRefMap = map[string]model.SpanRefType{
childOf: model.SpanRefType_CHILD_OF,
Expand Down Expand Up @@ -57,11 +63,14 @@ type converter struct{}

func (c converter) fromDomain(span *model.Span) *Span {
tags := c.toDBTags(span.Tags)
warnings := c.toDBWarnings(span.Warnings)
logs := c.toDBLogs(span.Logs)
refs := c.toDBRefs(span.References)
udtProcess := c.toDBProcess(span.Process)
spanHash, _ := model.HashCode(span)

tags = append(tags, warnings...)

return &Span{
TraceID: TraceIDFromDomain(span.TraceID),
SpanID: int64(span.SpanID),
Expand All @@ -83,6 +92,10 @@ func (c converter) toDomain(dbSpan *Span) (*model.Span, error) {
if err != nil {
return nil, err
}
warnings, err := c.fromDBWarnings(dbSpan.Tags)
if err != nil {
return nil, err
}
logs, err := c.fromDBLogs(dbSpan.Logs)
if err != nil {
return nil, err
Expand All @@ -105,20 +118,39 @@ func (c converter) toDomain(dbSpan *Span) (*model.Span, error) {
StartTime: model.EpochMicrosecondsAsTime(uint64(dbSpan.StartTime)),
Duration: model.MicrosecondsAsDuration(uint64(dbSpan.Duration)),
Tags: tags,
Warnings: warnings,
Logs: logs,
Process: process,
}
return span, nil
}

func (c converter) fromDBTags(tags []KeyValue) ([]model.KeyValue, error) {
retMe := make([]model.KeyValue, len(tags))
retMe := make([]model.KeyValue, 0, len(tags))
for i := range tags {
if strings.HasPrefix(tags[i].Key, warningStringPrefix) {
continue
}
kv, err := c.fromDBTag(&tags[i])
if err != nil {
return nil, err
}
retMe[i] = kv
retMe = append(retMe, kv)
}
return retMe, nil
}

func (c converter) fromDBWarnings(tags []KeyValue) ([]string, error) {
var retMe []string
for _, tag := range tags {
if !strings.HasPrefix(tag.Key, warningStringPrefix) {
continue
}
kv, err := c.fromDBTag(&tag)
if err != nil {
return nil, err
}
retMe = append(retMe, kv.VStr)
}
return retMe, nil
}
Expand Down Expand Up @@ -198,6 +230,19 @@ func (c converter) toDBTags(tags []model.KeyValue) []KeyValue {
return retMe
}

func (c converter) toDBWarnings(warnings []string) []KeyValue {
retMe := make([]KeyValue, len(warnings))
for i, w := range warnings {
kv := model.String(fmt.Sprintf("%s%d", warningStringPrefix, i+1), w)
retMe[i] = KeyValue{
Key: kv.Key,
ValueType: domainToDBValueTypeMap[kv.VType],
ValueString: kv.VStr,
}
}
return retMe
}

func (c converter) toDBLogs(logs []model.Log) []Log {
retMe := make([]Log, len(logs))
for i, l := range logs {
Expand Down
58 changes: 57 additions & 1 deletion plugin/storage/cassandra/spanstore/dbmodel/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ var (
model.Float64(someDoubleTagKey, someDoubleTagValue),
model.Binary(someBinaryTagKey, someBinaryTagValue),
}
someDBTags = []KeyValue{
someWarnings = []string{"warning text 1", "warning text 2"}
someDBTags = []KeyValue{
{
Key: someStringTagKey,
ValueType: stringType,
Expand Down Expand Up @@ -288,3 +289,58 @@ func TestGenerateHashCode(t *testing.T) {
assert.NotEqual(t, hc1, hc2)
assert.NoError(t, err2)
}

func TestFromDBTagsWithoutWarnings(t *testing.T) {
span := getTestJaegerSpan()
dbSpan := FromDomain(span)

tags, err := converter{}.fromDBTags(dbSpan.Tags)
assert.NoError(t, err)
assert.Equal(t, tags, span.Tags)
}

func TestFromDBTagsWithWarnings(t *testing.T) {
span := getTestJaegerSpan()
span.Warnings = someWarnings
dbSpan := FromDomain(span)

tags, err := converter{}.fromDBTags(dbSpan.Tags)
assert.NoError(t, err)
assert.Equal(t, tags, span.Tags)
}

func TestFromDBLogsWithWarnings(t *testing.T) {
span := getTestJaegerSpan()
span.Warnings = someWarnings
dbSpan := FromDomain(span)

logs, err := converter{}.fromDBLogs(dbSpan.Logs)
assert.NoError(t, err)
assert.Equal(t, logs, span.Logs)
}

func TestFromDBProcessWithWarnings(t *testing.T) {
span := getTestJaegerSpan()
span.Warnings = someWarnings
dbSpan := FromDomain(span)

process, err := converter{}.fromDBProcess(dbSpan.Process)
assert.NoError(t, err)
assert.Equal(t, process, span.Process)
}

func TestFromDBWarnings(t *testing.T) {
span := getTestJaegerSpan()
span.Warnings = someWarnings
dbSpan := FromDomain(span)

warnings, err := converter{}.fromDBWarnings(dbSpan.Tags)
assert.NoError(t, err)
assert.Equal(t, warnings, span.Warnings)
}

func TestFailingFromDBWarnings(t *testing.T) {
badDBWarningTags := []KeyValue{{Key: warningStringPrefix + "1", ValueType: "invalidValueType"}}
span := getCustomSpan(badDBWarningTags, someDBProcess, someDBLogs, someDBRefs)
failingDBSpanTransform(t, span, notValidTagTypeErrStr)
}

0 comments on commit b994aed

Please sign in to comment.