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 }