github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/queryrange/limits_test.go (about) 1 package queryrange 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "sync" 8 "testing" 9 "time" 10 11 "github.com/prometheus/prometheus/model/labels" 12 "github.com/prometheus/prometheus/promql" 13 "github.com/stretchr/testify/require" 14 "github.com/weaveworks/common/user" 15 "go.uber.org/atomic" 16 17 "github.com/grafana/loki/pkg/logproto" 18 "github.com/grafana/loki/pkg/logqlmodel" 19 "github.com/grafana/loki/pkg/querier/queryrange/queryrangebase" 20 "github.com/grafana/loki/pkg/storage/config" 21 util_log "github.com/grafana/loki/pkg/util/log" 22 "github.com/grafana/loki/pkg/util/marshal" 23 ) 24 25 func TestLimits(t *testing.T) { 26 l := fakeLimits{ 27 splits: map[string]time.Duration{"a": time.Minute}, 28 } 29 30 wrapped := WithSplitByLimits(l, time.Hour) 31 32 // Test default 33 require.Equal(t, wrapped.QuerySplitDuration("b"), time.Hour) 34 // Ensure we override the underlying implementation 35 require.Equal(t, wrapped.QuerySplitDuration("a"), time.Hour) 36 37 r := &LokiRequest{ 38 Query: "qry", 39 StartTs: time.Now(), 40 Step: int64(time.Minute / time.Millisecond), 41 } 42 43 require.Equal( 44 t, 45 fmt.Sprintf("%s:%s:%d:%d:%d", "a", r.GetQuery(), r.GetStep(), r.GetStart()/int64(time.Hour/time.Millisecond), int64(time.Hour)), 46 cacheKeyLimits{wrapped}.GenerateCacheKey("a", r), 47 ) 48 } 49 50 func Test_seriesLimiter(t *testing.T) { 51 cfg := testConfig 52 cfg.CacheResults = false 53 // split in 7 with 2 in // max. 54 l := WithSplitByLimits(fakeLimits{maxSeries: 1, maxQueryParallelism: 2}, time.Hour) 55 tpw, stopper, err := NewTripperware(cfg, util_log.Logger, l, config.SchemaConfig{}, nil, nil) 56 if stopper != nil { 57 defer stopper.Stop() 58 } 59 require.NoError(t, err) 60 61 lreq := &LokiRequest{ 62 Query: `rate({app="foo"} |= "foo"[1m])`, 63 Limit: 1000, 64 Step: 30000, // 30sec 65 StartTs: testTime.Add(-6 * time.Hour), 66 EndTs: testTime, 67 Direction: logproto.FORWARD, 68 Path: "/query_range", 69 } 70 71 ctx := user.InjectOrgID(context.Background(), "1") 72 req, err := LokiCodec.EncodeRequest(ctx, lreq) 73 require.NoError(t, err) 74 75 req = req.WithContext(ctx) 76 err = user.InjectOrgIDIntoHTTPRequest(ctx, req) 77 require.NoError(t, err) 78 79 rt, err := newfakeRoundTripper() 80 require.NoError(t, err) 81 defer rt.Close() 82 83 count, h := promqlResult(matrix) 84 rt.setHandler(h) 85 86 _, err = tpw(rt).RoundTrip(req) 87 require.NoError(t, err) 88 require.Equal(t, 7, *count) 89 90 // 2 series should not be allowed. 91 c := new(int) 92 m := &sync.Mutex{} 93 h = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 94 m.Lock() 95 defer m.Unlock() 96 defer func() { 97 *c++ 98 }() 99 // first time returns a single series 100 if *c == 0 { 101 if err := marshal.WriteQueryResponseJSON(logqlmodel.Result{Data: matrix}, rw); err != nil { 102 panic(err) 103 } 104 return 105 } 106 // second time returns a different series. 107 if err := marshal.WriteQueryResponseJSON(logqlmodel.Result{ 108 Data: promql.Matrix{ 109 { 110 Points: []promql.Point{ 111 { 112 T: toMs(testTime.Add(-4 * time.Hour)), 113 V: 0.013333333333333334, 114 }, 115 }, 116 Metric: []labels.Label{ 117 { 118 Name: "filename", 119 Value: `/var/hostlog/apport.log`, 120 }, 121 { 122 Name: "job", 123 Value: "anotherjob", 124 }, 125 }, 126 }, 127 }, 128 }, rw); err != nil { 129 panic(err) 130 } 131 }) 132 rt.setHandler(h) 133 134 _, err = tpw(rt).RoundTrip(req) 135 require.Error(t, err) 136 require.LessOrEqual(t, *c, 4) 137 } 138 139 func Test_MaxQueryParallelism(t *testing.T) { 140 maxQueryParallelism := 2 141 f, err := newfakeRoundTripper() 142 require.Nil(t, err) 143 var count atomic.Int32 144 var max atomic.Int32 145 f.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 146 cur := count.Inc() 147 if cur > max.Load() { 148 max.Store(cur) 149 } 150 defer count.Dec() 151 // simulate some work 152 time.Sleep(20 * time.Millisecond) 153 })) 154 ctx := user.InjectOrgID(context.Background(), "foo") 155 156 r, err := http.NewRequestWithContext(ctx, "GET", "/query_range", http.NoBody) 157 require.Nil(t, err) 158 159 _, _ = NewLimitedRoundTripper(f, LokiCodec, fakeLimits{maxQueryParallelism: maxQueryParallelism}, 160 queryrangebase.MiddlewareFunc(func(next queryrangebase.Handler) queryrangebase.Handler { 161 return queryrangebase.HandlerFunc(func(c context.Context, r queryrangebase.Request) (queryrangebase.Response, error) { 162 var wg sync.WaitGroup 163 for i := 0; i < 10; i++ { 164 wg.Add(1) 165 go func() { 166 defer wg.Done() 167 _, _ = next.Do(c, &LokiRequest{}) 168 }() 169 } 170 wg.Wait() 171 return nil, nil 172 }) 173 }), 174 ).RoundTrip(r) 175 maxFound := int(max.Load()) 176 require.LessOrEqual(t, maxFound, maxQueryParallelism, "max query parallelism: ", maxFound, " went over the configured one:", maxQueryParallelism) 177 } 178 179 func Test_MaxQueryParallelismLateScheduling(t *testing.T) { 180 maxQueryParallelism := 2 181 f, err := newfakeRoundTripper() 182 require.Nil(t, err) 183 184 f.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 185 // simulate some work 186 time.Sleep(20 * time.Millisecond) 187 })) 188 ctx := user.InjectOrgID(context.Background(), "foo") 189 190 r, err := http.NewRequestWithContext(ctx, "GET", "/query_range", http.NoBody) 191 require.Nil(t, err) 192 193 _, _ = NewLimitedRoundTripper(f, LokiCodec, fakeLimits{maxQueryParallelism: maxQueryParallelism}, 194 queryrangebase.MiddlewareFunc(func(next queryrangebase.Handler) queryrangebase.Handler { 195 return queryrangebase.HandlerFunc(func(c context.Context, r queryrangebase.Request) (queryrangebase.Response, error) { 196 for i := 0; i < 10; i++ { 197 go func() { 198 _, _ = next.Do(c, &LokiRequest{}) 199 }() 200 } 201 return nil, nil 202 }) 203 }), 204 ).RoundTrip(r) 205 } 206 207 func Test_MaxQueryParallelismDisable(t *testing.T) { 208 maxQueryParallelism := 0 209 f, err := newfakeRoundTripper() 210 require.Nil(t, err) 211 212 f.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 213 // simulate some work 214 time.Sleep(20 * time.Millisecond) 215 })) 216 ctx := user.InjectOrgID(context.Background(), "foo") 217 218 r, err := http.NewRequestWithContext(ctx, "GET", "/query_range", http.NoBody) 219 require.Nil(t, err) 220 221 _, err = NewLimitedRoundTripper(f, LokiCodec, fakeLimits{maxQueryParallelism: maxQueryParallelism}, 222 queryrangebase.MiddlewareFunc(func(next queryrangebase.Handler) queryrangebase.Handler { 223 return queryrangebase.HandlerFunc(func(c context.Context, r queryrangebase.Request) (queryrangebase.Response, error) { 224 for i := 0; i < 10; i++ { 225 go func() { 226 _, _ = next.Do(c, &LokiRequest{}) 227 }() 228 } 229 return nil, nil 230 }) 231 }), 232 ).RoundTrip(r) 233 require.Error(t, err) 234 } 235 236 func Test_MaxQueryLookBack(t *testing.T) { 237 tpw, stopper, err := NewTripperware(testConfig, util_log.Logger, fakeLimits{ 238 maxQueryLookback: 1 * time.Hour, 239 maxQueryParallelism: 1, 240 }, config.SchemaConfig{}, nil, nil) 241 if stopper != nil { 242 defer stopper.Stop() 243 } 244 require.NoError(t, err) 245 rt, err := newfakeRoundTripper() 246 require.NoError(t, err) 247 defer rt.Close() 248 249 lreq := &LokiRequest{ 250 Query: `{app="foo"} |= "foo"`, 251 Limit: 10000, 252 StartTs: testTime.Add(-6 * time.Hour), 253 EndTs: testTime, 254 Direction: logproto.FORWARD, 255 Path: "/loki/api/v1/query_range", 256 } 257 258 ctx := user.InjectOrgID(context.Background(), "1") 259 req, err := LokiCodec.EncodeRequest(ctx, lreq) 260 require.NoError(t, err) 261 262 req = req.WithContext(ctx) 263 err = user.InjectOrgIDIntoHTTPRequest(ctx, req) 264 require.NoError(t, err) 265 266 _, err = tpw(rt).RoundTrip(req) 267 require.NoError(t, err) 268 } 269 270 func Test_GenerateCacheKey_NoDivideZero(t *testing.T) { 271 l := cacheKeyLimits{WithSplitByLimits(nil, 0)} 272 start := time.Now() 273 r := &LokiRequest{ 274 Query: "qry", 275 StartTs: start, 276 Step: int64(time.Minute / time.Millisecond), 277 } 278 279 require.Equal( 280 t, 281 fmt.Sprintf("foo:qry:%d:0:0", r.GetStep()), 282 l.GenerateCacheKey("foo", r), 283 ) 284 }