@@ -42,6 +42,7 @@ import (
4242 "github.com/thanos-io/thanos/pkg/runutil"
4343 "github.com/thanos-io/thanos/pkg/shipper"
4444 storecache "github.com/thanos-io/thanos/pkg/store/cache"
45+ "github.com/thanos-io/thanos/pkg/store/storepb"
4546 "github.com/weaveworks/common/httpgrpc"
4647 "github.com/weaveworks/common/middleware"
4748 "github.com/weaveworks/common/user"
@@ -3941,6 +3942,134 @@ func BenchmarkIngester_QueryStream_Chunks(b *testing.B) {
39413942 }
39423943}
39433944
3945+ func BenchmarkIngester_QueryStreamChunks_MatcherOptimization (b * testing.B ) {
3946+ tests := map [string ]struct {
3947+ matchers []* labels.Matcher
3948+ description string
3949+ }{
3950+ "metric name with regex matchers" : {
3951+ matchers : []* labels.Matcher {
3952+ labels .MustNewMatcher (labels .MatchEqual , model .MetricNameLabel , "test_metric" ),
3953+ labels .MustNewMatcher (labels .MatchRegexp , "region" , ".+" ),
3954+ labels .MustNewMatcher (labels .MatchRegexp , "job" , ".+" ),
3955+ },
3956+ description : "Metric name with .+ regex matchers" ,
3957+ },
3958+ "metric name with not equal empty" : {
3959+ matchers : []* labels.Matcher {
3960+ labels .MustNewMatcher (labels .MatchEqual , model .MetricNameLabel , "test_metric" ),
3961+ labels .MustNewMatcher (labels .MatchNotEqual , "env" , "" ),
3962+ labels .MustNewMatcher (labels .MatchNotEqual , "pod" , "" ),
3963+ },
3964+ description : "Metric name with != \" \" matchers" ,
3965+ },
3966+ "metric name with sparse label" : {
3967+ matchers : []* labels.Matcher {
3968+ labels .MustNewMatcher (labels .MatchEqual , model .MetricNameLabel , "test_metric" ),
3969+ labels .MustNewMatcher (labels .MatchRegexp , "sparse_label" , ".+" ),
3970+ },
3971+ description : "Metric name with sparse label matcher" ,
3972+ },
3973+ "complex matchers" : {
3974+ matchers : []* labels.Matcher {
3975+ labels .MustNewMatcher (labels .MatchEqual , model .MetricNameLabel , "test_metric" ),
3976+ labels .MustNewMatcher (labels .MatchRegexp , "region" , ".+" ),
3977+ labels .MustNewMatcher (labels .MatchRegexp , "job" , ".+" ),
3978+ labels .MustNewMatcher (labels .MatchRegexp , "env" , ".+" ),
3979+ labels .MustNewMatcher (labels .MatchRegexp , "pod" , ".+" ),
3980+ },
3981+ description : "Complex matchers with .+ regex" ,
3982+ },
3983+ }
3984+
3985+ for testName , testData := range tests {
3986+ b .Run (testName + "_optimization_disabled" , func (b * testing.B ) {
3987+ benchmarkQueryStreamChunksWithMatcherOptimization (b , false , testData .matchers , testData .description + " without optimization" )
3988+ })
3989+ b .Run (testName + "_optimization_enabled" , func (b * testing.B ) {
3990+ benchmarkQueryStreamChunksWithMatcherOptimization (b , true , testData .matchers , testData .description + " with optimization" )
3991+ })
3992+ }
3993+ }
3994+
3995+ func benchmarkQueryStreamChunksWithMatcherOptimization (b * testing.B , enableMatcherOptimization bool , matchers []* labels.Matcher , description string ) {
3996+ cfg := defaultIngesterTestConfig (b )
3997+ cfg .EnableMatcherOptimization = enableMatcherOptimization
3998+
3999+ i , err := prepareIngesterWithBlocksStorage (b , cfg , prometheus .NewRegistry ())
4000+ require .NoError (b , err )
4001+ require .NoError (b , services .StartAndAwaitRunning (context .Background (), i ))
4002+ defer services .StopAndAwaitTerminated (context .Background (), i ) //nolint:errcheck
4003+
4004+ // Wait until it's ACTIVE
4005+ test .Poll (b , 1 * time .Second , ring .ACTIVE , func () any {
4006+ return i .lifecycler .GetState ()
4007+ })
4008+
4009+ ctx := user .InjectOrgID (context .Background (), userID )
4010+
4011+ for s := range 1000 {
4012+ // Create base labels
4013+ labelPairs := []string {
4014+ labels .MetricName , "test_metric" ,
4015+ "region" , fmt .Sprintf ("region-%d" , s % 10 ),
4016+ "job" , fmt .Sprintf ("job-%d" , s % 20 ),
4017+ "env" , fmt .Sprintf ("env-%d" , s % 5 ),
4018+ "pod" , fmt .Sprintf ("pod-%d" , s % 1000 ),
4019+ }
4020+
4021+ // Add sparse label only for half of the series
4022+ if s % 2 == 0 {
4023+ labelPairs = append (labelPairs , "sparse_label" , fmt .Sprintf ("sparse-%d" , s % 50 ))
4024+ }
4025+
4026+ lbls := labels .FromStrings (labelPairs ... )
4027+
4028+ samples := make ([]cortexpb.Sample , 0 , 5 )
4029+ for t := range 5 {
4030+ samples = append (samples , cortexpb.Sample {
4031+ Value : float64 (s + t ),
4032+ TimestampMs : int64 (s * 5 + t ),
4033+ })
4034+ }
4035+
4036+ // Create labels slice with same length as samples
4037+ labelsSlice := make ([]labels.Labels , len (samples ))
4038+ for j := range labelsSlice {
4039+ labelsSlice [j ] = lbls
4040+ }
4041+
4042+ req := cortexpb .ToWriteRequest (labelsSlice , samples , nil , nil , cortexpb .API )
4043+ _ , err = i .Push (ctx , req )
4044+ require .NoError (b , err )
4045+ }
4046+
4047+ db , err := i .getTSDB (userID )
4048+ require .NoError (b , err )
4049+ require .NotNil (b , db )
4050+
4051+ mockStream := & mockQueryStreamServer {ctx : ctx }
4052+ sm := (& storepb.ShardInfo {
4053+ TotalShards : 0 ,
4054+ }).Matcher (nil )
4055+
4056+ b .ReportAllocs ()
4057+ b .ResetTimer ()
4058+
4059+ for b .Loop () {
4060+ numSeries , numSamples , _ , numChunks , err := i .queryStreamChunks (
4061+ ctx , db , 0 , 5000 , matchers , sm , mockStream )
4062+
4063+ require .NoError (b , err )
4064+ require .Greater (b , numSeries , 0 )
4065+ require .Greater (b , numSamples , 0 )
4066+ require .Greater (b , numChunks , 0 )
4067+
4068+ // Reset the mock stream for next iteration
4069+ mockStream .series = mockStream .series [:0 ]
4070+ }
4071+ }
4072+
39444073func benchmarkQueryStream (b * testing.B , samplesCount , seriesCount int ) {
39454074 cfg := defaultIngesterTestConfig (b )
39464075
0 commit comments