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  }