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  }