github.com/grafana/pyroscope@v1.18.0/pkg/frontend/readpath/read_path_test.go (about)

     1  package readpath
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"math"
     7  	"testing"
     8  	"time"
     9  
    10  	"connectrpc.com/connect"
    11  	"github.com/go-kit/log"
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"github.com/stretchr/testify/mock"
    14  	"github.com/stretchr/testify/suite"
    15  
    16  	querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1"
    17  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    18  	"github.com/grafana/pyroscope/pkg/model"
    19  	"github.com/grafana/pyroscope/pkg/tenant"
    20  	"github.com/grafana/pyroscope/pkg/test/mocks/mockquerierv1connect"
    21  )
    22  
    23  type routerTestSuite struct {
    24  	suite.Suite
    25  
    26  	router   *Router
    27  	logger   log.Logger
    28  	registry *prometheus.Registry
    29  
    30  	overrides   *mockOverrides
    31  	oldFrontend *mockquerierv1connect.MockQuerierServiceClient
    32  	newFrontend *mockquerierv1connect.MockQuerierServiceClient
    33  
    34  	ctx context.Context
    35  }
    36  
    37  type mockOverrides struct{ mock.Mock }
    38  
    39  func (m *mockOverrides) ReadPathOverrides(tenantID string) Config {
    40  	args := m.Called(tenantID)
    41  	return args.Get(0).(Config)
    42  }
    43  
    44  func (s *routerTestSuite) SetupTest() {
    45  	s.logger = log.NewLogfmtLogger(io.Discard)
    46  	s.registry = prometheus.NewRegistry()
    47  	s.overrides = new(mockOverrides)
    48  	s.oldFrontend = new(mockquerierv1connect.MockQuerierServiceClient)
    49  	s.newFrontend = new(mockquerierv1connect.MockQuerierServiceClient)
    50  	s.router = NewRouter(
    51  		s.logger,
    52  		s.overrides,
    53  		s.oldFrontend,
    54  		s.newFrontend,
    55  	)
    56  	s.ctx = tenant.InjectTenantID(context.Background(), "tenant-a")
    57  }
    58  
    59  func (s *routerTestSuite) BeforeTest(_, _ string) {}
    60  
    61  func (s *routerTestSuite) AfterTest(_, _ string) {
    62  	s.overrides.AssertExpectations(s.T())
    63  	s.oldFrontend.AssertExpectations(s.T())
    64  	s.newFrontend.AssertExpectations(s.T())
    65  }
    66  
    67  func TestRouterSuite(t *testing.T) { suite.Run(t, new(routerTestSuite)) }
    68  
    69  func (s *routerTestSuite) Test_FrontendOnly() {
    70  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{EnableQueryBackend: false})
    71  
    72  	expected := connect.NewResponse(&typesv1.LabelNamesResponse{Names: []string{"foo", "bar"}})
    73  	s.oldFrontend.On("LabelNames", mock.Anything, mock.Anything).Return(expected, nil).Once()
    74  
    75  	resp, err := s.router.LabelNames(s.ctx, connect.NewRequest(&typesv1.LabelNamesRequest{}))
    76  	s.Require().NoError(err)
    77  	s.Assert().Equal(expected, resp)
    78  }
    79  
    80  func (s *routerTestSuite) Test_NewFrontendOnly() {
    81  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{EnableQueryBackend: true})
    82  
    83  	expected := connect.NewResponse(&typesv1.LabelNamesResponse{Names: []string{"foo", "bar"}})
    84  	s.newFrontend.On("LabelNames", mock.Anything, mock.Anything).Return(expected, nil).Once()
    85  
    86  	resp, err := s.router.LabelNames(s.ctx, connect.NewRequest(&typesv1.LabelNamesRequest{}))
    87  	s.Require().NoError(err)
    88  	s.Assert().Equal(expected, resp)
    89  }
    90  
    91  func (s *routerTestSuite) Test_Combined() {
    92  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
    93  		EnableQueryBackend:     true,
    94  		EnableQueryBackendFrom: time.Unix(20, 0),
    95  	})
    96  
    97  	req1 := connect.NewRequest(&typesv1.LabelNamesRequest{Start: 10, End: 19999})
    98  	resp1 := connect.NewResponse(&typesv1.LabelNamesResponse{Names: []string{"foo", "bar"}})
    99  	s.oldFrontend.On("LabelNames", mock.Anything, req1).Return(resp1, nil).Once()
   100  
   101  	req2 := connect.NewRequest(&typesv1.LabelNamesRequest{Start: 20000, End: math.MaxInt64})
   102  	resp2 := connect.NewResponse(&typesv1.LabelNamesResponse{Names: []string{"baz", "foo", "qux"}})
   103  	s.newFrontend.On("LabelNames", mock.Anything, req2).Return(resp2, nil).Once()
   104  
   105  	expected := connect.NewResponse(&typesv1.LabelNamesResponse{Names: []string{"bar", "baz", "foo", "qux"}})
   106  	resp, err := s.router.LabelNames(s.ctx, connect.NewRequest(&typesv1.LabelNamesRequest{
   107  		Start: 10,
   108  		End:   math.MaxInt64,
   109  	}))
   110  
   111  	s.Require().NoError(err)
   112  	s.Assert().Equal(expected, resp)
   113  }
   114  
   115  func (s *routerTestSuite) Test_Combined_BeforeSplit() {
   116  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
   117  		EnableQueryBackend:     true,
   118  		EnableQueryBackendFrom: time.Unix(20, 0),
   119  	})
   120  
   121  	expected := connect.NewResponse(&typesv1.LabelNamesResponse{Names: []string{"foo", "bar"}})
   122  	req := connect.NewRequest(&typesv1.LabelNamesRequest{Start: 10, End: 10000})
   123  	s.oldFrontend.On("LabelNames", mock.Anything, req).Return(expected, nil).Once()
   124  
   125  	resp, err := s.router.LabelNames(s.ctx, req)
   126  	s.Require().NoError(err)
   127  	s.Assert().Equal(expected, resp)
   128  }
   129  
   130  func (s *routerTestSuite) Test_Combined_AfterSplit() {
   131  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
   132  		EnableQueryBackend:     true,
   133  		EnableQueryBackendFrom: time.Unix(20, 0),
   134  	})
   135  
   136  	expected := connect.NewResponse(&typesv1.LabelNamesResponse{Names: []string{"foo", "bar"}})
   137  	req := connect.NewRequest(&typesv1.LabelNamesRequest{Start: 30000, End: 40000})
   138  	s.newFrontend.On("LabelNames", mock.Anything, req).Return(expected, nil).Once()
   139  
   140  	resp, err := s.router.LabelNames(s.ctx, req)
   141  	s.Require().NoError(err)
   142  	s.Assert().Equal(expected, resp)
   143  }
   144  
   145  func (s *routerTestSuite) Test_LabelNames() {
   146  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
   147  		EnableQueryBackend:     true,
   148  		EnableQueryBackendFrom: time.Unix(5, 0),
   149  	})
   150  
   151  	req := connect.NewRequest(&typesv1.LabelNamesRequest{Start: 10, End: 10000})
   152  	expected := connect.NewResponse(&typesv1.LabelNamesResponse{Names: []string{"bar", "foo"}})
   153  	s.oldFrontend.On("LabelNames", mock.Anything, mock.Anything).Return(expected, nil).Once()
   154  	s.newFrontend.On("LabelNames", mock.Anything, mock.Anything).Return(expected, nil).Once()
   155  
   156  	resp, err := s.router.LabelNames(s.ctx, req)
   157  	s.Require().NoError(err)
   158  	s.Assert().Equal(expected, resp)
   159  }
   160  
   161  func (s *routerTestSuite) Test_LabelValues() {
   162  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
   163  		EnableQueryBackend:     true,
   164  		EnableQueryBackendFrom: time.Unix(5, 0),
   165  	})
   166  
   167  	req := connect.NewRequest(&typesv1.LabelValuesRequest{Start: 10, End: 10000})
   168  	expected := connect.NewResponse(&typesv1.LabelValuesResponse{Names: []string{"bar", "foo"}})
   169  	s.oldFrontend.On("LabelValues", mock.Anything, mock.Anything).Return(expected, nil).Once()
   170  	s.newFrontend.On("LabelValues", mock.Anything, mock.Anything).Return(expected, nil).Once()
   171  
   172  	resp, err := s.router.LabelValues(s.ctx, req)
   173  	s.Require().NoError(err)
   174  	s.Assert().Equal(expected, resp)
   175  }
   176  
   177  func (s *routerTestSuite) Test_Series() {
   178  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
   179  		EnableQueryBackend:     true,
   180  		EnableQueryBackendFrom: time.Unix(5, 0),
   181  	})
   182  
   183  	req := connect.NewRequest(&querierv1.SeriesRequest{Start: 10, End: 10000})
   184  	expected := connect.NewResponse(&querierv1.SeriesResponse{
   185  		LabelsSet: []*typesv1.Labels{
   186  			{Labels: []*typesv1.LabelPair{{Name: "foo", Value: "bar"}}},
   187  		},
   188  	})
   189  
   190  	s.oldFrontend.On("Series", mock.Anything, mock.Anything).Return(expected, nil).Once()
   191  	s.newFrontend.On("Series", mock.Anything, mock.Anything).Return(expected, nil).Once()
   192  
   193  	resp, err := s.router.Series(s.ctx, req)
   194  	s.Require().NoError(err)
   195  	s.Assert().Equal(expected, resp)
   196  }
   197  
   198  func (s *routerTestSuite) Test_TimeSeries_Limit() {
   199  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
   200  		EnableQueryBackend:     true,
   201  		EnableQueryBackendFrom: time.Unix(5, 0),
   202  	})
   203  
   204  	one := int64(1)
   205  	req := connect.NewRequest(&querierv1.SelectSeriesRequest{Start: 10, End: 10000, Limit: &one})
   206  	expected := connect.NewResponse(&querierv1.SelectSeriesResponse{
   207  		Series: []*typesv1.Series{
   208  			{Labels: model.LabelsFromStrings("foo", "baz"), Points: []*typesv1.Point{{Timestamp: 1, Value: 3}}},
   209  		},
   210  	})
   211  
   212  	s.oldFrontend.On("SelectSeries",
   213  		mock.Anything, connect.NewRequest(&querierv1.SelectSeriesRequest{Start: 10, End: 4999})).
   214  		Return(connect.NewResponse(&querierv1.SelectSeriesResponse{
   215  			Series: []*typesv1.Series{
   216  				{Labels: model.LabelsFromStrings("foo", "bar"), Points: []*typesv1.Point{{Timestamp: 1, Value: 1}}},
   217  				{Labels: model.LabelsFromStrings("foo", "baz"), Points: []*typesv1.Point{{Timestamp: 1, Value: 2}}},
   218  			}}), nil).Once()
   219  
   220  	s.newFrontend.On("SelectSeries",
   221  		mock.Anything, connect.NewRequest(&querierv1.SelectSeriesRequest{Start: 5000, End: 10000})).
   222  		Return(connect.NewResponse(&querierv1.SelectSeriesResponse{
   223  			Series: []*typesv1.Series{
   224  				{Labels: model.LabelsFromStrings("foo", "bar"), Points: []*typesv1.Point{{Timestamp: 1, Value: 1}}},
   225  				{Labels: model.LabelsFromStrings("foo", "baz"), Points: []*typesv1.Point{{Timestamp: 1, Value: 1}}},
   226  			}}), nil).Once()
   227  
   228  	resp, err := s.router.SelectSeries(s.ctx, req)
   229  	s.Require().NoError(err)
   230  	s.Assert().Equal(expected, resp)
   231  }
   232  
   233  func (s *routerTestSuite) Test_TimeSeries_Limit_NewFrontendOnly() {
   234  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
   235  		EnableQueryBackend: true,
   236  	})
   237  
   238  	one := int64(1)
   239  	req := connect.NewRequest(&querierv1.SelectSeriesRequest{Start: 10, End: 10000, Limit: &one})
   240  	expected := connect.NewResponse(&querierv1.SelectSeriesResponse{
   241  		Series: []*typesv1.Series{
   242  			{Labels: model.LabelsFromStrings("foo", "baz"), Points: []*typesv1.Point{{Timestamp: 1, Value: 3}}},
   243  		},
   244  	})
   245  
   246  	s.newFrontend.On("SelectSeries", mock.Anything, req).Return(expected, nil).Once()
   247  	resp, err := s.router.SelectSeries(s.ctx, req)
   248  	s.Require().NoError(err)
   249  	s.Assert().Equal(expected, resp)
   250  }
   251  
   252  func (s *routerTestSuite) Test_TimeSeries_Limit_OldFrontendOnly() {
   253  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{})
   254  
   255  	one := int64(1)
   256  	req := connect.NewRequest(&querierv1.SelectSeriesRequest{Start: 10, End: 10000, Limit: &one})
   257  	expected := connect.NewResponse(&querierv1.SelectSeriesResponse{
   258  		Series: []*typesv1.Series{
   259  			{Labels: model.LabelsFromStrings("foo", "baz"), Points: []*typesv1.Point{{Timestamp: 1, Value: 3}}},
   260  		},
   261  	})
   262  
   263  	s.oldFrontend.On("SelectSeries", mock.Anything, req).Return(expected, nil).Once()
   264  	resp, err := s.router.SelectSeries(s.ctx, req)
   265  	s.Require().NoError(err)
   266  	s.Assert().Equal(expected, resp)
   267  }
   268  
   269  func (s *routerTestSuite) Test_TimeSeries_NoLimit() {
   270  	s.overrides.On("ReadPathOverrides", "tenant-a").Return(Config{
   271  		EnableQueryBackend:     true,
   272  		EnableQueryBackendFrom: time.Unix(5, 0),
   273  	})
   274  
   275  	req := connect.NewRequest(&querierv1.SelectSeriesRequest{Start: 10, End: 10000})
   276  	expected := connect.NewResponse(&querierv1.SelectSeriesResponse{
   277  		Series: []*typesv1.Series{
   278  			{Labels: model.LabelsFromStrings("foo", "baz"), Points: []*typesv1.Point{{Timestamp: 1, Value: 3}}},
   279  			{Labels: model.LabelsFromStrings("foo", "bar"), Points: []*typesv1.Point{{Timestamp: 1, Value: 2}}},
   280  		},
   281  	})
   282  
   283  	s.oldFrontend.On("SelectSeries",
   284  		mock.Anything, connect.NewRequest(&querierv1.SelectSeriesRequest{Start: 10, End: 4999})).
   285  		Return(connect.NewResponse(&querierv1.SelectSeriesResponse{
   286  			Series: []*typesv1.Series{
   287  				{Labels: model.LabelsFromStrings("foo", "bar"), Points: []*typesv1.Point{{Timestamp: 1, Value: 1}}},
   288  				{Labels: model.LabelsFromStrings("foo", "baz"), Points: []*typesv1.Point{{Timestamp: 1, Value: 2}}},
   289  			}}), nil).Once()
   290  
   291  	s.newFrontend.On("SelectSeries",
   292  		mock.Anything, connect.NewRequest(&querierv1.SelectSeriesRequest{Start: 5000, End: 10000})).
   293  		Return(connect.NewResponse(&querierv1.SelectSeriesResponse{
   294  			Series: []*typesv1.Series{
   295  				{Labels: model.LabelsFromStrings("foo", "bar"), Points: []*typesv1.Point{{Timestamp: 1, Value: 1}}},
   296  				{Labels: model.LabelsFromStrings("foo", "baz"), Points: []*typesv1.Point{{Timestamp: 1, Value: 1}}},
   297  			}}), nil).Once()
   298  
   299  	resp, err := s.router.SelectSeries(s.ctx, req)
   300  	s.Require().NoError(err)
   301  	s.Assert().Equal(expected, resp)
   302  }