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  }