github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/queryrange/log_result_cache_test.go (about) 1 package queryrange 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/go-kit/log" 10 "github.com/stretchr/testify/mock" 11 "github.com/stretchr/testify/require" 12 "github.com/weaveworks/common/user" 13 14 "github.com/grafana/loki/pkg/loghttp" 15 "github.com/grafana/loki/pkg/logproto" 16 "github.com/grafana/loki/pkg/logqlmodel/stats" 17 "github.com/grafana/loki/pkg/querier/queryrange/queryrangebase" 18 "github.com/grafana/loki/pkg/storage/chunk/cache" 19 ) 20 21 func Test_LogResultCacheSameRange(t *testing.T) { 22 var ( 23 ctx = user.InjectOrgID(context.Background(), "foo") 24 lrc = NewLogResultCache( 25 log.NewNopLogger(), 26 fakeLimits{ 27 splits: map[string]time.Duration{"foo": time.Minute}, 28 }, 29 cache.NewMockCache(), 30 nil, 31 nil, 32 ) 33 ) 34 35 req := &LokiRequest{ 36 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 37 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 38 } 39 40 fake := newFakeResponse([]mockResponse{ 41 { 42 RequestResponse: queryrangebase.RequestResponse{ 43 Request: req, 44 Response: emptyResponse(req), 45 }, 46 }, 47 }) 48 49 h := lrc.Wrap(fake) 50 51 resp, err := h.Do(ctx, req) 52 require.NoError(t, err) 53 require.Equal(t, emptyResponse(req), resp) 54 resp, err = h.Do(ctx, req) 55 require.NoError(t, err) 56 require.Equal(t, emptyResponse(req), resp) 57 58 fake.AssertExpectations(t) 59 } 60 61 func Test_LogResultCacheSameRangeNonEmpty(t *testing.T) { 62 var ( 63 ctx = user.InjectOrgID(context.Background(), "foo") 64 lrc = NewLogResultCache( 65 log.NewNopLogger(), 66 fakeLimits{ 67 splits: map[string]time.Duration{"foo": time.Minute}, 68 }, 69 cache.NewMockCache(), 70 nil, 71 nil, 72 ) 73 ) 74 75 req := &LokiRequest{ 76 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 77 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 78 } 79 80 fake := newFakeResponse([]mockResponse{ 81 { 82 RequestResponse: queryrangebase.RequestResponse{ 83 Request: req, 84 Response: nonEmptyResponse(req, 1), 85 }, 86 }, 87 { 88 RequestResponse: queryrangebase.RequestResponse{ 89 Request: req, 90 Response: nonEmptyResponse(req, 2), 91 }, 92 }, 93 }) 94 95 h := lrc.Wrap(fake) 96 97 resp, err := h.Do(ctx, req) 98 require.NoError(t, err) 99 require.Equal(t, nonEmptyResponse(req, 1), resp) 100 resp, err = h.Do(ctx, req) 101 require.NoError(t, err) 102 require.Equal(t, nonEmptyResponse(req, 2), resp) 103 104 fake.AssertExpectations(t) 105 } 106 107 func Test_LogResultCacheSmallerRange(t *testing.T) { 108 var ( 109 ctx = user.InjectOrgID(context.Background(), "foo") 110 lrc = NewLogResultCache( 111 log.NewNopLogger(), 112 fakeLimits{ 113 splits: map[string]time.Duration{"foo": time.Minute}, 114 }, 115 cache.NewMockCache(), 116 nil, 117 nil, 118 ) 119 ) 120 121 req := &LokiRequest{ 122 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 123 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 124 } 125 126 fake := newFakeResponse([]mockResponse{ 127 { 128 RequestResponse: queryrangebase.RequestResponse{ 129 Request: req, 130 Response: emptyResponse(req), 131 }, 132 }, 133 }) 134 135 h := lrc.Wrap(fake) 136 137 resp, err := h.Do(ctx, req) 138 require.NoError(t, err) 139 require.Equal(t, emptyResponse(req), resp) 140 resp, err = h.Do(ctx, &LokiRequest{ 141 StartTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 142 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 143 }) 144 require.NoError(t, err) 145 require.Equal(t, emptyResponse(&LokiRequest{ 146 StartTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 147 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 148 }), resp) 149 150 fake.AssertExpectations(t) 151 } 152 153 func Test_LogResultCacheDifferentRange(t *testing.T) { 154 var ( 155 ctx = user.InjectOrgID(context.Background(), "foo") 156 lrc = NewLogResultCache( 157 log.NewNopLogger(), 158 fakeLimits{ 159 splits: map[string]time.Duration{"foo": time.Minute}, 160 }, 161 cache.NewMockCache(), 162 nil, 163 nil, 164 ) 165 ) 166 167 req1 := &LokiRequest{ 168 StartTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 169 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 170 } 171 172 req2 := &LokiRequest{ 173 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 174 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 175 } 176 177 fake := newFakeResponse([]mockResponse{ 178 { 179 RequestResponse: queryrangebase.RequestResponse{ 180 Request: req1, 181 Response: emptyResponse(req1), 182 }, 183 }, 184 { 185 RequestResponse: queryrangebase.RequestResponse{ 186 Request: &LokiRequest{ 187 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 188 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 189 }, 190 Response: emptyResponse(&LokiRequest{ 191 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 192 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 193 }), 194 }, 195 }, 196 { 197 RequestResponse: queryrangebase.RequestResponse{ 198 Request: &LokiRequest{ 199 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 200 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 201 }, 202 Response: emptyResponse(&LokiRequest{ 203 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 204 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 205 }), 206 }, 207 }, 208 }) 209 210 h := lrc.Wrap(fake) 211 212 resp, err := h.Do(ctx, req1) 213 require.NoError(t, err) 214 require.Equal(t, emptyResponse(req1), resp) 215 resp, err = h.Do(ctx, req2) 216 require.NoError(t, err) 217 require.Equal(t, emptyResponse(req2), resp) 218 219 fake.AssertExpectations(t) 220 } 221 222 func Test_LogResultCacheDifferentRangeNonEmpty(t *testing.T) { 223 var ( 224 ctx = user.InjectOrgID(context.Background(), "foo") 225 lrc = NewLogResultCache( 226 log.NewNopLogger(), 227 fakeLimits{ 228 splits: map[string]time.Duration{"foo": time.Minute}, 229 }, 230 cache.NewMockCache(), 231 nil, 232 nil, 233 ) 234 ) 235 236 req1 := &LokiRequest{ 237 StartTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 238 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 239 } 240 241 req2 := &LokiRequest{ 242 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 243 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 244 } 245 246 fake := newFakeResponse([]mockResponse{ 247 { 248 RequestResponse: queryrangebase.RequestResponse{ 249 Request: req1, 250 Response: emptyResponse(req1), 251 }, 252 }, 253 { 254 RequestResponse: queryrangebase.RequestResponse{ 255 Request: &LokiRequest{ 256 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 257 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 258 }, 259 Response: nonEmptyResponse(&LokiRequest{ 260 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 261 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 262 }, 1), 263 }, 264 }, 265 { 266 RequestResponse: queryrangebase.RequestResponse{ 267 Request: &LokiRequest{ 268 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 269 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 270 }, 271 Response: nonEmptyResponse(&LokiRequest{ 272 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 273 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 274 }, 2), 275 }, 276 }, 277 }) 278 279 h := lrc.Wrap(fake) 280 281 resp, err := h.Do(ctx, req1) 282 require.NoError(t, err) 283 require.Equal(t, emptyResponse(req1), resp) 284 resp, err = h.Do(ctx, req2) 285 require.NoError(t, err) 286 require.Equal(t, mergeLokiResponse( 287 nonEmptyResponse(&LokiRequest{ 288 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 289 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 290 }, 2), 291 nonEmptyResponse(&LokiRequest{ 292 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 293 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 294 }, 1), 295 ), resp) 296 297 fake.AssertExpectations(t) 298 } 299 300 func Test_LogResultCacheDifferentRangeNonEmptyAndEmpty(t *testing.T) { 301 var ( 302 ctx = user.InjectOrgID(context.Background(), "foo") 303 lrc = NewLogResultCache( 304 log.NewNopLogger(), 305 fakeLimits{ 306 splits: map[string]time.Duration{"foo": time.Minute}, 307 }, 308 cache.NewMockCache(), 309 nil, 310 nil, 311 ) 312 ) 313 314 req1 := &LokiRequest{ 315 StartTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 316 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 317 } 318 319 req2 := &LokiRequest{ 320 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 321 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 322 } 323 324 fake := newFakeResponse([]mockResponse{ 325 { 326 RequestResponse: queryrangebase.RequestResponse{ 327 Request: req1, 328 Response: emptyResponse(req1), 329 }, 330 }, 331 { 332 RequestResponse: queryrangebase.RequestResponse{ 333 Request: &LokiRequest{ 334 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 335 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 336 }, 337 Response: emptyResponse(&LokiRequest{ 338 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 339 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 340 }), 341 }, 342 }, 343 { 344 RequestResponse: queryrangebase.RequestResponse{ 345 Request: &LokiRequest{ 346 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 347 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 348 }, 349 Response: nonEmptyResponse(&LokiRequest{ 350 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 351 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 352 }, 2), 353 }, 354 }, 355 // we call it twice 356 { 357 RequestResponse: queryrangebase.RequestResponse{ 358 Request: &LokiRequest{ 359 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 360 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 361 }, 362 Response: nonEmptyResponse(&LokiRequest{ 363 StartTs: time.Unix(0, 2*time.Minute.Nanoseconds()-30*time.Second.Nanoseconds()), 364 EndTs: time.Unix(0, 2*time.Minute.Nanoseconds()), 365 }, 2), 366 }, 367 }, 368 }) 369 370 h := lrc.Wrap(fake) 371 372 resp, err := h.Do(ctx, req1) 373 require.NoError(t, err) 374 require.Equal(t, emptyResponse(req1), resp) 375 resp, err = h.Do(ctx, req2) 376 require.NoError(t, err) 377 require.Equal(t, mergeLokiResponse( 378 emptyResponse(req1), 379 nonEmptyResponse(&LokiRequest{ 380 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 381 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 382 }, 1), 383 ), resp) 384 resp, err = h.Do(ctx, req2) 385 require.NoError(t, err) 386 require.Equal(t, mergeLokiResponse( 387 emptyResponse(req1), 388 nonEmptyResponse(&LokiRequest{ 389 StartTs: time.Unix(0, time.Minute.Nanoseconds()), 390 EndTs: time.Unix(0, time.Minute.Nanoseconds()+30*time.Second.Nanoseconds()), 391 }, 1), 392 ), resp) 393 fake.AssertExpectations(t) 394 } 395 396 type fakeResponse struct { 397 *mock.Mock 398 } 399 400 type mockResponse struct { 401 queryrangebase.RequestResponse 402 err error 403 } 404 405 func newFakeResponse(responses []mockResponse) fakeResponse { 406 m := &mock.Mock{} 407 for _, r := range responses { 408 m.On("Do", mock.Anything, r.Request).Return(r.Response, r.err).Once() 409 } 410 return fakeResponse{ 411 Mock: m, 412 } 413 } 414 415 func (f fakeResponse) Do(ctx context.Context, r queryrangebase.Request) (queryrangebase.Response, error) { 416 var ( 417 resp queryrangebase.Response 418 err error 419 args = f.Mock.Called(ctx, r) 420 ) 421 if args.Get(0) != nil { 422 resp = args.Get(0).(queryrangebase.Response) 423 } 424 if args.Get(1) != nil { 425 err = args.Get(1).(error) 426 } 427 return resp, err 428 } 429 430 func nonEmptyResponse(lokiReq *LokiRequest, i int) *LokiResponse { 431 return &LokiResponse{ 432 Status: loghttp.QueryStatusSuccess, 433 Statistics: stats.Result{}, 434 Direction: lokiReq.Direction, 435 Limit: lokiReq.Limit, 436 Version: uint32(loghttp.GetVersion(lokiReq.Path)), 437 Data: LokiData{ 438 ResultType: loghttp.ResultTypeStream, 439 Result: []logproto.Stream{ 440 { 441 Labels: `{foo="bar"}`, 442 Entries: []logproto.Entry{ 443 { 444 Timestamp: time.Unix(1, 0), 445 Line: fmt.Sprintf("%d", i), 446 }, 447 }, 448 }, 449 }, 450 }, 451 } 452 }