diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/model.go b/internal/storage/v1/cassandra/spanstore/dbmodel/model.go index 127444d5a82..00df21668eb 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/model.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/model.go @@ -7,6 +7,10 @@ package dbmodel import ( "bytes" "encoding/binary" + "encoding/hex" + "sort" + "strconv" + "strings" "github.com/jaegertracing/jaeger-idl/model/v1" ) @@ -53,6 +57,96 @@ type KeyValue struct { ValueBinary []byte `cql:"value_binary"` } +func (t *KeyValue) compareValues(that *KeyValue) int { + switch t.ValueType { + case stringType: + return strings.Compare(t.ValueString, that.ValueString) + case boolType: + if t.ValueBool != that.ValueBool { + if !t.ValueBool { + return -1 + } + return 1 + } + case int64Type: + return int(t.ValueInt64 - that.ValueInt64) + case float64Type: + if t.ValueFloat64 != that.ValueFloat64 { + if t.ValueFloat64 < that.ValueFloat64 { + return -1 + } + return 1 + } + case binaryType: + return bytes.Compare(t.ValueBinary, that.ValueBinary) + default: + return -1 // theoretical case, not stating them equal but placing the base pointer before other + } + return 0 +} + +func (t *KeyValue) Compare(that any) int { + if that == nil { + if t == nil { + return 0 + } + return 1 + } + that1, ok := that.(*KeyValue) + if !ok { + that2, ok := that.(KeyValue) + if !ok { + return 1 + } + that1 = &that2 + } + if that1 == nil { + if t == nil { + return 0 + } + return 1 + } else if t == nil { + return -1 + } + if cmp := strings.Compare(t.Key, that1.Key); cmp != 0 { + return cmp + } + if cmp := strings.Compare(t.ValueType, that1.ValueType); cmp != 0 { + return cmp + } + return t.compareValues(that1) +} + +func (t *KeyValue) Equal(that any) bool { + return t.Compare(that) == 0 +} + +func (t *KeyValue) AsString() string { + switch t.ValueType { + case stringType: + return t.ValueString + case boolType: + if t.ValueBool { + return "true" + } + return "false" + case int64Type: + return strconv.FormatInt(t.ValueInt64, 10) + case float64Type: + return strconv.FormatFloat(t.ValueFloat64, 'g', 10, 64) + case binaryType: + return hex.EncodeToString(t.ValueBinary) + default: + return "unknown type " + t.ValueType + } +} + +func SortKVs(kvs []KeyValue) { + sort.Slice(kvs, func(i, j int) bool { + return kvs[i].Compare(kvs[j]) < 0 + }) +} + // Log is the UDT representation of a Jaeger Log. type Log struct { Timestamp int64 `cql:"ts"` // microseconds since epoch diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/model_test.go b/internal/storage/v1/cassandra/spanstore/dbmodel/model_test.go index f79ea19b95b..4bd68e242c4 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/model_test.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/model_test.go @@ -5,6 +5,7 @@ package dbmodel import ( + "bytes" "testing" "github.com/stretchr/testify/assert" @@ -21,3 +22,382 @@ func TestTraceIDString(t *testing.T) { id := TraceIDFromDomain(model.NewTraceID(1, 1)) assert.Equal(t, "00000000000000010000000000000001", id.String()) } + +func TestKeyValueCompare(t *testing.T) { + tests := []struct { + name string + kv1 *KeyValue + kv2 any + result int + }{ + { + name: "BothNil", + kv1: nil, + kv2: nil, + result: 0, + }, + { + name: "Nil_vs_Value", + kv1: nil, + kv2: &KeyValue{Key: "k", ValueType: "string"}, + result: -1, + }, + { + name: "Pointer_vs_Value", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: KeyValue{Key: "m", ValueType: "string"}, + result: -1, + }, + { + name: "Value_vs_Nil", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: nil, + result: 1, + }, + { + name: "TypedNil_vs_Value", + kv1: (*KeyValue)(nil), + kv2: &KeyValue{Key: "k", ValueType: "string"}, + result: -1, + }, + { + name: "TypedNil_vs_TypedNil", + kv1: (*KeyValue)(nil), + kv2: (*KeyValue)(nil), + result: 0, + }, + { + name: "Value_vs_TypedNil", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: (*KeyValue)(nil), + result: 1, + }, + { + name: "InvalidType", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: 123, + result: 1, + }, + { + name: "Equal", + kv1: &KeyValue{ + Key: "k", + ValueType: "string", + ValueString: "hello", + }, + kv2: &KeyValue{ + Key: "k", + ValueType: "string", + ValueString: "hello", + }, + result: 0, + }, + { + name: "KeyMismatch", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: &KeyValue{Key: "a", ValueType: "string"}, + result: 1, + }, + { + name: "ValueTypeMismatch", + kv1: &KeyValue{Key: "k", ValueType: "z"}, + kv2: &KeyValue{Key: "k", ValueType: "a"}, + result: 1, + }, + { + name: "ValueStringMismatch", + kv1: &KeyValue{Key: "k", ValueType: "string", ValueString: "zzz"}, + kv2: &KeyValue{Key: "k", ValueType: "string", ValueString: "aaa"}, + result: 1, + }, + { + name: "ValueBoolMismatch_After", + kv1: &KeyValue{Key: "k", ValueType: "bool", ValueBool: true}, + kv2: &KeyValue{Key: "k", ValueType: "bool", ValueBool: false}, + result: 1, + }, + { + name: "ValueBoolMismatch_Before", + kv1: &KeyValue{Key: "k", ValueType: "bool", ValueBool: false}, + kv2: &KeyValue{Key: "k", ValueType: "bool", ValueBool: true}, + result: -1, + }, + { + name: "ValueInt64Mismatch_After", + kv1: &KeyValue{Key: "k", ValueType: "int64", ValueInt64: 10}, + kv2: &KeyValue{Key: "k", ValueType: "int64", ValueInt64: 5}, + result: 5, + }, + { + name: "ValueFloat64Mismatch_After", + kv1: &KeyValue{Key: "k", ValueType: "float64", ValueFloat64: 1.5}, + kv2: &KeyValue{Key: "k", ValueType: "float64", ValueFloat64: 0.5}, + result: 1, + }, + { + name: "ValueFloat64Mismatch_Before", + kv1: &KeyValue{Key: "k", ValueType: "float64", ValueFloat64: 0.5}, + kv2: &KeyValue{Key: "k", ValueType: "float64", ValueFloat64: 1.5}, + result: -1, + }, + { + name: "ValueBinaryMismatch", + kv1: &KeyValue{Key: "k", ValueType: "binary", ValueBinary: []byte{1, 2, 3}}, + kv2: &KeyValue{Key: "k", ValueType: "binary", ValueBinary: []byte{1, 2, 4}}, + result: bytes.Compare([]byte{1, 2, 3}, []byte{1, 2, 4}), + }, + { + name: "ValueBinaryEqual", + kv1: &KeyValue{Key: "k", ValueType: "binary", ValueBinary: []byte{1, 2, 3}}, + kv2: &KeyValue{Key: "k", ValueType: "binary", ValueBinary: []byte{1, 2, 3}}, + result: 0, + }, + { + name: "UnknownType", + kv1: &KeyValue{Key: "k", ValueType: "random", ValueString: "hello"}, + kv2: &KeyValue{Key: "k", ValueType: "random", ValueString: "hellobig"}, + result: -1, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.result, tc.kv1.Compare(tc.kv2)) + }) + } +} + +func TestKeyValueEqual(t *testing.T) { + tests := []struct { + name string + kv1 *KeyValue + kv2 any + result bool + }{ + { + name: "BothNil", + kv1: nil, + kv2: nil, + result: true, + }, + { + name: "Nil_vs_Value", + kv1: nil, + kv2: &KeyValue{Key: "k", ValueType: "string"}, + result: false, + }, + { + name: "Value_vs_Nil", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: nil, + result: false, + }, + { + name: "TypedNil_vs_Value", + kv1: (*KeyValue)(nil), + kv2: &KeyValue{Key: "k", ValueType: "string"}, + result: false, + }, + { + name: "Value_vs_TypedNil", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: (*KeyValue)(nil), + result: false, + }, + { + name: "InvalidType", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: 123, + result: false, + }, + { + name: "Equal", + kv1: &KeyValue{ + Key: "k", + ValueType: "string", + ValueString: "hello", + }, + kv2: &KeyValue{ + Key: "k", + ValueType: "string", + ValueString: "hello", + }, + result: true, + }, + { + name: "KeyMismatch", + kv1: &KeyValue{Key: "k", ValueType: "string"}, + kv2: &KeyValue{Key: "a", ValueType: "string"}, + result: false, + }, + { + name: "ValueTypeMismatch", + kv1: &KeyValue{Key: "k", ValueType: "z"}, + kv2: &KeyValue{Key: "k", ValueType: "a"}, + result: false, + }, + { + name: "ValueStringMismatch", + kv1: &KeyValue{Key: "k", ValueType: "string", ValueString: "zzz"}, + kv2: &KeyValue{Key: "k", ValueType: "string", ValueString: "aaa"}, + result: false, + }, + { + name: "ValueBoolMismatch", + kv1: &KeyValue{Key: "k", ValueType: "bool", ValueBool: true}, + kv2: &KeyValue{Key: "k", ValueType: "bool", ValueBool: false}, + result: false, + }, + { + name: "ValueInt64Mismatch", + kv1: &KeyValue{Key: "k", ValueType: "int64", ValueInt64: 10}, + kv2: &KeyValue{Key: "k", ValueType: "int64", ValueInt64: 5}, + result: false, + }, + { + name: "ValueFloat64Mismatch", + kv1: &KeyValue{Key: "k", ValueType: "float64", ValueFloat64: 1.5}, + kv2: &KeyValue{Key: "k", ValueType: "float64", ValueFloat64: 0.5}, + result: false, + }, + { + name: "ValueBinaryMismatch", + kv1: &KeyValue{Key: "k", ValueType: "binary", ValueBinary: []byte{1, 2, 3}}, + kv2: &KeyValue{Key: "k", ValueType: "binary", ValueBinary: []byte{1, 2, 4}}, + result: false, + }, + { + name: "ValueBinaryEqual", + kv1: &KeyValue{Key: "k", ValueType: "binary", ValueBinary: []byte{1, 2, 3}}, + kv2: &KeyValue{Key: "k", ValueType: "binary", ValueBinary: []byte{1, 2, 3}}, + result: true, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.result, tc.kv1.Equal(tc.kv2)) + }) + } +} + +func TestKeyValueAsString(t *testing.T) { + tests := []struct { + name string + kv KeyValue + expect string + }{ + { + name: "StringType", + kv: KeyValue{ + Key: "k", + ValueType: stringType, + ValueString: "hello", + }, + expect: "hello", + }, + { + name: "BoolTrue", + kv: KeyValue{ + Key: "k", + ValueType: boolType, + ValueBool: true, + }, + expect: "true", + }, + { + name: "BoolFalse", + kv: KeyValue{ + Key: "k", + ValueType: boolType, + ValueBool: false, + }, + expect: "false", + }, + { + name: "Int64Type", + kv: KeyValue{ + Key: "k", + ValueType: int64Type, + ValueInt64: 12345, + }, + expect: "12345", + }, + { + name: "Float64Type", + kv: KeyValue{ + Key: "k", + ValueType: float64Type, + ValueFloat64: 12.34, + }, + expect: "12.34", + }, + { + name: "BinaryType", + kv: KeyValue{ + Key: "k", + ValueType: binaryType, + ValueBinary: []byte{0xAB, 0xCD, 0xEF}, + }, + expect: "abcdef", + }, + { + name: "UnknownType", + kv: KeyValue{ + Key: "k", + ValueType: "random-type", + }, + expect: "unknown type random-type", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expect, tc.kv.AsString()) + }) + } +} + +func TestSortKVs_WithKey(t *testing.T) { + kvs := []KeyValue{ + {Key: "z", ValueType: "string", ValueString: "hello"}, + {Key: "y", ValueType: "bool", ValueBool: true}, + {Key: "x", ValueType: "int64", ValueInt64: 99}, + {Key: "w", ValueType: "double", ValueFloat64: 1.23}, + {Key: "v", ValueType: "binary", ValueBinary: []byte{1, 2, 3}}, + {Key: "m", ValueType: "string", ValueString: "abc"}, + } + SortKVs(kvs) + want := []string{"m", "v", "w", "x", "y", "z"} + for i, kv := range kvs { + assert.Equal(t, want[i], kv.Key) + } +} + +func TestSortKVs_WithType(t *testing.T) { + kvs := []KeyValue{ + {Key: "m", ValueType: "string", ValueString: "hello"}, + {Key: "m", ValueType: "bool", ValueBool: true}, + {Key: "m", ValueType: "int64", ValueInt64: 99}, + {Key: "m", ValueType: "double", ValueFloat64: 1.23}, + {Key: "m", ValueType: "binary", ValueBinary: []byte{1, 2, 3}}, + } + SortKVs(kvs) + want := []string{"binary", "bool", "double", "int64", "string"} + for i, kv := range kvs { + assert.Equal(t, want[i], kv.ValueType) + } +} + +func TestSortKVs_WithValue(t *testing.T) { + kvs := []KeyValue{ + {Key: "m", ValueType: "string", ValueString: "a"}, + {Key: "m", ValueType: "string", ValueString: "b"}, + {Key: "m", ValueType: "string", ValueString: "c"}, + {Key: "m", ValueType: "string", ValueString: "d"}, + {Key: "m", ValueType: "string", ValueString: "e"}, + } + SortKVs(kvs) + want := []string{"a", "b", "c", "d", "e"} + for i, kv := range kvs { + assert.Equal(t, want[i], kv.ValueString) + } +} diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter.go b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter.go index 316875154ae..31a5e0605b2 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter.go @@ -4,15 +4,11 @@ package dbmodel -import ( - "github.com/jaegertracing/jaeger-idl/model/v1" -) - // TagFilter filters out any tags that should not be indexed. type TagFilter interface { - FilterProcessTags(span *model.Span, processTags model.KeyValues) model.KeyValues - FilterTags(span *model.Span, tags model.KeyValues) model.KeyValues - FilterLogFields(span *model.Span, logFields model.KeyValues) model.KeyValues + FilterProcessTags(span *Span, processTags []KeyValue) []KeyValue + FilterTags(span *Span, tags []KeyValue) []KeyValue + FilterLogFields(span *Span, logFields []KeyValue) []KeyValue } // ChainedTagFilter applies multiple tag filters in serial fashion. @@ -24,7 +20,7 @@ func NewChainedTagFilter(filters ...TagFilter) ChainedTagFilter { } // FilterProcessTags calls each FilterProcessTags. -func (tf ChainedTagFilter) FilterProcessTags(span *model.Span, processTags model.KeyValues) model.KeyValues { +func (tf ChainedTagFilter) FilterProcessTags(span *Span, processTags []KeyValue) []KeyValue { for _, f := range tf { processTags = f.FilterProcessTags(span, processTags) } @@ -32,7 +28,7 @@ func (tf ChainedTagFilter) FilterProcessTags(span *model.Span, processTags model } // FilterTags calls each FilterTags -func (tf ChainedTagFilter) FilterTags(span *model.Span, tags model.KeyValues) model.KeyValues { +func (tf ChainedTagFilter) FilterTags(span *Span, tags []KeyValue) []KeyValue { for _, f := range tf { tags = f.FilterTags(span, tags) } @@ -40,7 +36,7 @@ func (tf ChainedTagFilter) FilterTags(span *model.Span, tags model.KeyValues) mo } // FilterLogFields calls each FilterLogFields -func (tf ChainedTagFilter) FilterLogFields(span *model.Span, logFields model.KeyValues) model.KeyValues { +func (tf ChainedTagFilter) FilterLogFields(span *Span, logFields []KeyValue) []KeyValue { for _, f := range tf { logFields = f.FilterLogFields(span, logFields) } @@ -52,14 +48,14 @@ var DefaultTagFilter = tagFilterImpl{} type tagFilterImpl struct{} -func (tagFilterImpl) FilterProcessTags(_ *model.Span, processTags model.KeyValues) model.KeyValues { +func (tagFilterImpl) FilterProcessTags(_ *Span, processTags []KeyValue) []KeyValue { return processTags } -func (tagFilterImpl) FilterTags(_ *model.Span, tags model.KeyValues) model.KeyValues { +func (tagFilterImpl) FilterTags(_ *Span, tags []KeyValue) []KeyValue { return tags } -func (tagFilterImpl) FilterLogFields(_ *model.Span, logFields model.KeyValues) model.KeyValues { +func (tagFilterImpl) FilterLogFields(_ *Span, logFields []KeyValue) []KeyValue { return logFields } diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_drop_all.go b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_drop_all.go index df1920042e8..e3ac47822cf 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_drop_all.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_drop_all.go @@ -3,10 +3,6 @@ package dbmodel -import ( - "github.com/jaegertracing/jaeger-idl/model/v1" -) - // TagFilterDropAll filters all fields of a given type. type TagFilterDropAll struct { dropTags bool @@ -24,25 +20,25 @@ func NewTagFilterDropAll(dropTags bool, dropProcessTags bool, dropLogs bool) *Ta } // FilterProcessTags implements TagFilter -func (f *TagFilterDropAll) FilterProcessTags(_ *model.Span, processTags model.KeyValues) model.KeyValues { +func (f *TagFilterDropAll) FilterProcessTags(_ *Span, processTags []KeyValue) []KeyValue { if f.dropProcessTags { - return model.KeyValues{} + return []KeyValue{} } return processTags } // FilterTags implements TagFilter -func (f *TagFilterDropAll) FilterTags(_ *model.Span, tags model.KeyValues) model.KeyValues { +func (f *TagFilterDropAll) FilterTags(_ *Span, tags []KeyValue) []KeyValue { if f.dropTags { - return model.KeyValues{} + return []KeyValue{} } return tags } // FilterLogFields implements TagFilter -func (f *TagFilterDropAll) FilterLogFields(_ *model.Span, logFields model.KeyValues) model.KeyValues { +func (f *TagFilterDropAll) FilterLogFields(_ *Span, logFields []KeyValue) []KeyValue { if f.dropLogs { - return model.KeyValues{} + return []KeyValue{} } return logFields } diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_drop_all_test.go b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_drop_all_test.go index 19f3df4c9ef..00d6baa79c1 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_drop_all_test.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_drop_all_test.go @@ -7,73 +7,63 @@ import ( "testing" "github.com/stretchr/testify/assert" - - "github.com/jaegertracing/jaeger-idl/model/v1" ) var _ TagFilter = &TagFilterDropAll{} // Check API compliance func TestDropAll(t *testing.T) { - sampleTags := model.KeyValues{ - model.String(someStringTagKey, someStringTagValue), - model.Bool(someBoolTagKey, someBoolTagValue), - model.Int64(someLongTagKey, someLongTagValue), - model.Float64(someDoubleTagKey, someDoubleTagValue), - model.Binary(someBinaryTagKey, someBinaryTagValue), - } - tt := []struct { filter *TagFilterDropAll - expectedTags model.KeyValues - expectedProcessTags model.KeyValues - expectedLogs model.KeyValues + expectedTags []KeyValue + expectedProcessTags []KeyValue + expectedLogs []KeyValue }{ { filter: NewTagFilterDropAll(false, false, false), - expectedTags: sampleTags, - expectedProcessTags: sampleTags, - expectedLogs: sampleTags, + expectedTags: someDBTags, + expectedProcessTags: someDBTags, + expectedLogs: someDBTags, }, { filter: NewTagFilterDropAll(true, false, false), - expectedTags: model.KeyValues{}, - expectedProcessTags: sampleTags, - expectedLogs: sampleTags, + expectedTags: []KeyValue{}, + expectedProcessTags: someDBTags, + expectedLogs: someDBTags, }, { filter: NewTagFilterDropAll(false, true, false), - expectedTags: sampleTags, - expectedProcessTags: model.KeyValues{}, - expectedLogs: sampleTags, + expectedTags: someDBTags, + expectedProcessTags: []KeyValue{}, + expectedLogs: someDBTags, }, { filter: NewTagFilterDropAll(false, false, true), - expectedTags: sampleTags, - expectedProcessTags: sampleTags, - expectedLogs: model.KeyValues{}, + expectedTags: someDBTags, + expectedProcessTags: someDBTags, + expectedLogs: []KeyValue{}, }, { filter: NewTagFilterDropAll(true, false, true), - expectedTags: model.KeyValues{}, - expectedProcessTags: sampleTags, - expectedLogs: model.KeyValues{}, + expectedTags: []KeyValue{}, + expectedProcessTags: someDBTags, + expectedLogs: []KeyValue{}, }, { filter: NewTagFilterDropAll(true, true, true), - expectedTags: model.KeyValues{}, - expectedProcessTags: model.KeyValues{}, - expectedLogs: model.KeyValues{}, + expectedTags: []KeyValue{}, + expectedProcessTags: []KeyValue{}, + expectedLogs: []KeyValue{}, }, } for _, test := range tt { - actualTags := test.filter.FilterTags(nil, sampleTags) + actualTags := test.filter.FilterTags(nil, someDBTags) assert.Equal(t, test.expectedTags, actualTags) - actualProcessTags := test.filter.FilterProcessTags(nil, sampleTags) + actualProcessTags := test.filter.FilterProcessTags(nil, someDBTags) assert.Equal(t, test.expectedProcessTags, actualProcessTags) - actualLogs := test.filter.FilterLogFields(nil, sampleTags) + actualLogs := test.filter.FilterLogFields(nil, someDBTags) assert.Equal(t, test.expectedLogs, actualLogs) } } diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_exact_match.go b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_exact_match.go index 1752ffd8cba..8942373b3f8 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_exact_match.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_exact_match.go @@ -3,10 +3,6 @@ package dbmodel -import ( - "github.com/jaegertracing/jaeger-idl/model/v1" -) - // ExactMatchTagFilter filters out all tags in its tags slice type ExactMatchTagFilter struct { tags map[string]struct{} @@ -38,22 +34,22 @@ func NewWhitelistFilter(tags []string) ExactMatchTagFilter { } // FilterProcessTags implements TagFilter -func (tf ExactMatchTagFilter) FilterProcessTags(_ *model.Span, processTags model.KeyValues) model.KeyValues { +func (tf ExactMatchTagFilter) FilterProcessTags(_ *Span, processTags []KeyValue) []KeyValue { return tf.filter(processTags) } // FilterTags implements TagFilter -func (tf ExactMatchTagFilter) FilterTags(_ *model.Span, tags model.KeyValues) model.KeyValues { +func (tf ExactMatchTagFilter) FilterTags(_ *Span, tags []KeyValue) []KeyValue { return tf.filter(tags) } // FilterLogFields implements TagFilter -func (tf ExactMatchTagFilter) FilterLogFields(_ *model.Span, logFields model.KeyValues) model.KeyValues { +func (tf ExactMatchTagFilter) FilterLogFields(_ *Span, logFields []KeyValue) []KeyValue { return tf.filter(logFields) } -func (tf ExactMatchTagFilter) filter(tags model.KeyValues) model.KeyValues { - var filteredTags model.KeyValues +func (tf ExactMatchTagFilter) filter(tags []KeyValue) []KeyValue { + var filteredTags []KeyValue for _, t := range tags { if _, ok := tf.tags[t.Key]; ok == !tf.dropMatches { filteredTags = append(filteredTags, t) diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_exact_match_test.go b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_exact_match_test.go index 142df8e2a70..5dfef02c5b8 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_exact_match_test.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_exact_match_test.go @@ -7,8 +7,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - - "github.com/jaegertracing/jaeger-idl/model/v1" ) func TestBlacklistFilter(t *testing.T) { @@ -30,31 +28,31 @@ func TestBlacklistFilter(t *testing.T) { } for _, test := range tt { - var inputKVs model.KeyValues + var inputKVs []KeyValue for _, i := range test.input { - inputKVs = append(inputKVs, model.String(i, "")) + inputKVs = append(inputKVs, KeyValue{Key: i, ValueType: stringType, ValueString: ""}) } - var expectedKVs model.KeyValues + var expectedKVs []KeyValue for _, e := range test.expected { - expectedKVs = append(expectedKVs, model.String(e, "")) + expectedKVs = append(expectedKVs, KeyValue{Key: e, ValueType: stringType, ValueString: ""}) } - expectedKVs.Sort() + SortKVs(expectedKVs) tf := NewBlacklistFilter(test.filter) actualKVs := tf.filter(inputKVs) - actualKVs.Sort() + SortKVs(actualKVs) assert.Equal(t, expectedKVs, actualKVs) actualKVs = tf.FilterLogFields(nil, inputKVs) - actualKVs.Sort() + SortKVs(actualKVs) assert.Equal(t, expectedKVs, actualKVs) actualKVs = tf.FilterProcessTags(nil, inputKVs) - actualKVs.Sort() + SortKVs(actualKVs) assert.Equal(t, expectedKVs, actualKVs) actualKVs = tf.FilterTags(nil, inputKVs) - actualKVs.Sort() + SortKVs(actualKVs) assert.Equal(t, expectedKVs, actualKVs) } } @@ -78,31 +76,31 @@ func TestWhitelistFilter(t *testing.T) { } for _, test := range tt { - var inputKVs model.KeyValues + var inputKVs []KeyValue for _, i := range test.input { - inputKVs = append(inputKVs, model.String(i, "")) + inputKVs = append(inputKVs, KeyValue{Key: i, ValueType: stringType, ValueString: ""}) } - var expectedKVs model.KeyValues + var expectedKVs []KeyValue for _, e := range test.expected { - expectedKVs = append(expectedKVs, model.String(e, "")) + expectedKVs = append(expectedKVs, KeyValue{Key: e, ValueType: stringType, ValueString: ""}) } - expectedKVs.Sort() + SortKVs(expectedKVs) tf := NewWhitelistFilter(test.filter) actualKVs := tf.filter(inputKVs) - actualKVs.Sort() + SortKVs(actualKVs) assert.Equal(t, expectedKVs, actualKVs) actualKVs = tf.FilterLogFields(nil, inputKVs) - actualKVs.Sort() + SortKVs(actualKVs) assert.Equal(t, expectedKVs, actualKVs) actualKVs = tf.FilterProcessTags(nil, inputKVs) - actualKVs.Sort() + SortKVs(actualKVs) assert.Equal(t, expectedKVs, actualKVs) actualKVs = tf.FilterTags(nil, inputKVs) - actualKVs.Sort() + SortKVs(actualKVs) assert.Equal(t, expectedKVs, actualKVs) } } diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_test.go b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_test.go index 807f79dc1a0..76db3370312 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_test.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/tag_filter_test.go @@ -9,13 +9,11 @@ import ( "github.com/kr/pretty" "github.com/stretchr/testify/assert" - - "github.com/jaegertracing/jaeger-idl/model/v1" ) func TestDefaultTagFilter(t *testing.T) { - span := getTestJaegerSpan() - expectedTags := append(append(someTags, someTags...), someTags...) + span := getTestSpan() + expectedTags := append(append(someDBTags, someDBTags...), someDBTags...) filteredTags := DefaultTagFilter.FilterProcessTags(span, span.Process.Tags) filteredTags = append(filteredTags, DefaultTagFilter.FilterTags(span, span.Tags)...) for _, log := range span.Logs { @@ -26,40 +24,40 @@ func TestDefaultTagFilter(t *testing.T) { type onlyStringsFilter struct{} -func (onlyStringsFilter) filterStringTags(tags model.KeyValues) model.KeyValues { - var ret model.KeyValues +func (onlyStringsFilter) filterStringTags(tags []KeyValue) []KeyValue { + var ret []KeyValue for _, tag := range tags { - if tag.VType == model.StringType { + if tag.ValueType == stringType { ret = append(ret, tag) } } return ret } -func (f onlyStringsFilter) FilterProcessTags(_ *model.Span, processTags model.KeyValues) model.KeyValues { +func (f onlyStringsFilter) FilterProcessTags(_ *Span, processTags []KeyValue) []KeyValue { return f.filterStringTags(processTags) } -func (f onlyStringsFilter) FilterTags(_ *model.Span, tags model.KeyValues) model.KeyValues { +func (f onlyStringsFilter) FilterTags(_ *Span, tags []KeyValue) []KeyValue { return f.filterStringTags(tags) } -func (f onlyStringsFilter) FilterLogFields(_ *model.Span, logFields model.KeyValues) model.KeyValues { +func (f onlyStringsFilter) FilterLogFields(_ *Span, logFields []KeyValue) []KeyValue { return f.filterStringTags(logFields) } func TestChainedTagFilter(t *testing.T) { - expectedTags := model.KeyValues{model.String(someStringTagKey, someStringTagValue)} + expectedTags := []KeyValue{{Key: someStringTagKey, ValueType: stringType, ValueString: someStringTagValue}} filter := NewChainedTagFilter(DefaultTagFilter, onlyStringsFilter{}) - filteredTags := filter.FilterProcessTags(nil, someTags) + filteredTags := filter.FilterProcessTags(nil, someDBTags) compareTags(t, expectedTags, filteredTags) - filteredTags = filter.FilterTags(nil, someTags) + filteredTags = filter.FilterTags(nil, someDBTags) compareTags(t, expectedTags, filteredTags) - filteredTags = filter.FilterLogFields(nil, someTags) + filteredTags = filter.FilterLogFields(nil, someDBTags) compareTags(t, expectedTags, filteredTags) } -func compareTags(t *testing.T, expected, actual model.KeyValues) { +func compareTags(t *testing.T, expected, actual []KeyValue) { if !assert.Equal(t, expected, actual) { for _, diff := range pretty.Diff(expected, actual) { t.Log(diff) diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/unique_tags.go b/internal/storage/v1/cassandra/spanstore/dbmodel/unique_tags.go index 4b5abb3be4c..2ab3ddaa7db 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/unique_tags.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/unique_tags.go @@ -4,21 +4,17 @@ package dbmodel -import ( - "github.com/jaegertracing/jaeger-idl/model/v1" -) - // GetAllUniqueTags creates a list of all unique tags from a set of filtered tags. -func GetAllUniqueTags(span *model.Span, tagFilter TagFilter) []TagInsertion { - allTags := append(model.KeyValues{}, tagFilter.FilterProcessTags(span, span.Process.Tags)...) +func GetAllUniqueTags(span *Span, tagFilter TagFilter) []TagInsertion { + allTags := append([]KeyValue{}, tagFilter.FilterProcessTags(span, span.Process.Tags)...) allTags = append(allTags, tagFilter.FilterTags(span, span.Tags)...) for _, log := range span.Logs { allTags = append(allTags, tagFilter.FilterLogFields(span, log.Fields)...) } - allTags.Sort() + SortKVs(allTags) uniqueTags := make([]TagInsertion, 0, len(allTags)) for i := range allTags { - if allTags[i].VType == model.BinaryType { + if allTags[i].ValueType == binaryType { continue // do not index binary tags } if i > 0 && allTags[i-1].Equal(&allTags[i]) { diff --git a/internal/storage/v1/cassandra/spanstore/dbmodel/unique_tags_test.go b/internal/storage/v1/cassandra/spanstore/dbmodel/unique_tags_test.go index ac3ec860c35..afb418cb9e8 100644 --- a/internal/storage/v1/cassandra/spanstore/dbmodel/unique_tags_test.go +++ b/internal/storage/v1/cassandra/spanstore/dbmodel/unique_tags_test.go @@ -13,7 +13,7 @@ import ( func TestGetUniqueTags(t *testing.T) { expectedTags := getTestUniqueTags() - uniqueTags := GetAllUniqueTags(getTestJaegerSpan(), DefaultTagFilter) + uniqueTags := GetAllUniqueTags(getTestSpan(), DefaultTagFilter) if !assert.Equal(t, expectedTags, uniqueTags) { for _, diff := range pretty.Diff(expectedTags, uniqueTags) { t.Log(diff) diff --git a/internal/storage/v1/cassandra/spanstore/writer.go b/internal/storage/v1/cassandra/spanstore/writer.go index 3eb2fc8ded5..fb2c520889e 100644 --- a/internal/storage/v1/cassandra/spanstore/writer.go +++ b/internal/storage/v1/cassandra/spanstore/writer.go @@ -194,7 +194,7 @@ func (s *SpanWriter) writeIndexes(span *model.Span, ds *dbmodel.Span) error { return nil // skipping expensive indexing } - if err := s.indexByTags(span, ds); err != nil { + if err := s.indexByTags(ds); err != nil { return s.logError(ds, err, "Failed to index tags", s.logger) } @@ -206,8 +206,8 @@ func (s *SpanWriter) writeIndexes(span *model.Span, ds *dbmodel.Span) error { return nil } -func (s *SpanWriter) indexByTags(span *model.Span, ds *dbmodel.Span) error { - for _, v := range dbmodel.GetAllUniqueTags(span, s.tagFilter) { +func (s *SpanWriter) indexByTags(ds *dbmodel.Span) error { + for _, v := range dbmodel.GetAllUniqueTags(ds, s.tagFilter) { // we should introduce retries or just ignore failures imo, retrying each individual tag insertion might be better // we should consider bucketing. if s.shouldIndexTag(v) {