Files
32517c119c Add vparquet4 encoding (#3368)
* Copy vparquet3 to vparquet4 folder

* Create initial vParquet4 encoding

* vParquet4: handle unsupported / dropped attributes (#3123)

* Count dropped attributes and store them in separate column

* Remove current support for arrays and kv-lists

* Store per-trace service statistics in vparquet4 blocks (#2941)

* Add support for attributes with array values (#3221)

* Implement single values as arrays and introduce type column

* Add support for array attributes

* Tests more array values and fix attribute conversion

* Use a combination of snappy and delta for ValueType

* Vendor module google/go-cmp/cmp/cmpopts

* Use all types of array attributes in TestTraceToParquet

* Improve TestFieldsAreCleared

* links and events schema changes (#3163)

* Add attributes to instrumentation scope (#3322)

* Fix typo in vParquet4 event name enconding (#3336)

* Precalculate and reuse the vParquet4 schema before opening blocks

* Convert block in vparquet4/test-data directory

* CHANGELOG.md

* Update test cases in BenchmarkBackendBlockTraceQL

* Unsupported attribute values are no longer dropped

* Replace ValueType column with IsArray column

* Skip service stats map allocation when none are present

---------

Signed-off-by: Andreas Gerstmayr <[email protected]>
Co-authored-by: Andreas Gerstmayr <[email protected]>
Co-authored-by: Jennie Pham <[email protected]>
Co-authored-by: Andreas Gerstmayr <[email protected]>
2024-05-07 10:01:03 +10:00

390 lines
15 KiB
Go

package vparquet4
import (
"testing"
v1 "github.com/grafana/tempo/pkg/tempopb/trace/v1"
"github.com/grafana/tempo/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAssignNestedSetModelBounds(t *testing.T) {
tests := []struct {
name string
trace [][]Span
expected [][]Span
expectedConnected bool
}{
{
name: "single span",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 2, ParentID: -1},
},
},
expectedConnected: true,
},
{
name: "linear trace",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa")},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 6, ParentID: -1},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), NestedSetLeft: 2, NestedSetRight: 5, ParentID: 1},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 3, NestedSetRight: 4, ParentID: 2},
},
},
expectedConnected: true,
},
{
name: "branched trace",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa")},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("eeeeeeee"), ParentSpanID: []byte("dddddddd")},
{SpanID: []byte("ffffffff"), ParentSpanID: []byte("dddddddd")},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 12, ParentID: -1},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), NestedSetLeft: 2, NestedSetRight: 11, ParentID: 1},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 3, NestedSetRight: 4, ParentID: 2},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 5, NestedSetRight: 10, ParentID: 2},
{SpanID: []byte("eeeeeeee"), ParentSpanID: []byte("dddddddd"), NestedSetLeft: 6, NestedSetRight: 7, ParentID: 5},
{SpanID: []byte("ffffffff"), ParentSpanID: []byte("dddddddd"), NestedSetLeft: 8, NestedSetRight: 9, ParentID: 5},
},
},
expectedConnected: true,
},
{
name: "multiple scope spans",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa")},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb")},
},
{
{SpanID: []byte("eeeeeeee"), ParentSpanID: []byte("dddddddd")},
{SpanID: []byte("ffffffff"), ParentSpanID: []byte("dddddddd")},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 12, ParentID: -1},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), NestedSetLeft: 2, NestedSetRight: 11, ParentID: 1},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 3, NestedSetRight: 4, ParentID: 2},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 5, NestedSetRight: 10, ParentID: 2},
},
{
{SpanID: []byte("eeeeeeee"), ParentSpanID: []byte("dddddddd"), NestedSetLeft: 6, NestedSetRight: 7, ParentID: 5},
{SpanID: []byte("ffffffff"), ParentSpanID: []byte("dddddddd"), NestedSetLeft: 8, NestedSetRight: 9, ParentID: 5},
},
},
expectedConnected: true,
},
{
name: "multiple roots",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa")},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("eeeeeeee"), ParentSpanID: []byte("dddddddd")},
{SpanID: []byte("ffffffff"), ParentSpanID: []byte("dddddddd")},
{SpanID: []byte("gggggggg")},
{SpanID: []byte("iiiiiiii"), ParentSpanID: []byte("hhhhhhhh")},
{SpanID: []byte("hhhhhhhh"), ParentSpanID: []byte("gggggggg")},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 12, ParentID: -1},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), NestedSetLeft: 2, NestedSetRight: 11, ParentID: 1},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 3, NestedSetRight: 4, ParentID: 2},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 5, NestedSetRight: 10, ParentID: 2},
{SpanID: []byte("eeeeeeee"), ParentSpanID: []byte("dddddddd"), NestedSetLeft: 6, NestedSetRight: 7, ParentID: 5},
{SpanID: []byte("ffffffff"), ParentSpanID: []byte("dddddddd"), NestedSetLeft: 8, NestedSetRight: 9, ParentID: 5},
{SpanID: []byte("gggggggg"), NestedSetLeft: 13, NestedSetRight: 18, ParentID: -1},
{SpanID: []byte("hhhhhhhh"), ParentSpanID: []byte("gggggggg"), NestedSetLeft: 14, NestedSetRight: 17, ParentID: 13},
{SpanID: []byte("iiiiiiii"), ParentSpanID: []byte("hhhhhhhh"), NestedSetLeft: 15, NestedSetRight: 16, ParentID: 14},
},
},
expectedConnected: true,
},
{
name: "interrupted",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa")},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("eeeeeeee"), ParentSpanID: []byte("xxxxxxxx")}, // <- interrupted
{SpanID: []byte("ffffffff"), ParentSpanID: []byte("eeeeeeee")},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 8, ParentID: -1},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), NestedSetLeft: 2, NestedSetRight: 7, ParentID: 1},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 3, NestedSetRight: 4, ParentID: 2},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 5, NestedSetRight: 6, ParentID: 2},
{SpanID: []byte("eeeeeeee"), ParentSpanID: []byte("xxxxxxxx")}, // <- interrupted
{SpanID: []byte("ffffffff"), ParentSpanID: []byte("eeeeeeee")},
},
},
expectedConnected: false,
},
{
name: "partially assigned",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 4},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), NestedSetLeft: 2, NestedSetRight: 0, ParentID: 1},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 4, ParentID: -1},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), NestedSetLeft: 2, NestedSetRight: 3, ParentID: 1},
},
},
expectedConnected: true,
},
{
name: "non unique IDs",
trace: [][]Span{
{
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), Kind: int(v1.Span_SPAN_KIND_CLIENT)},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb"), Kind: int(v1.Span_SPAN_KIND_SERVER)},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("aaaaaaaa")},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 10, ParentID: -1},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb"), Kind: int(v1.Span_SPAN_KIND_SERVER), NestedSetLeft: 3, NestedSetRight: 8, ParentID: 2},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), Kind: int(v1.Span_SPAN_KIND_CLIENT), NestedSetLeft: 2, NestedSetRight: 9, ParentID: 1},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 4, NestedSetRight: 5, ParentID: 3},
{SpanID: []byte("dddddddd"), ParentSpanID: []byte("bbbbbbbb"), NestedSetLeft: 6, NestedSetRight: 7, ParentID: 3},
},
},
expectedConnected: true,
},
{
name: "non unique IDs 2x",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), Kind: int(v1.Span_SPAN_KIND_CLIENT)},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb"), Kind: int(v1.Span_SPAN_KIND_SERVER)},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("cccccccc"), Kind: int(v1.Span_SPAN_KIND_SERVER)},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb"), Kind: int(v1.Span_SPAN_KIND_CLIENT)},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa"), NestedSetLeft: 1, NestedSetRight: 10, ParentID: -1},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa"), Kind: int(v1.Span_SPAN_KIND_CLIENT), NestedSetLeft: 2, NestedSetRight: 9, ParentID: 1},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb"), Kind: int(v1.Span_SPAN_KIND_CLIENT), NestedSetLeft: 4, NestedSetRight: 7, ParentID: 3},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb"), Kind: int(v1.Span_SPAN_KIND_SERVER), NestedSetLeft: 3, NestedSetRight: 8, ParentID: 2},
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("cccccccc"), Kind: int(v1.Span_SPAN_KIND_SERVER), NestedSetLeft: 5, NestedSetRight: 6, ParentID: 4},
},
},
expectedConnected: true,
},
{
name: "3x IDs - invalid trace",
trace: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb")},
},
},
expected: [][]Span{
{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("bbbbbbbb")},
},
},
expectedConnected: false,
},
{
name: "no roots",
trace: [][]Span{
{
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa")},
},
},
expected: [][]Span{
{
{SpanID: []byte("cccccccc"), ParentSpanID: []byte("bbbbbbbb")},
{SpanID: []byte("bbbbbbbb"), ParentSpanID: []byte("aaaaaaaa")},
},
},
expectedConnected: false,
},
}
makeTrace := func(traceSpans [][]Span) *Trace {
var resourceSpans ResourceSpans
for _, spans := range traceSpans {
scopeSpans := ScopeSpans{Spans: append([]Span{}, spans...)}
resourceSpans.ScopeSpans = append(resourceSpans.ScopeSpans, scopeSpans)
}
return &Trace{ResourceSpans: []ResourceSpans{resourceSpans}}
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
trace := makeTrace(tt.trace)
expected := makeTrace(tt.expected)
connected := assignNestedSetModelBoundsAndServiceStats(trace)
assertEqualNestedSetModelBounds(t, trace, expected)
assert.Equal(t, tt.expectedConnected, connected)
})
}
}
func assertEqualNestedSetModelBounds(t testing.TB, actual, expected *Trace) {
t.Helper()
actualSpans := map[uint64]*Span{}
actualCount := 0
for _, rs := range actual.ResourceSpans {
for _, ss := range rs.ScopeSpans {
for i, s := range ss.Spans {
actualSpans[util.SpanIDAndKindToToken(s.SpanID, s.Kind)] = &ss.Spans[i]
actualCount++
}
}
}
expectedCount := 0
for _, rs := range expected.ResourceSpans {
for _, ss := range rs.ScopeSpans {
for _, exp := range ss.Spans {
expectedCount++
act, ok := actualSpans[util.SpanIDAndKindToToken(exp.SpanID, exp.Kind)]
require.Truef(t, ok, "span '%v' expected but was missing", string(exp.SpanID))
assert.Equalf(t, exp.NestedSetLeft, act.NestedSetLeft, "span '%v' NestedSetLeft is expected %d but was %d", string(exp.SpanID), exp.NestedSetLeft, act.NestedSetLeft)
assert.Equalf(t, exp.NestedSetRight, act.NestedSetRight, "span '%v' NestedSetRight is expected %d but was %d", string(exp.SpanID), exp.NestedSetRight, act.NestedSetRight)
assert.Equalf(t, exp.ParentID, act.ParentID, "span '%v' ParentID is expected %d but was %d", string(exp.SpanID), exp.ParentID, act.ParentID)
assert.Equalf(t, exp.ParentSpanID, act.ParentSpanID, "span '%v' ParentSpanID is expected %d but was %d", string(exp.SpanID), string(exp.ParentSpanID), string(act.ParentSpanID))
assert.Equalf(t, exp.Kind, act.Kind, "span '%v' Kind is expected %d but was %d", string(exp.SpanID), exp.Kind, act.Kind)
}
}
}
assert.Equalf(t, expectedCount, actualCount, "expected %d spans but found %d instead", expectedCount, actualCount)
}
func TestAssignServiceStats(t *testing.T) {
tests := []struct {
name string
trace []ResourceSpans
expected map[string]ServiceStats
}{
{
name: "single span",
trace: []ResourceSpans{
{
Resource: Resource{ServiceName: "serviceA"},
ScopeSpans: []ScopeSpans{{
Spans: []Span{
{SpanID: []byte("aaaaaaaa")},
},
}},
},
},
expected: map[string]ServiceStats{"serviceA": {SpanCount: 1}},
},
{
name: "multiple services",
trace: []ResourceSpans{
{
Resource: Resource{ServiceName: "serviceA"},
ScopeSpans: []ScopeSpans{{
Spans: []Span{
{SpanID: []byte("aaaaaaaa")},
{SpanID: []byte("aaaaaaab")},
},
}},
},
{
Resource: Resource{ServiceName: "serviceB"},
ScopeSpans: []ScopeSpans{{
Spans: []Span{
{SpanID: []byte("aaaaaaac")},
},
}},
},
},
expected: map[string]ServiceStats{"serviceA": {SpanCount: 2}, "serviceB": {SpanCount: 1}},
},
{
name: "multiple services with errors",
trace: []ResourceSpans{
{
Resource: Resource{ServiceName: "serviceA"},
ScopeSpans: []ScopeSpans{{
Spans: []Span{
{SpanID: []byte("aaaaaaaa"), StatusCode: 0},
{SpanID: []byte("aaaaaaab"), StatusCode: 2},
},
}},
},
{
Resource: Resource{ServiceName: "serviceB"},
ScopeSpans: []ScopeSpans{{
Spans: []Span{
{SpanID: []byte("aaaaaaac"), StatusCode: 1},
{SpanID: []byte("aaaaaaad"), StatusCode: 2},
{SpanID: []byte("aaaaaaae"), StatusCode: 2},
},
}},
},
},
expected: map[string]ServiceStats{"serviceA": {SpanCount: 2, ErrorCount: 1}, "serviceB": {SpanCount: 3, ErrorCount: 2}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
trace := &Trace{ResourceSpans: tt.trace}
assignNestedSetModelBoundsAndServiceStats(trace)
assert.Equal(t, tt.expected, trace.ServiceStats)
})
}
}