diff --git a/CHANGELOG.md b/CHANGELOG.md index fdcfccfdb36..edeaa412dcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ * [ENHANCEMENT] Make the trace ID label name configurable for remote written exemplars [#3074](https://github.com/grafana/tempo/pull/3074) * [ENHANCEMENT] Update poller to make use of previous results and reduce backend load. [#2652](https://github.com/grafana/tempo/pull/2652) (@zalegrala) * [ENHANCEMENT] Improve TraceQL regex performance in certain queries. [#3139](https://github.com/grafana/tempo/pull/3139) (@joe-elliott) -* [BUGFIX] Readd session token to s3 credentials. [#3144](https://github.com/grafana/tempo/pull/3144) (@farodin91) +* [ENHANCEMENT] Improve TraceQL performance in complex queries. [#3113](https://github.com/grafana/tempo/pull/3113) (@joe-elliott) ## v2.3.0 / 2023-10-30 diff --git a/pkg/traceql/ast_execute.go b/pkg/traceql/ast_execute.go index ae786ff89a3..45e6b526459 100644 --- a/pkg/traceql/ast_execute.go +++ b/pkg/traceql/ast_execute.go @@ -475,28 +475,11 @@ func (s Static) execute(Span) (Static, error) { } func (a Attribute) execute(span Span) (Static, error) { - atts := span.Attributes() - static, ok := atts[a] + static, ok := span.AttributeFor(a) if ok { return static, nil } - // if the requested attribute has a scope none then we will check first for span attributes matching - // then any attributes matching. we don't need to both if this is an intrinsic b/c those will always - // be caught above if they exist - if a.Scope == AttributeScopeNone && a.Intrinsic == IntrinsicNone { - for attribute, static := range atts { - if a.Name == attribute.Name && attribute.Scope == AttributeScopeSpan { - return static, nil - } - } - for attribute, static := range atts { - if a.Name == attribute.Name { - return static, nil - } - } - } - return NewStaticNil(), nil } diff --git a/pkg/traceql/ast_test.go b/pkg/traceql/ast_test.go index 79cd0c9e14b..2019602aaa8 100644 --- a/pkg/traceql/ast_test.go +++ b/pkg/traceql/ast_test.go @@ -371,7 +371,24 @@ func (m *mockSpan) WithAttrBool(key string, value bool) *mockSpan { return m } -func (m *mockSpan) Attributes() map[Attribute]Static { +func (m *mockSpan) AttributeFor(a Attribute) (Static, bool) { + s, ok := m.attributes[a] + // if not found explicitly, check if it's a span attribute + if !ok && a.Scope == AttributeScopeNone { + aSpan := a + aSpan.Scope = AttributeScopeSpan + s, ok = m.attributes[aSpan] + } + // if not found explicitly, check if it's a resource attribute + if !ok && a.Scope == AttributeScopeNone { + aRes := a + aRes.Scope = AttributeScopeResource + s, ok = m.attributes[aRes] + } + return s, ok +} + +func (m *mockSpan) AllAttributes() map[Attribute]Static { return m.attributes } diff --git a/pkg/traceql/engine.go b/pkg/traceql/engine.go index 944785022b0..e1b110f30c5 100644 --- a/pkg/traceql/engine.go +++ b/pkg/traceql/engine.go @@ -179,7 +179,7 @@ func (e *Engine) ExecuteTagValues( case AttributeScopeResource, AttributeScopeSpan: // If tag is scoped, we can check the map directly collectAttributeValue = func(s Span) bool { - if v, ok := s.Attributes()[tag]; ok { + if v, ok := s.AttributeFor(tag); ok { return cb(v) } return false @@ -193,13 +193,13 @@ func (e *Engine) ExecuteTagValues( // If the tag is unscoped, we need to check resource and span scoped manually by building a new Attribute with each scope. collectAttributeValue = func(s Span) bool { if tag.Intrinsic != IntrinsicNone { // it's intrinsic - if v, ok := s.Attributes()[tag]; ok { + if v, ok := s.AttributeFor(tag); ok { return cb(v) } } else { // it's unscoped for _, scope := range []AttributeScope{AttributeScopeResource, AttributeScopeSpan} { scopedAttr := Attribute{Scope: scope, Parent: tag.Parent, Name: tag.Name} - if v, ok := s.Attributes()[scopedAttr]; ok { + if v, ok := s.AttributeFor(scopedAttr); ok { return cb(v) } } @@ -296,7 +296,7 @@ func (e *Engine) asTraceSearchMetadata(spanset *Spanset) *tempopb.TraceSearchMet Attributes: nil, } - atts := span.Attributes() + atts := span.AllAttributes() if name, ok := atts[NewIntrinsic(IntrinsicName)]; ok { tempopbSpan.Name = name.S diff --git a/pkg/traceql/storage.go b/pkg/traceql/storage.go index 170c83978f3..878fded15d7 100644 --- a/pkg/traceql/storage.go +++ b/pkg/traceql/storage.go @@ -77,9 +77,12 @@ func (f *FetchSpansRequest) appendCondition(c ...Condition) { } type Span interface { - // these are the actual fields used by the engine to evaluate queries - // if a Filter parameter is passed the spans returned will only have this field populated - Attributes() map[Attribute]Static + // AttributeFor returns the attribute for the given key. If the attribute is not found then + // the second return value will be false. + AttributeFor(Attribute) (Static, bool) + // AllAttributes returns a map of all attributes for this span. AllAttributes should be used sparingly + // and is expected to be significantly slower than AttributeFor. + AllAttributes() map[Attribute]Static ID() []byte StartTimeUnixNanos() uint64 diff --git a/pkg/traceqlmetrics/metrics.go b/pkg/traceqlmetrics/metrics.go index 93d534921b1..ef9bc074a2e 100644 --- a/pkg/traceqlmetrics/metrics.go +++ b/pkg/traceqlmetrics/metrics.go @@ -281,13 +281,13 @@ func GetMetrics(ctx context.Context, query, groupBy string, spanLimit int, start } var ( - attrs = s.Attributes() - series = MetricSeries{} - err = attrs[status] == statusErr + series = MetricSeries{} + status, _ = s.AttributeFor(status) + err = status == statusErr ) for i, g := range groupBys { - series[i] = KeyValue{Key: groupByKeys[i], Value: lookup(g, attrs)} + series[i] = KeyValue{Key: groupByKeys[i], Value: lookup(g, s)} } results.Record(series, s.DurationNanos(), err) @@ -307,9 +307,9 @@ func GetMetrics(ctx context.Context, query, groupBy string, spanLimit int, start return results, nil } -func lookup(needles []traceql.Attribute, haystack map[traceql.Attribute]traceql.Static) traceql.Static { +func lookup(needles []traceql.Attribute, span traceql.Span) traceql.Static { for _, n := range needles { - if v, ok := haystack[n]; ok { + if v, ok := span.AttributeFor(n); ok { return v } } diff --git a/pkg/traceqlmetrics/mocks.go b/pkg/traceqlmetrics/mocks.go index 271df4d6ccf..03178b5dca7 100644 --- a/pkg/traceqlmetrics/mocks.go +++ b/pkg/traceqlmetrics/mocks.go @@ -45,14 +45,19 @@ func (m *mockSpan) WithErr() *mockSpan { return m } -func (m *mockSpan) Attributes() map[traceql.Attribute]traceql.Static { return m.attrs } -func (m *mockSpan) ID() []byte { return nil } -func (m *mockSpan) StartTimeUnixNanos() uint64 { return m.start } -func (m *mockSpan) DurationNanos() uint64 { return m.duration } +func (m *mockSpan) AllAttributes() map[traceql.Attribute]traceql.Static { return m.attrs } +func (m *mockSpan) ID() []byte { return nil } +func (m *mockSpan) StartTimeUnixNanos() uint64 { return m.start } +func (m *mockSpan) DurationNanos() uint64 { return m.duration } func (m *mockSpan) DescendantOf([]traceql.Span, []traceql.Span, bool, bool, []traceql.Span) []traceql.Span { return nil } +func (m *mockSpan) AttributeFor(a traceql.Attribute) (traceql.Static, bool) { + s, ok := m.attrs[a] + return s, ok +} + func (m *mockSpan) SiblingOf([]traceql.Span, []traceql.Span, bool, []traceql.Span) []traceql.Span { return nil } diff --git a/tempodb/encoding/vparquet/block_traceql.go b/tempodb/encoding/vparquet/block_traceql.go index 6a14871b2f9..7a526069c93 100644 --- a/tempodb/encoding/vparquet/block_traceql.go +++ b/tempodb/encoding/vparquet/block_traceql.go @@ -35,10 +35,36 @@ type span struct { cbSpanset *traceql.Spanset } -func (s *span) Attributes() map[traceql.Attribute]traceql.Static { +func (s *span) AllAttributes() map[traceql.Attribute]traceql.Static { return s.attributes } +func (s *span) AttributeFor(a traceql.Attribute) (traceql.Static, bool) { + atts := s.attributes + static, ok := atts[a] + if ok { + return static, ok + } + + // if the requested attribute has a scope none then we will check first for span attributes matching + // then any attributes matching. we don't need to both if this is an intrinsic b/c those will always + // be caught above if they exist + if a.Scope == traceql.AttributeScopeNone && a.Intrinsic == traceql.IntrinsicNone { + for attribute, static := range atts { + if a.Name == attribute.Name && attribute.Scope == traceql.AttributeScopeSpan { + return static, true + } + } + for attribute, static := range atts { + if a.Name == attribute.Name { + return static, true + } + } + } + + return traceql.NewStaticNil(), false +} + func (s *span) ID() []byte { return s.id } diff --git a/tempodb/encoding/vparquet2/block_traceql.go b/tempodb/encoding/vparquet2/block_traceql.go index 332482636c6..aa253be9487 100644 --- a/tempodb/encoding/vparquet2/block_traceql.go +++ b/tempodb/encoding/vparquet2/block_traceql.go @@ -37,10 +37,36 @@ type span struct { cbSpanset *traceql.Spanset } -func (s *span) Attributes() map[traceql.Attribute]traceql.Static { +func (s *span) AllAttributes() map[traceql.Attribute]traceql.Static { return s.attributes } +func (s *span) AttributeFor(a traceql.Attribute) (traceql.Static, bool) { + atts := s.attributes + static, ok := atts[a] + if ok { + return static, ok + } + + // if the requested attribute has a scope none then we will check first for span attributes matching + // then any attributes matching. we don't need to both if this is an intrinsic b/c those will always + // be caught above if they exist + if a.Scope == traceql.AttributeScopeNone && a.Intrinsic == traceql.IntrinsicNone { + for attribute, static := range atts { + if a.Name == attribute.Name && attribute.Scope == traceql.AttributeScopeSpan { + return static, true + } + } + for attribute, static := range atts { + if a.Name == attribute.Name { + return static, true + } + } + } + + return traceql.NewStaticNil(), false +} + func (s *span) ID() []byte { return s.id } diff --git a/tempodb/encoding/vparquet3/block_traceql.go b/tempodb/encoding/vparquet3/block_traceql.go index cd119dd4514..3cc519e10e3 100644 --- a/tempodb/encoding/vparquet3/block_traceql.go +++ b/tempodb/encoding/vparquet3/block_traceql.go @@ -23,9 +23,17 @@ import ( "github.com/grafana/tempo/tempodb/encoding/common" ) +type attrVal struct { + a traceql.Attribute + s traceql.Static +} + // span implements traceql.Span type span struct { - attributes map[traceql.Attribute]traceql.Static + spanAttrs []attrVal + resourceAttrs []attrVal + traceAttrs []attrVal + id []byte startTimeUnixNanos uint64 durationNanos uint64 @@ -39,8 +47,112 @@ type span struct { cbSpanset *traceql.Spanset } -func (s *span) Attributes() map[traceql.Attribute]traceql.Static { - return s.attributes +func (s *span) AllAttributes() map[traceql.Attribute]traceql.Static { + atts := make(map[traceql.Attribute]traceql.Static, len(s.spanAttrs)+len(s.resourceAttrs)+len(s.traceAttrs)) + for _, st := range s.traceAttrs { + if st.s.Type == traceql.TypeNil { + continue + } + atts[st.a] = st.s + } + for _, st := range s.resourceAttrs { + if st.s.Type == traceql.TypeNil { + continue + } + atts[st.a] = st.s + } + for _, st := range s.spanAttrs { + if st.s.Type == traceql.TypeNil { + continue + } + atts[st.a] = st.s + } + return atts +} + +func (s *span) AttributeFor(a traceql.Attribute) (traceql.Static, bool) { + find := func(a traceql.Attribute, attrs []attrVal) *traceql.Static { + if len(attrs) == 1 { + if attrs[0].a == a { + return &attrs[0].s + } + } + if len(attrs) == 2 { + if attrs[0].a == a { + return &attrs[0].s + } + if attrs[1].a == a { + return &attrs[1].s + } + } + + for _, st := range attrs { + if st.a == a { + return &st.s + } + } + return nil + } + findName := func(s string, attrs []attrVal) *traceql.Static { + if len(attrs) == 1 { + if attrs[0].a.Name == s { + return &attrs[0].s + } + } + if len(attrs) == 2 { + if attrs[0].a.Name == s { + return &attrs[0].s + } + if attrs[1].a.Name == s { + return &attrs[1].s + } + } + + for _, st := range attrs { + if st.a.Name == s { + return &st.s + } + } + return nil + } + + if a.Scope == traceql.AttributeScopeResource { + if attr := find(a, s.resourceAttrs); attr != nil { + return *attr, true + } + return traceql.Static{}, false + } + + if a.Scope == traceql.AttributeScopeSpan { + if attr := find(a, s.spanAttrs); attr != nil { + return *attr, true + } + return traceql.Static{}, false + } + + if a.Intrinsic != traceql.IntrinsicNone { + // intrinsics are always on the span or trace ... for now + if attr := find(a, s.spanAttrs); attr != nil { + return *attr, true + } + + if attr := find(a, s.traceAttrs); attr != nil { + return *attr, true + } + + } + + // name search in span and then resource to give precedence to span + // we don't need to do a name search at the trace level b/c it is intrinsics only + if attr := findName(a.Name, s.spanAttrs); attr != nil { + return *attr, true + } + + if attr := findName(a.Name, s.resourceAttrs); attr != nil { + return *attr, true + } + + return traceql.Static{}, false } func (s *span) ID() []byte { @@ -200,11 +312,34 @@ func (s *span) ChildOf(lhs []traceql.Span, rhs []traceql.Span, falseForAll bool, return buffer } +func (s *span) addSpanAttr(a traceql.Attribute, st traceql.Static) { + s.spanAttrs = append(s.spanAttrs, attrVal{a: a, s: st}) +} + +func (s *span) setResourceAttrs(attrs []attrVal) { + s.resourceAttrs = append(s.resourceAttrs, attrs...) +} + +func (s *span) setTraceAttrs(attrs []attrVal) { + s.traceAttrs = append(s.traceAttrs, attrs...) +} + // attributesMatched counts all attributes in the map as well as metadata fields like start/end/id func (s *span) attributesMatched() int { count := 0 - for _, v := range s.attributes { - if v.Type != traceql.TypeNil { + // todo: attributesMatced is called a lot. we could cache this count on set + for _, st := range s.spanAttrs { + if st.s.Type != traceql.TypeNil { + count++ + } + } + for _, st := range s.resourceAttrs { + if st.s.Type != traceql.TypeNil { + count++ + } + } + for _, st := range s.traceAttrs { + if st.s.Type != traceql.TypeNil { count++ } } @@ -234,9 +369,7 @@ func (s *span) attributesMatched() int { // can return a slice of dropped and kept spansets? var spanPool = sync.Pool{ New: func() interface{} { - return &span{ - attributes: make(map[traceql.Attribute]traceql.Static), - } + return &span{} }, } @@ -250,7 +383,9 @@ func putSpan(s *span) { s.nestedSetParent = 0 s.nestedSetLeft = 0 s.nestedSetRight = 0 - clear(s.attributes) + s.spanAttrs = s.spanAttrs[:0] + s.resourceAttrs = s.resourceAttrs[:0] + s.traceAttrs = s.traceAttrs[:0] spanPool.Put(s) } @@ -1695,7 +1830,7 @@ func (c *spanCollector) KeepGroup(res *parquetquery.IteratorResult) bool { for _, e := range res.OtherEntries { if v, ok := e.Value.(traceql.Static); ok { - sp.attributes[newSpanAttr(e.Key)] = v + sp.addSpanAttr(newSpanAttr(e.Key), v) } } @@ -1711,9 +1846,9 @@ func (c *spanCollector) KeepGroup(res *parquetquery.IteratorResult) bool { case columnPathSpanDuration: durationNanos = kv.Value.Uint64() sp.durationNanos = durationNanos - sp.attributes[traceql.IntrinsicDurationAttribute] = traceql.NewStaticDuration(time.Duration(durationNanos)) + sp.addSpanAttr(traceql.IntrinsicDurationAttribute, traceql.NewStaticDuration(time.Duration(durationNanos))) case columnPathSpanName: - sp.attributes[traceql.IntrinsicNameAttribute] = traceql.NewStaticString(unsafeToString(kv.Value.Bytes())) + sp.addSpanAttr(traceql.IntrinsicNameAttribute, traceql.NewStaticString(unsafeToString(kv.Value.Bytes()))) case columnPathSpanStatusCode: // Map OTLP status code back to TraceQL enum. // For other values, use the raw integer. @@ -1728,9 +1863,9 @@ func (c *spanCollector) KeepGroup(res *parquetquery.IteratorResult) bool { default: status = traceql.Status(kv.Value.Uint64()) } - sp.attributes[traceql.IntrinsicStatusAttribute] = traceql.NewStaticStatus(status) + sp.addSpanAttr(traceql.IntrinsicStatusAttribute, traceql.NewStaticStatus(status)) case columnPathSpanStatusMessage: - sp.attributes[traceql.IntrinsicStatusMessageAttribute] = traceql.NewStaticString(unsafeToString(kv.Value.Bytes())) + sp.addSpanAttr(traceql.IntrinsicStatusMessageAttribute, traceql.NewStaticString(unsafeToString(kv.Value.Bytes()))) case columnPathSpanKind: var kind traceql.Kind switch kv.Value.Uint64() { @@ -1749,7 +1884,7 @@ func (c *spanCollector) KeepGroup(res *parquetquery.IteratorResult) bool { default: kind = traceql.Kind(kv.Value.Uint64()) } - sp.attributes[traceql.IntrinsicKindAttribute] = traceql.NewStaticKind(kind) + sp.addSpanAttr(traceql.IntrinsicKindAttribute, traceql.NewStaticKind(kind)) case columnPathSpanParentID: sp.nestedSetParent = kv.Value.Int32() case columnPathSpanNestedSetLeft: @@ -1761,13 +1896,13 @@ func (c *spanCollector) KeepGroup(res *parquetquery.IteratorResult) bool { // Are nils possible here? switch kv.Value.Kind() { case parquet.Boolean: - sp.attributes[newSpanAttr(kv.Key)] = traceql.NewStaticBool(kv.Value.Boolean()) + sp.addSpanAttr(newSpanAttr(kv.Key), traceql.NewStaticBool(kv.Value.Boolean())) case parquet.Int32, parquet.Int64: - sp.attributes[newSpanAttr(kv.Key)] = traceql.NewStaticInt(int(kv.Value.Int64())) + sp.addSpanAttr(newSpanAttr(kv.Key), traceql.NewStaticInt(int(kv.Value.Int64()))) case parquet.Float: - sp.attributes[newSpanAttr(kv.Key)] = traceql.NewStaticFloat(kv.Value.Double()) + sp.addSpanAttr(newSpanAttr(kv.Key), traceql.NewStaticFloat(kv.Value.Double())) case parquet.ByteArray: - sp.attributes[newSpanAttr(kv.Key)] = traceql.NewStaticString(unsafeToString(kv.Value.Bytes())) + sp.addSpanAttr(newSpanAttr(kv.Key), traceql.NewStaticString(unsafeToString(kv.Value.Bytes()))) } } } @@ -1792,7 +1927,7 @@ func (c *spanCollector) KeepGroup(res *parquetquery.IteratorResult) bool { type batchCollector struct { requireAtLeastOneMatchOverall bool minAttributes int - resAttrs map[traceql.Attribute]traceql.Static + resAttrs []attrVal } var _ parquetquery.GroupPredicate = (*batchCollector)(nil) @@ -1801,7 +1936,6 @@ func newBatchCollector(requireAtLeastOneMatchOverall bool, minAttributes int) *b return &batchCollector{ requireAtLeastOneMatchOverall: requireAtLeastOneMatchOverall, minAttributes: minAttributes, - resAttrs: make(map[traceql.Attribute]traceql.Static), } } @@ -1815,13 +1949,14 @@ func (c *batchCollector) String() string { func (c *batchCollector) KeepGroup(res *parquetquery.IteratorResult) bool { // First pass over spans and attributes from the AttributeCollector spans := res.OtherEntries[:0] - clear(c.resAttrs) + c.resAttrs = c.resAttrs[:0] + for _, kv := range res.OtherEntries { switch v := kv.Value.(type) { case *span: spans = append(spans, kv) case traceql.Static: - c.resAttrs[newResAttr(kv.Key)] = v + c.resAttrs = append(c.resAttrs, attrVal{newResAttr(kv.Key), v}) } } res.OtherEntries = spans @@ -1835,9 +1970,9 @@ func (c *batchCollector) KeepGroup(res *parquetquery.IteratorResult) bool { for _, e := range res.Entries { switch e.Value.Kind() { case parquet.Int64: - c.resAttrs[newResAttr(e.Key)] = traceql.NewStaticInt(int(e.Value.Int64())) + c.resAttrs = append(c.resAttrs, attrVal{newResAttr(e.Key), traceql.NewStaticInt(int(e.Value.Int64()))}) case parquet.ByteArray: - c.resAttrs[newResAttr(e.Key)] = traceql.NewStaticString(unsafeToString(e.Value.Bytes())) + c.resAttrs = append(c.resAttrs, attrVal{newResAttr(e.Key), traceql.NewStaticString(unsafeToString(e.Value.Bytes()))}) } } @@ -1855,18 +1990,7 @@ func (c *batchCollector) KeepGroup(res *parquetquery.IteratorResult) bool { // Copy resource-level attributes to the span // If the span already has an entry for this attribute it // takes precedence (can be nil to indicate no match) - for k, v := range c.resAttrs { - if _, alreadyExists := span.attributes[k]; !alreadyExists { - span.attributes[k] = v - } - } - - // Remove unmatched attributes - for k, v := range span.attributes { - if v.Type == traceql.TypeNil { - delete(span.attributes, k) - } - } + span.setResourceAttrs(c.resAttrs) if c.requireAtLeastOneMatchOverall { // Skip over span if it didn't meet minimum criteria @@ -1894,16 +2018,14 @@ func (c *batchCollector) KeepGroup(res *parquetquery.IteratorResult) bool { // It adds trace-level attributes into the spansets before // they are returned type traceCollector struct { - // traceAttrs is a map reused by KeepGroup to reduce allocations - traceAttrs map[traceql.Attribute]traceql.Static + // traceAttrs is a slice reused by KeepGroup to reduce allocations + traceAttrs []attrVal } var _ parquetquery.GroupPredicate = (*traceCollector)(nil) func newTraceCollector() *traceCollector { - return &traceCollector{ - traceAttrs: make(map[traceql.Attribute]traceql.Static), - } + return &traceCollector{} } func (c *traceCollector) String() string { @@ -1915,7 +2037,7 @@ func (c *traceCollector) String() string { // resource-level data. func (c *traceCollector) KeepGroup(res *parquetquery.IteratorResult) bool { finalSpanset := getSpanset() - clear(c.traceAttrs) + c.traceAttrs = c.traceAttrs[:0] for _, e := range res.Entries { switch e.Key { @@ -1925,13 +2047,13 @@ func (c *traceCollector) KeepGroup(res *parquetquery.IteratorResult) bool { finalSpanset.StartTimeUnixNanos = e.Value.Uint64() case columnPathDurationNanos: finalSpanset.DurationNanos = e.Value.Uint64() - c.traceAttrs[traceql.IntrinsicTraceDurationAttribute] = traceql.NewStaticDuration(time.Duration(finalSpanset.DurationNanos)) + c.traceAttrs = append(c.traceAttrs, attrVal{traceql.IntrinsicTraceDurationAttribute, traceql.NewStaticDuration(time.Duration(finalSpanset.DurationNanos))}) case columnPathRootSpanName: finalSpanset.RootSpanName = unsafeToString(e.Value.Bytes()) - c.traceAttrs[traceql.IntrinsicTraceRootSpanAttribute] = traceql.NewStaticString(finalSpanset.RootSpanName) + c.traceAttrs = append(c.traceAttrs, attrVal{traceql.IntrinsicTraceRootSpanAttribute, traceql.NewStaticString(finalSpanset.RootSpanName)}) case columnPathRootServiceName: finalSpanset.RootServiceName = unsafeToString(e.Value.Bytes()) - c.traceAttrs[traceql.IntrinsicTraceRootServiceAttribute] = traceql.NewStaticString(finalSpanset.RootServiceName) + c.traceAttrs = append(c.traceAttrs, attrVal{traceql.IntrinsicTraceRootServiceAttribute, traceql.NewStaticString(finalSpanset.RootServiceName)}) } } @@ -1952,13 +2074,9 @@ func (c *traceCollector) KeepGroup(res *parquetquery.IteratorResult) bool { } // loop over all spans and add the trace-level attributes - for k, v := range c.traceAttrs { - for _, s := range finalSpanset.Spans { - s := s.(*span) - if _, alreadyExists := s.attributes[k]; !alreadyExists { - s.attributes[k] = v - } - } + for _, s := range finalSpanset.Spans { + s := s.(*span) + s.setTraceAttrs(c.traceAttrs) } res.Entries = res.Entries[:0] diff --git a/tempodb/encoding/vparquet3/block_traceql_meta_test.go b/tempodb/encoding/vparquet3/block_traceql_meta_test.go index 41a6208cfe2..e25dcb3dcaa 100644 --- a/tempodb/encoding/vparquet3/block_traceql_meta_test.go +++ b/tempodb/encoding/vparquet3/block_traceql_meta_test.go @@ -2,6 +2,7 @@ package vparquet3 import ( "context" + "sort" "testing" "time" @@ -51,22 +52,26 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { id: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(100 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(100 * time.Second)}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, &span{ id: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(0), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(0)}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, ), @@ -90,19 +95,20 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { id: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - // foo not returned because the span didn't match it - traceql.NewScopedAttribute(traceql.AttributeScopeSpan, false, "bar"): traceql.NewStaticInt(123), - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(100 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewScopedAttribute(traceql.AttributeScopeSpan, false, "foo"), traceql.NewStaticNil()}, + {traceql.NewScopedAttribute(traceql.AttributeScopeSpan, false, "bar"), traceql.NewStaticInt(123)}, + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(100 * time.Second)}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, ), ), }, - { // Resource attributes lookup makeReq( @@ -119,21 +125,21 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { id: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - // Foo matched on resource. - // TODO - This seems misleading since the span has foo= - // but for this query we never even looked at span attribute columns. - newResAttr("foo"): traceql.NewStaticString("abc"), - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(100 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(100 * time.Second)}, + }, + resourceAttrs: []attrVal{ + {traceql.NewScopedAttribute(traceql.AttributeScopeResource, false, "foo"), traceql.NewStaticString("abc")}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, ), ), }, - { // Multiple attributes, only 1 matches and is returned makeReq( @@ -151,18 +157,23 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { id: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - newSpanAttr(LabelHTTPStatusCode): traceql.NewStaticInt(500), // This is the only attribute that matched anything - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(100 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewScopedAttribute(traceql.AttributeScopeSpan, false, "foo"), traceql.NewStaticNil()}, + {newSpanAttr(LabelHTTPStatusCode), traceql.NewStaticInt(500)}, // This is the only attribute that matched anything + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(100 * time.Second)}, + }, + resourceAttrs: []attrVal{ + {traceql.NewScopedAttribute(traceql.AttributeScopeResource, false, "foo"), traceql.NewStaticNil()}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, ), ), }, - { // Project attributes of all types makeReq( @@ -182,16 +193,20 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { id: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - newResAttr("foo"): traceql.NewStaticString("abc"), // Both are returned - newSpanAttr("foo"): traceql.NewStaticString("def"), // Both are returned - newSpanAttr(LabelHTTPStatusCode): traceql.NewStaticInt(500), - newSpanAttr("float"): traceql.NewStaticFloat(456.78), - newSpanAttr("bool"): traceql.NewStaticBool(false), - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(100 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewScopedAttribute(traceql.AttributeScopeSpan, false, "foo"), traceql.NewStaticString("def")}, + {newSpanAttr("float"), traceql.NewStaticFloat(456.78)}, + {newSpanAttr("bool"), traceql.NewStaticBool(false)}, + {newSpanAttr(LabelHTTPStatusCode), traceql.NewStaticInt(500)}, // This is the only attribute that matched anything + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(100 * time.Second)}, + }, + resourceAttrs: []attrVal{ + {traceql.NewScopedAttribute(traceql.AttributeScopeResource, false, "foo"), traceql.NewStaticString("abc")}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, ), @@ -221,13 +236,15 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { id: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(0), - traceql.NewIntrinsic(traceql.IntrinsicName): traceql.NewStaticString("world"), - traceql.NewIntrinsic(traceql.IntrinsicStatus): traceql.NewStaticStatus(traceql.StatusUnset), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicName), traceql.NewStaticString("world")}, + {traceql.NewIntrinsic(traceql.IntrinsicStatus), traceql.NewStaticStatus(traceql.StatusUnset)}, + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(0)}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, ), @@ -247,22 +264,32 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { id: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(100 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + // duration exists twice on the span attrs b/c it's requested twice. once in the normal fetch conditions and once in the second + // pass conditions. the actual engine code removes meta conditions based on the actual conditions so this won't normally happen + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(100 * time.Second)}, + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(100 * time.Second)}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, &span{ id: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(0 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + // duration exists twice on the span attrs b/c it's requested twice. once in the normal fetch conditions and once in the second + // pass conditions. the actual engine code removes meta conditions based on the actual conditions so this won't normally happen + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(0)}, + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(0)}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, ), @@ -282,22 +309,26 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { id: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[0].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(100 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(100 * time.Second)}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, &span{ id: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].SpanID, startTimeUnixNanos: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].StartTimeUnixNano, durationNanos: wantTr.ResourceSpans[1].ScopeSpans[0].Spans[0].DurationNano, - attributes: map[traceql.Attribute]traceql.Static{ - traceql.NewIntrinsic(traceql.IntrinsicDuration): traceql.NewStaticDuration(0 * time.Second), - traceql.NewIntrinsic(traceql.IntrinsicTraceDuration): traceql.NewStaticDuration(100 * time.Millisecond), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootService): traceql.NewStaticString("RootService"), - traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan): traceql.NewStaticString("RootSpan"), + spanAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicDuration), traceql.NewStaticDuration(0)}, + }, + traceAttrs: []attrVal{ + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootService), traceql.NewStaticString("RootService")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceRootSpan), traceql.NewStaticString("RootSpan")}, + {traceql.NewIntrinsic(traceql.IntrinsicTraceDuration), traceql.NewStaticDuration(100 * time.Millisecond)}, }, }, ), @@ -328,10 +359,37 @@ func TestBackendBlockSearchFetchMetaData(t *testing.T) { sp.(*span).cbSpanset = nil sp.(*span).cbSpansetFinal = false sp.(*span).rowNum = parquetquery.RowNumber{} + + // sort actual attrs to get consistent comparisons + sortSpanAttrs(sp.(*span)) } s.ReleaseFn = nil } + // sort expected attrs to get consistent comparisons + for _, s := range tc.expectedResults { + for _, sp := range s.Spans { + sortSpanAttrs(sp.(*span)) + } + } + require.Equal(t, tc.expectedResults, ss, "search request:", req) } } + +func sortSpanAttrs(s *span) { + // create sort func + sortFn := func(a, b attrVal) bool { + return a.a.String() < b.a.String() + } + // sort + sort.Slice(s.spanAttrs, func(i, j int) bool { + return sortFn(s.spanAttrs[i], s.spanAttrs[j]) + }) + sort.Slice(s.resourceAttrs, func(i, j int) bool { + return sortFn(s.resourceAttrs[i], s.resourceAttrs[j]) + }) + sort.Slice(s.traceAttrs, func(i, j int) bool { + return sortFn(s.traceAttrs[i], s.traceAttrs[j]) + }) +} diff --git a/tempodb/encoding/vparquet3/block_traceql_test.go b/tempodb/encoding/vparquet3/block_traceql_test.go index b644d9db69b..0d6fc822886 100644 --- a/tempodb/encoding/vparquet3/block_traceql_test.go +++ b/tempodb/encoding/vparquet3/block_traceql_test.go @@ -492,40 +492,32 @@ func BenchmarkBackendBlockTraceQL(b *testing.B) { query string }{ // span - {"spanAttNameNoMatch", "{ span.foo = `bar` }"}, {"spanAttValNoMatch", "{ span.bloom = `bar` }"}, - {"spanAttValMatch", "{ span.bloom > 0 }"}, {"spanAttIntrinsicNoMatch", "{ name = `asdfasdf` }"}, - {"spanAttIntrinsicMatch", "{ name = `gcs.ReadRange` }"}, - {"spanAttIntrinsicRegexNoMatch", "{ name =~ `asdfasdf` }"}, - {"spanAttIntrinsicRegexMatch", "{ name =~ `gcs.ReadRange` }"}, // resource - {"resourceAttNameNoMatch", "{ resource.foo = `bar` }"}, {"resourceAttValNoMatch", "{ resource.module.path = `bar` }"}, - {"resourceAttValMatch", "{ resource.os.type = `linux` }"}, - {"resourceAttIntrinsicNoMatch", "{ resource.service.name = `a` }"}, {"resourceAttIntrinsicMatch", "{ resource.service.name = `tempo-query-frontend` }"}, // mixed - {"mixedNameNoMatch", "{ .foo = `bar` }"}, {"mixedValNoMatch", "{ .bloom = `bar` }"}, {"mixedValMixedMatchAnd", "{ resource.foo = `bar` && name = `gcs.ReadRange` }"}, {"mixedValMixedMatchOr", "{ resource.foo = `bar` || name = `gcs.ReadRange` }"}, - {"mixedValBothMatch", "{ resource.service.name = `query-frontend` && name = `gcs.ReadRange` }"}, - {"count", "{} | count() > 1"}, - {"desc", "{ resource.service.name = `loki-querier` } >> { resource.service.name = `loki-querier` }"}, + {"count", "{ } | count() > 1"}, + {"struct", "{ resource.service.name != `loki-querier` } >> { resource.service.name = `loki-querier` && status = error }"}, + {"||", "{ resource.service.name = `loki-querier` } || { resource.service.name = `loki-ingester` }"}, + {"mixed", `{resource.namespace!="" && resource.service.name="loki-distributor" && duration>2s && resource.cluster=~"prod.*"}`}, } ctx := context.TODO() tenantID := "1" - // blockID := uuid.MustParse("000d37d0-1e66-4f4e-bbd4-f85c1deb6e5e") - blockID := uuid.MustParse("06ebd383-8d4e-4289-b0e9-cf2197d611d5") + blockID := uuid.MustParse("000d37d0-1e66-4f4e-bbd4-f85c1deb6e5e") + // blockID := uuid.MustParse("06ebd383-8d4e-4289-b0e9-cf2197d611d5") r, _, _, err := local.New(&local.Config{ - // Path: path.Join("/home/joe/testblock/"), - Path: path.Join("/Users/marty/src/tmp"), + Path: path.Join("/home/joe/testblock/"), + // Path: path.Join("/Users/marty/src/tmp"), }) require.NoError(b, err) @@ -535,7 +527,7 @@ func BenchmarkBackendBlockTraceQL(b *testing.B) { opts := common.DefaultSearchOptions() opts.StartPage = 10 - opts.TotalPages = 10 + opts.TotalPages = 1 block := newBackendBlock(meta, rr) _, _, err = block.openForSearch(ctx, opts)