github.com/grafana/pyroscope@v1.18.0/pkg/distributor/distributor_recvmetric_test.go (about) 1 package distributor 2 3 import ( 4 "context" 5 "os" 6 "regexp" 7 "testing" 8 9 "github.com/go-kit/log" 10 "github.com/grafana/dskit/ring" 11 "github.com/grafana/dskit/ring/client" 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/prometheus/client_golang/prometheus/testutil" 14 "github.com/prometheus/common/expfmt" 15 "github.com/prometheus/common/model" 16 "github.com/prometheus/prometheus/model/relabel" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 20 typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" 21 distributormodel "github.com/grafana/pyroscope/pkg/distributor/model" 22 phlaremodel "github.com/grafana/pyroscope/pkg/model" 23 "github.com/grafana/pyroscope/pkg/pprof" 24 "github.com/grafana/pyroscope/pkg/tenant" 25 "github.com/grafana/pyroscope/pkg/testhelper" 26 "github.com/grafana/pyroscope/pkg/validation" 27 ) 28 29 func TestDistributorPushWithDifferentTenantStages(t *testing.T) { 30 const tenantId = "239" 31 32 testCases := []struct { 33 name string 34 profilePath string 35 limitOverrides func(l *validation.Limits) 36 failIngester bool 37 expectErr bool 38 expectedErrMsg string 39 expectedMetrics []string 40 }{ 41 { 42 name: "successful push", 43 profilePath: "../../pkg/og/convert/testdata/cpu.pprof", 44 expectedMetrics: []string{ 45 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="normalized",tenant="239"} 2024`, 46 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="received",tenant="239"} 2198`, 47 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="sampled",tenant="239"} 2144`, 48 }, 49 }, 50 { 51 name: "rate limit - only received stage", 52 profilePath: "../../pkg/og/convert/testdata/cpu.pprof", 53 limitOverrides: func(l *validation.Limits) { 54 l.IngestionRateMB = 0.000001 55 l.IngestionBurstSizeMB = 0.000001 56 }, 57 expectErr: true, 58 expectedErrMsg: "rate limit", 59 expectedMetrics: []string{ 60 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="received",tenant="239"} 2198`, 61 }, 62 }, 63 64 { 65 name: "invalid profile", 66 profilePath: "../../pkg/og/convert/testdata/cpu.pprof", 67 limitOverrides: func(l *validation.Limits) { 68 l.MaxProfileSizeBytes = 2 69 }, 70 expectErr: true, 71 expectedErrMsg: "exceeds the size limit (max_profile_size_byte, actual: 2144, limit: 2)", 72 expectedMetrics: []string{ 73 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="received",tenant="239"} 2198`, 74 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="sampled",tenant="239"} 2144`, 75 }, 76 }, 77 { 78 name: "ingester fails", 79 profilePath: "../../pkg/og/convert/testdata/cpu.pprof", 80 failIngester: true, 81 expectErr: true, 82 expectedMetrics: []string{ 83 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="normalized",tenant="239"} 2024`, 84 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="received",tenant="239"} 2198`, 85 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="sampled",tenant="239"} 2144`, 86 }, 87 }, 88 { 89 name: "heap profile with sample type relabeling - keep only inuse_space", 90 profilePath: "../../pkg/pprof/testdata/heap", 91 limitOverrides: func(l *validation.Limits) { 92 l.SampleTypeRelabelingRules = []*relabel.Config{ 93 { 94 SourceLabels: []model.LabelName{"__type__"}, 95 Regex: relabel.MustNewRegexp("inuse_space"), 96 Action: relabel.Keep, 97 }, 98 } 99 }, 100 expectedMetrics: []string{ 101 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="normalized",tenant="239"} 46234`, 102 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="received",tenant="239"} 847192`, 103 `pyroscope_distributor_received_decompressed_bytes_total_sum{stage="sampled",tenant="239"} 847138`, 104 }, 105 }, 106 } 107 108 for _, tc := range testCases { 109 t.Run(tc.name, func(t *testing.T) { 110 reg := prometheus.NewRegistry() 111 112 ing := newFakeIngester(t, tc.failIngester) 113 114 overrides := validation.MockOverrides(func(defaults *validation.Limits, tenantLimits map[string]*validation.Limits) { 115 l := validation.MockDefaultLimits() 116 117 if tc.limitOverrides != nil { 118 tc.limitOverrides(l) 119 } 120 121 tenantLimits[tenantId] = l 122 }) 123 124 d, err := New( 125 Config{DistributorRing: ringConfig}, 126 testhelper.NewMockRing([]ring.InstanceDesc{{Addr: "foo"}}, 3), 127 &poolFactory{func(addr string) (client.PoolClient, error) { 128 return ing, nil 129 }}, 130 overrides, 131 reg, 132 log.NewLogfmtLogger(os.Stdout), 133 nil, 134 ) 135 require.NoError(t, err) 136 137 profileBytes, err := os.ReadFile(tc.profilePath) 138 require.NoError(t, err) 139 140 parsedProfile, err := pprof.RawFromBytes(profileBytes) 141 require.NoError(t, err) 142 143 ctx := tenant.InjectTenantID(context.Background(), tenantId) 144 req := &distributormodel.PushRequest{ 145 Series: []*distributormodel.ProfileSeries{ 146 { 147 Labels: []*typesv1.LabelPair{ 148 {Name: "cluster", Value: "test-cluster"}, 149 {Name: phlaremodel.LabelNameServiceName, Value: "test-service"}, 150 {Name: "__name__", Value: "cpu"}, 151 }, 152 RawProfile: profileBytes, 153 Profile: parsedProfile, 154 }, 155 }, 156 RawProfileType: distributormodel.RawProfileTypePPROF, 157 } 158 159 err = d.PushBatch(ctx, req) 160 161 if tc.expectErr { 162 require.Error(t, err) 163 if tc.expectedErrMsg != "" { 164 require.Contains(t, err.Error(), tc.expectedErrMsg) 165 } 166 } else { 167 require.NoError(t, err) 168 } 169 170 bs, err := testutil.CollectAndFormat(reg, expfmt.TypeTextPlain, "pyroscope_distributor_received_decompressed_bytes_total") 171 require.NoError(t, err) 172 173 sums := regexp.MustCompile("pyroscope_distributor_received_decompressed_bytes_total_sum.*"). 174 FindAllString(string(bs), -1) 175 176 assert.Equal(t, tc.expectedMetrics, sums) 177 }) 178 } 179 }