github.com/grafana/pyroscope@v1.18.0/pkg/querier/select_merge_test.go (about) 1 package querier 2 3 import ( 4 "context" 5 "sort" 6 "strconv" 7 "testing" 8 9 "github.com/stretchr/testify/require" 10 11 ingestv1 "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1" 12 typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" 13 "github.com/grafana/pyroscope/pkg/clientpool" 14 "github.com/grafana/pyroscope/pkg/iter" 15 "github.com/grafana/pyroscope/pkg/model" 16 "github.com/grafana/pyroscope/pkg/testhelper" 17 ) 18 19 var foobarlabels = model.Labels([]*typesv1.LabelPair{{Name: "foo", Value: "bar"}}) 20 21 func TestSelectMergeStacktraces(t *testing.T) { 22 resp1 := newFakeBidiClientStacktraces([]*ingestv1.ProfileSets{ 23 { 24 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 25 Profiles: []*ingestv1.SeriesProfile{ 26 {LabelIndex: 0, Timestamp: 1}, 27 {LabelIndex: 0, Timestamp: 2}, 28 {LabelIndex: 0, Timestamp: 4}, 29 }, 30 }, 31 { 32 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 33 Profiles: []*ingestv1.SeriesProfile{ 34 {LabelIndex: 0, Timestamp: 5}, 35 {LabelIndex: 0, Timestamp: 6}, 36 }, 37 }, 38 }) 39 resp2 := newFakeBidiClientStacktraces([]*ingestv1.ProfileSets{ 40 { 41 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 42 Profiles: []*ingestv1.SeriesProfile{ 43 {LabelIndex: 0, Timestamp: 2}, 44 {LabelIndex: 0, Timestamp: 3}, 45 {LabelIndex: 0, Timestamp: 4}, 46 }, 47 }, 48 { 49 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 50 Profiles: []*ingestv1.SeriesProfile{ 51 {LabelIndex: 0, Timestamp: 5}, 52 {LabelIndex: 0, Timestamp: 6}, 53 }, 54 }, 55 }) 56 resp3 := newFakeBidiClientStacktraces([]*ingestv1.ProfileSets{ 57 { 58 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 59 Profiles: []*ingestv1.SeriesProfile{ 60 {LabelIndex: 0, Timestamp: 3}, 61 {LabelIndex: 0, Timestamp: 5}, 62 }, 63 }, 64 }) 65 res, err := selectMergeTree(context.Background(), []ResponseFromReplica[clientpool.BidiClientMergeProfilesStacktraces]{ 66 { 67 response: resp1, 68 }, 69 { 70 response: resp2, 71 }, 72 { 73 response: resp3, 74 }, 75 }) 76 require.NoError(t, err) 77 requireFakeMergeProfilesStacktracesResultTree(t, res) 78 all := []testProfile{} 79 all = append(all, resp1.kept...) 80 all = append(all, resp2.kept...) 81 all = append(all, resp3.kept...) 82 sort.Slice(all, func(i, j int) bool { return all[i].Ts < all[j].Ts }) 83 testhelper.EqualProto(t, all, []testProfile{ 84 {Ts: 1, Labels: &typesv1.Labels{Labels: foobarlabels}}, 85 {Ts: 2, Labels: &typesv1.Labels{Labels: foobarlabels}}, 86 {Ts: 3, Labels: &typesv1.Labels{Labels: foobarlabels}}, 87 {Ts: 4, Labels: &typesv1.Labels{Labels: foobarlabels}}, 88 {Ts: 5, Labels: &typesv1.Labels{Labels: foobarlabels}}, 89 {Ts: 6, Labels: &typesv1.Labels{Labels: foobarlabels}}, 90 }) 91 res, err = selectMergeTree(context.Background(), []ResponseFromReplica[clientpool.BidiClientMergeProfilesStacktraces]{ 92 { 93 response: newFakeBidiClientStacktraces([]*ingestv1.ProfileSets{ 94 { 95 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 96 Profiles: []*ingestv1.SeriesProfile{ 97 {LabelIndex: 0, Timestamp: 1}, 98 {LabelIndex: 0, Timestamp: 2}, 99 {LabelIndex: 0, Timestamp: 4}, 100 }, 101 }, 102 { 103 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 104 Profiles: []*ingestv1.SeriesProfile{ 105 {LabelIndex: 0, Timestamp: 5}, 106 {LabelIndex: 0, Timestamp: 6}, 107 }, 108 }, 109 }), 110 }, 111 }) 112 require.NoError(t, err) 113 requireFakeMergeProfilesStacktracesResultTree(t, res) 114 } 115 116 func TestSelectMergeStacktracesWithBlockDeduplication(t *testing.T) { 117 } 118 119 func TestSelectMergeByLabels(t *testing.T) { 120 resp1 := newFakeBidiClientSeries([]*ingestv1.ProfileSets{ 121 { 122 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 123 Profiles: []*ingestv1.SeriesProfile{ 124 {LabelIndex: 0, Timestamp: 1}, 125 {LabelIndex: 0, Timestamp: 2}, 126 {LabelIndex: 0, Timestamp: 4}, 127 }, 128 }, 129 { 130 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 131 Profiles: []*ingestv1.SeriesProfile{ 132 {LabelIndex: 0, Timestamp: 5}, 133 {LabelIndex: 0, Timestamp: 6}, 134 }, 135 }, 136 }, &typesv1.Series{ 137 Labels: []*typesv1.LabelPair{{Name: "foo", Value: "bar"}}, 138 Points: []*typesv1.Point{{Timestamp: 1, Value: 1.0}, {Timestamp: 2, Value: 2.0}}, 139 }) 140 resp2 := newFakeBidiClientSeries([]*ingestv1.ProfileSets{ 141 { 142 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 143 Profiles: []*ingestv1.SeriesProfile{ 144 {LabelIndex: 0, Timestamp: 2}, 145 {LabelIndex: 0, Timestamp: 3}, 146 {LabelIndex: 0, Timestamp: 4}, 147 }, 148 }, 149 { 150 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 151 Profiles: []*ingestv1.SeriesProfile{ 152 {LabelIndex: 0, Timestamp: 5}, 153 {LabelIndex: 0, Timestamp: 6}, 154 }, 155 }, 156 }, &typesv1.Series{ 157 Labels: foobarlabels, 158 Points: []*typesv1.Point{{Timestamp: 3, Value: 3.0}, {Timestamp: 4, Value: 4.0}}, 159 }) 160 resp3 := newFakeBidiClientSeries([]*ingestv1.ProfileSets{ 161 { 162 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 163 Profiles: []*ingestv1.SeriesProfile{ 164 {LabelIndex: 0, Timestamp: 3}, 165 {LabelIndex: 0, Timestamp: 5}, 166 }, 167 }, 168 }, &typesv1.Series{ 169 Labels: foobarlabels, 170 Points: []*typesv1.Point{{Timestamp: 5, Value: 5.0}, {Timestamp: 6, Value: 6.0}}, 171 }) 172 173 res, err := selectMergeSeries(context.Background(), nil, []ResponseFromReplica[clientpool.BidiClientMergeProfilesLabels]{ 174 { 175 response: resp1, 176 }, 177 { 178 response: resp2, 179 }, 180 { 181 response: resp3, 182 }, 183 }) 184 require.NoError(t, err) 185 // ensure we have correctly selected the right profiles 186 all := []testProfile{} 187 all = append(all, resp1.kept...) 188 all = append(all, resp2.kept...) 189 all = append(all, resp3.kept...) 190 sort.Slice(all, func(i, j int) bool { return all[i].Ts < all[j].Ts }) 191 testhelper.EqualProto(t, all, []testProfile{ 192 {Ts: 1, Labels: &typesv1.Labels{Labels: foobarlabels}}, 193 {Ts: 2, Labels: &typesv1.Labels{Labels: foobarlabels}}, 194 {Ts: 3, Labels: &typesv1.Labels{Labels: foobarlabels}}, 195 {Ts: 4, Labels: &typesv1.Labels{Labels: foobarlabels}}, 196 {Ts: 5, Labels: &typesv1.Labels{Labels: foobarlabels}}, 197 {Ts: 6, Labels: &typesv1.Labels{Labels: foobarlabels}}, 198 }) 199 values, err := iter.Slice(res) 200 require.NoError(t, err) 201 require.Equal(t, []model.TimeSeriesValue{ 202 {Ts: 1, Value: 1.0, Lbs: foobarlabels, LabelsHash: foobarlabels.Hash()}, 203 {Ts: 2, Value: 2.0, Lbs: foobarlabels, LabelsHash: foobarlabels.Hash()}, 204 {Ts: 3, Value: 3.0, Lbs: foobarlabels, LabelsHash: foobarlabels.Hash()}, 205 {Ts: 4, Value: 4.0, Lbs: foobarlabels, LabelsHash: foobarlabels.Hash()}, 206 {Ts: 5, Value: 5.0, Lbs: foobarlabels, LabelsHash: foobarlabels.Hash()}, 207 {Ts: 6, Value: 6.0, Lbs: foobarlabels, LabelsHash: foobarlabels.Hash()}, 208 }, values) 209 } 210 211 func TestSelectMergePprof(t *testing.T) { 212 resp1 := newFakeBidiClientProfiles([]*ingestv1.ProfileSets{ 213 { 214 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 215 Profiles: []*ingestv1.SeriesProfile{ 216 {LabelIndex: 0, Timestamp: 1}, 217 {LabelIndex: 0, Timestamp: 2}, 218 {LabelIndex: 0, Timestamp: 4}, 219 }, 220 }, 221 { 222 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 223 Profiles: []*ingestv1.SeriesProfile{ 224 {LabelIndex: 0, Timestamp: 5}, 225 {LabelIndex: 0, Timestamp: 6}, 226 }, 227 }, 228 }) 229 resp2 := newFakeBidiClientProfiles([]*ingestv1.ProfileSets{ 230 { 231 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 232 Profiles: []*ingestv1.SeriesProfile{ 233 {LabelIndex: 0, Timestamp: 2}, 234 {LabelIndex: 0, Timestamp: 3}, 235 {LabelIndex: 0, Timestamp: 4}, 236 }, 237 }, 238 { 239 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 240 Profiles: []*ingestv1.SeriesProfile{ 241 {LabelIndex: 0, Timestamp: 5}, 242 {LabelIndex: 0, Timestamp: 6}, 243 }, 244 }, 245 }) 246 resp3 := newFakeBidiClientProfiles([]*ingestv1.ProfileSets{ 247 { 248 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 249 Profiles: []*ingestv1.SeriesProfile{ 250 {LabelIndex: 0, Timestamp: 3}, 251 {LabelIndex: 0, Timestamp: 5}, 252 }, 253 }, 254 }) 255 res, err := selectMergePprofProfile(context.Background(), &typesv1.ProfileType{}, []ResponseFromReplica[clientpool.BidiClientMergeProfilesPprof]{ 256 { 257 response: resp1, 258 }, 259 { 260 response: resp2, 261 }, 262 { 263 response: resp3, 264 }, 265 }) 266 require.NoError(t, err) 267 requireFakeMergeProfilesPprof(t, 3, res) 268 all := []testProfile{} 269 all = append(all, resp1.kept...) 270 all = append(all, resp2.kept...) 271 all = append(all, resp3.kept...) 272 sort.Slice(all, func(i, j int) bool { return all[i].Ts < all[j].Ts }) 273 testhelper.EqualProto(t, all, []testProfile{ 274 {Ts: 1, Labels: &typesv1.Labels{Labels: foobarlabels}}, 275 {Ts: 2, Labels: &typesv1.Labels{Labels: foobarlabels}}, 276 {Ts: 3, Labels: &typesv1.Labels{Labels: foobarlabels}}, 277 {Ts: 4, Labels: &typesv1.Labels{Labels: foobarlabels}}, 278 {Ts: 5, Labels: &typesv1.Labels{Labels: foobarlabels}}, 279 {Ts: 6, Labels: &typesv1.Labels{Labels: foobarlabels}}, 280 }) 281 res, err = selectMergePprofProfile(context.Background(), &typesv1.ProfileType{}, []ResponseFromReplica[clientpool.BidiClientMergeProfilesPprof]{ 282 { 283 response: newFakeBidiClientProfiles([]*ingestv1.ProfileSets{ 284 { 285 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 286 Profiles: []*ingestv1.SeriesProfile{ 287 {LabelIndex: 0, Timestamp: 1}, 288 {LabelIndex: 0, Timestamp: 2}, 289 {LabelIndex: 0, Timestamp: 4}, 290 }, 291 }, 292 { 293 LabelsSets: []*typesv1.Labels{{Labels: foobarlabels}}, 294 Profiles: []*ingestv1.SeriesProfile{ 295 {LabelIndex: 0, Timestamp: 5}, 296 {LabelIndex: 0, Timestamp: 6}, 297 }, 298 }, 299 }), 300 }, 301 }) 302 require.NoError(t, err) 303 requireFakeMergeProfilesPprof(t, 1, res) 304 } 305 306 func BenchmarkSelectMergeStacktraces(b *testing.B) { 307 rf := 3 308 clientsCount := 20 309 profilesCount := 2048 310 batchCount := 5 311 seriesCount := 50 312 // todo stacktraces := 1000 313 314 responses := make([]ResponseFromReplica[clientpool.BidiClientMergeProfilesStacktraces], clientsCount*rf) 315 316 for clientId := 0; clientId < clientsCount; clientId++ { 317 batches := make([]*ingestv1.ProfileSets, batchCount) 318 for batchID := 0; batchID < batchCount; batchID++ { 319 batches[batchID] = &ingestv1.ProfileSets{ 320 LabelsSets: make([]*typesv1.Labels, seriesCount), 321 Profiles: make([]*ingestv1.SeriesProfile, profilesCount*seriesCount), 322 } 323 batch := batches[batchID] 324 for i := 0; i < seriesCount; i++ { 325 batch.LabelsSets[i] = &typesv1.Labels{ 326 Labels: []*typesv1.LabelPair{ 327 {Name: "client", Value: strconv.Itoa(clientId)}, 328 {Name: "series", Value: strconv.Itoa(i)}, 329 }, 330 } 331 } 332 sort.Slice(batch.LabelsSets, func(i, j int) bool { 333 return model.CompareLabelPairs(batch.LabelsSets[i].Labels, batch.LabelsSets[j].Labels) < 0 334 }) 335 for j := 0; j < profilesCount; j++ { 336 for i := 0; i < seriesCount; i++ { 337 batch.Profiles[j+(i*profilesCount)] = &ingestv1.SeriesProfile{LabelIndex: int32(i), Timestamp: int64(j + (batchID * profilesCount))} 338 } 339 } 340 } 341 for replica := 0; replica < rf; replica++ { 342 responses[replica+(clientId*rf)] = ResponseFromReplica[clientpool.BidiClientMergeProfilesStacktraces]{ 343 response: newFakeBidiClientStacktraces(batches), 344 } 345 } 346 } 347 348 b.ResetTimer() 349 b.ReportAllocs() 350 for i := 0; i < b.N; i++ { 351 _, err := selectMergeTree(context.Background(), responses) 352 if err != nil { 353 b.Fatal(err) 354 } 355 } 356 }