github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/prometheus/remote/write_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package remote
    22  
    23  import (
    24  	"bytes"
    25  	"context"
    26  	"errors"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"math"
    30  	"net/http"
    31  	"net/http/httptest"
    32  	"strings"
    33  	"testing"
    34  	"time"
    35  
    36  	"github.com/golang/mock/gomock"
    37  	"github.com/stretchr/testify/assert"
    38  	"github.com/stretchr/testify/require"
    39  	"github.com/uber-go/tally"
    40  
    41  	"github.com/m3db/m3/src/cmd/services/m3coordinator/ingest"
    42  	"github.com/m3db/m3/src/cmd/services/m3query/config"
    43  	"github.com/m3db/m3/src/dbnode/generated/proto/annotation"
    44  	"github.com/m3db/m3/src/metrics/policy"
    45  	"github.com/m3db/m3/src/query/api/v1/handler/prometheus/handleroptions"
    46  	"github.com/m3db/m3/src/query/api/v1/handler/prometheus/remote/test"
    47  	"github.com/m3db/m3/src/query/api/v1/options"
    48  	"github.com/m3db/m3/src/query/generated/proto/prompb"
    49  	"github.com/m3db/m3/src/query/models"
    50  	"github.com/m3db/m3/src/query/storage/m3/storagemetadata"
    51  	xclock "github.com/m3db/m3/src/x/clock"
    52  	xerrors "github.com/m3db/m3/src/x/errors"
    53  	"github.com/m3db/m3/src/x/headers"
    54  	"github.com/m3db/m3/src/x/instrument"
    55  	xtest "github.com/m3db/m3/src/x/test"
    56  )
    57  
    58  func makeOptions(ds ingest.DownsamplerAndWriter) options.HandlerOptions {
    59  	return options.EmptyHandlerOptions().
    60  		SetNowFn(time.Now).
    61  		SetDownsamplerAndWriter(ds).
    62  		SetTagOptions(models.NewTagOptions()).
    63  		SetConfig(config.Configuration{
    64  			WriteForwarding: config.WriteForwardingConfiguration{
    65  				PromRemoteWrite: handleroptions.PromWriteHandlerForwardingOptions{},
    66  			},
    67  		}).
    68  		SetStoreMetricsType(true)
    69  }
    70  
    71  func TestPromWriteParsing(t *testing.T) {
    72  	ctrl := xtest.NewController(t)
    73  	defer ctrl.Finish()
    74  
    75  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
    76  	handlerOpts := makeOptions(mockDownsamplerAndWriter)
    77  	handler, err := NewPromWriteHandler(handlerOpts)
    78  	require.NoError(t, err)
    79  
    80  	promReq := test.GeneratePromWriteRequest()
    81  	promReqBody := test.GeneratePromWriteRequestBody(t, promReq)
    82  	req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBody)
    83  
    84  	r, err := handler.(*PromWriteHandler).parseRequest(req)
    85  	require.Nil(t, err, "unable to parse request")
    86  	require.Equal(t, len(r.Request.Timeseries), 2)
    87  	require.Equal(t, ingest.WriteOptions{}, r.Options)
    88  }
    89  
    90  func TestPromWrite(t *testing.T) {
    91  	ctrl := xtest.NewController(t)
    92  	defer ctrl.Finish()
    93  
    94  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
    95  	mockDownsamplerAndWriter.
    96  		EXPECT().
    97  		WriteBatch(gomock.Any(), gomock.Any(), gomock.Any())
    98  
    99  	opts := makeOptions(mockDownsamplerAndWriter)
   100  	handler, err := NewPromWriteHandler(opts)
   101  	require.NoError(t, err)
   102  
   103  	promReq := test.GeneratePromWriteRequest()
   104  	promReqBody := test.GeneratePromWriteRequestBody(t, promReq)
   105  	req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBody)
   106  
   107  	writer := httptest.NewRecorder()
   108  	handler.ServeHTTP(writer, req)
   109  	resp := writer.Result()
   110  	require.Equal(t, http.StatusOK, resp.StatusCode)
   111  }
   112  
   113  func TestPromWriteError(t *testing.T) {
   114  	ctrl := xtest.NewController(t)
   115  	defer ctrl.Finish()
   116  
   117  	multiErr := xerrors.NewMultiError().Add(errors.New("an error"))
   118  	batchErr := ingest.BatchError(multiErr)
   119  
   120  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   121  	mockDownsamplerAndWriter.EXPECT().
   122  		WriteBatch(gomock.Any(), gomock.Any(), gomock.Any()).
   123  		Return(batchErr)
   124  
   125  	opts := makeOptions(mockDownsamplerAndWriter)
   126  	handler, err := NewPromWriteHandler(opts)
   127  	require.NoError(t, err)
   128  
   129  	promReq := test.GeneratePromWriteRequest()
   130  	promReqBody := test.GeneratePromWriteRequestBody(t, promReq)
   131  	req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBody)
   132  	require.NoError(t, err)
   133  
   134  	writer := httptest.NewRecorder()
   135  	handler.ServeHTTP(writer, req)
   136  	resp := writer.Result()
   137  	require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
   138  
   139  	body, err := ioutil.ReadAll(resp.Body)
   140  	require.NoError(t, err)
   141  	require.True(t, bytes.Contains(body, []byte(batchErr.Error())))
   142  }
   143  
   144  func TestWriteErrorMetricCount(t *testing.T) {
   145  	ctrl := xtest.NewController(t)
   146  	defer ctrl.Finish()
   147  
   148  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   149  
   150  	scope := tally.NewTestScope("",
   151  		map[string]string{"test": "error-metric-test"})
   152  
   153  	iopts := instrument.NewOptions().SetMetricsScope(scope)
   154  	opts := makeOptions(mockDownsamplerAndWriter).SetInstrumentOpts(iopts)
   155  	handler, err := NewPromWriteHandler(opts)
   156  	require.NoError(t, err)
   157  
   158  	req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, nil)
   159  	handler.ServeHTTP(httptest.NewRecorder(), req)
   160  
   161  	foundMetric := xclock.WaitUntil(func() bool {
   162  		found, ok := scope.Snapshot().Counters()["write.errors+code=4XX,handler=remote-write,test=error-metric-test"]
   163  		return ok && found.Value() == 1
   164  	}, 5*time.Second)
   165  	require.True(t, foundMetric)
   166  }
   167  
   168  func TestWriteDatapointDelayMetric(t *testing.T) {
   169  	ctrl := xtest.NewController(t)
   170  	defer ctrl.Finish()
   171  
   172  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   173  	mockDownsamplerAndWriter.
   174  		EXPECT().
   175  		WriteBatch(gomock.Any(), gomock.Any(), gomock.Any())
   176  
   177  	scope := tally.NewTestScope("",
   178  		map[string]string{"test": "delay-metric-test"})
   179  
   180  	iopts := instrument.NewOptions().SetMetricsScope(scope)
   181  	opts := makeOptions(mockDownsamplerAndWriter).SetInstrumentOpts(iopts)
   182  	handler, err := NewPromWriteHandler(opts)
   183  	require.NoError(t, err)
   184  
   185  	writeHandler, ok := handler.(*PromWriteHandler)
   186  	require.True(t, ok)
   187  
   188  	buckets := writeHandler.metrics.ingestLatencyBuckets
   189  
   190  	// NB(r): Bucket length is tested just to sanity check how many buckets we are creating
   191  	require.Equal(t, 80, len(buckets.AsDurations()))
   192  
   193  	// NB(r): Bucket values are tested to sanity check they look right
   194  	expected := "[0s 100ms 200ms 300ms 400ms 500ms 600ms 700ms 800ms 900ms 1s 1.5s 2s 2.5s 3s 3.5s 4s 4.5s 5s 5.5s 6s 6.5s 7s 7.5s 8s 8.5s 9s 9.5s 10s 15s 20s 25s 30s 35s 40s 45s 50s 55s 1m0s 5m0s 10m0s 15m0s 20m0s 25m0s 30m0s 35m0s 40m0s 45m0s 50m0s 55m0s 1h0m0s 1h30m0s 2h0m0s 2h30m0s 3h0m0s 3h30m0s 4h0m0s 4h30m0s 5h0m0s 5h30m0s 6h0m0s 6h30m0s 7h0m0s 8h0m0s 9h0m0s 10h0m0s 11h0m0s 12h0m0s 13h0m0s 14h0m0s 15h0m0s 16h0m0s 17h0m0s 18h0m0s 19h0m0s 20h0m0s 21h0m0s 22h0m0s 23h0m0s 24h0m0s]"
   195  	actual := fmt.Sprintf("%v", buckets.AsDurations())
   196  	require.Equal(t, expected, actual)
   197  
   198  	// Ensure buckets increasing in order
   199  	lastValue := time.Duration(math.MinInt64)
   200  	for _, value := range buckets.AsDurations() {
   201  		require.True(t, value > lastValue,
   202  			fmt.Sprintf("%s must be greater than last bucket value %s", value, lastValue))
   203  		lastValue = value
   204  	}
   205  
   206  	promReq := test.GeneratePromWriteRequest()
   207  	promReqBody := test.GeneratePromWriteRequestBody(t, promReq)
   208  	req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBody)
   209  	handler.ServeHTTP(httptest.NewRecorder(), req)
   210  
   211  	foundMetric := xclock.WaitUntil(func() bool {
   212  		values, found := scope.Snapshot().Histograms()["ingest.latency+handler=remote-write,test=delay-metric-test"]
   213  		if !found {
   214  			return false
   215  		}
   216  		for _, valuesInBucket := range values.Durations() {
   217  			if valuesInBucket > 0 {
   218  				return true
   219  			}
   220  		}
   221  		return false
   222  	}, 5*time.Second)
   223  	require.True(t, foundMetric)
   224  }
   225  
   226  func TestPromWriteUnaggregatedMetricsWithHeader(t *testing.T) {
   227  	ctrl := xtest.NewController(t)
   228  	defer ctrl.Finish()
   229  
   230  	expectedIngestWriteOptions := ingest.WriteOptions{
   231  		DownsampleOverride:     true,
   232  		DownsampleMappingRules: nil,
   233  		WriteOverride:          false,
   234  		WriteStoragePolicies:   nil,
   235  	}
   236  
   237  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   238  	mockDownsamplerAndWriter.
   239  		EXPECT().
   240  		WriteBatch(gomock.Any(), gomock.Any(), expectedIngestWriteOptions)
   241  
   242  	opts := makeOptions(mockDownsamplerAndWriter)
   243  	handler, err := NewPromWriteHandler(opts)
   244  	require.NoError(t, err)
   245  
   246  	promReq := test.GeneratePromWriteRequest()
   247  	promReqBody := test.GeneratePromWriteRequestBody(t, promReq)
   248  	req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBody)
   249  	req.Header.Add(headers.MetricsTypeHeader,
   250  		storagemetadata.UnaggregatedMetricsType.String())
   251  
   252  	writer := httptest.NewRecorder()
   253  	handler.ServeHTTP(writer, req)
   254  	resp := writer.Result()
   255  	require.Equal(t, http.StatusOK, resp.StatusCode)
   256  }
   257  
   258  func TestPromWriteAggregatedMetricsWithHeader(t *testing.T) {
   259  	ctrl := xtest.NewController(t)
   260  	defer ctrl.Finish()
   261  
   262  	expectedIngestWriteOptions := ingest.WriteOptions{
   263  		DownsampleOverride:     true,
   264  		DownsampleMappingRules: nil,
   265  		WriteOverride:          true,
   266  		WriteStoragePolicies: policy.StoragePolicies{
   267  			policy.MustParseStoragePolicy("1m:21d"),
   268  		},
   269  	}
   270  
   271  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   272  	mockDownsamplerAndWriter.
   273  		EXPECT().
   274  		WriteBatch(gomock.Any(), gomock.Any(), expectedIngestWriteOptions)
   275  
   276  	opts := makeOptions(mockDownsamplerAndWriter)
   277  	writeHandler, err := NewPromWriteHandler(opts)
   278  	require.NoError(t, err)
   279  
   280  	promReq := test.GeneratePromWriteRequest()
   281  	promReqBody := test.GeneratePromWriteRequestBody(t, promReq)
   282  	req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBody)
   283  	req.Header.Add(headers.MetricsTypeHeader,
   284  		storagemetadata.AggregatedMetricsType.String())
   285  	req.Header.Add(headers.MetricsStoragePolicyHeader,
   286  		"1m:21d")
   287  
   288  	writer := httptest.NewRecorder()
   289  	writeHandler.ServeHTTP(writer, req)
   290  	resp := writer.Result()
   291  	require.Equal(t, http.StatusOK, resp.StatusCode)
   292  }
   293  
   294  func TestPromWriteOpenMetricsTypes(t *testing.T) {
   295  	ctrl := xtest.NewController(t)
   296  	defer ctrl.Finish()
   297  
   298  	var capturedIter ingest.DownsampleAndWriteIter
   299  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   300  	mockDownsamplerAndWriter.
   301  		EXPECT().
   302  		WriteBatch(gomock.Any(), gomock.Any(), gomock.Any()).
   303  		Do(func(_ context.Context, iter ingest.DownsampleAndWriteIter, _ ingest.WriteOptions) ingest.BatchError {
   304  			capturedIter = iter
   305  			return nil
   306  		})
   307  
   308  	opts := makeOptions(mockDownsamplerAndWriter)
   309  
   310  	promReq := &prompb.WriteRequest{
   311  		Timeseries: []prompb.TimeSeries{
   312  			{Type: prompb.MetricType_UNKNOWN},
   313  			{Type: prompb.MetricType_COUNTER},
   314  			{Type: prompb.MetricType_GAUGE},
   315  			{Type: prompb.MetricType_GAUGE},
   316  			{Type: prompb.MetricType_SUMMARY},
   317  			{Type: prompb.MetricType_HISTOGRAM},
   318  			{Type: prompb.MetricType_GAUGE_HISTOGRAM},
   319  			{Type: prompb.MetricType_INFO},
   320  			{Type: prompb.MetricType_STATESET},
   321  			{},
   322  		},
   323  	}
   324  
   325  	executeWriteRequest(t, opts, promReq)
   326  
   327  	firstValue := verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_UNKNOWN, false)
   328  	secondValue := verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_COUNTER, true)
   329  	verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_GAUGE, false)
   330  	verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_GAUGE, false)
   331  	verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_SUMMARY, false)
   332  	verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_HISTOGRAM, true)
   333  	verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_GAUGE_HISTOGRAM, false)
   334  	verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_INFO, false)
   335  	verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_STATESET, false)
   336  	verifyIterValueAnnotation(t, capturedIter, annotation.OpenMetricsFamilyType_UNKNOWN, false)
   337  
   338  	require.False(t, capturedIter.Next())
   339  	require.NoError(t, capturedIter.Error())
   340  
   341  	assert.Nil(t, firstValue.Annotation, "first annotation invalidation")
   342  
   343  	secondAnnotationPayload := unmarshalAnnotation(t, secondValue.Annotation)
   344  	assert.Equal(t, annotation.Payload{
   345  		OpenMetricsFamilyType:        annotation.OpenMetricsFamilyType_COUNTER,
   346  		OpenMetricsHandleValueResets: true,
   347  	}, secondAnnotationPayload, "second annotation invalidated")
   348  }
   349  
   350  func TestPromWriteGraphiteMetricsTypes(t *testing.T) {
   351  	ctrl := xtest.NewController(t)
   352  	defer ctrl.Finish()
   353  
   354  	var capturedIter ingest.DownsampleAndWriteIter
   355  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   356  	mockDownsamplerAndWriter.
   357  		EXPECT().
   358  		WriteBatch(gomock.Any(), gomock.Any(), gomock.Any()).
   359  		Do(func(_ context.Context, iter ingest.DownsampleAndWriteIter, _ ingest.WriteOptions) ingest.BatchError {
   360  			capturedIter = iter
   361  			return nil
   362  		})
   363  
   364  	opts := makeOptions(mockDownsamplerAndWriter)
   365  
   366  	promReq := &prompb.WriteRequest{
   367  		Timeseries: []prompb.TimeSeries{
   368  			{Source: prompb.Source_GRAPHITE, M3Type: prompb.M3Type_M3_TIMER},
   369  			{Source: prompb.Source_GRAPHITE, M3Type: prompb.M3Type_M3_COUNTER},
   370  			{Source: prompb.Source_GRAPHITE, M3Type: prompb.M3Type_M3_GAUGE},
   371  			{Source: prompb.Source_GRAPHITE, M3Type: prompb.M3Type_M3_GAUGE},
   372  			{Source: prompb.Source_GRAPHITE, M3Type: prompb.M3Type_M3_TIMER},
   373  			{Source: prompb.Source_GRAPHITE, M3Type: prompb.M3Type_M3_COUNTER},
   374  		},
   375  	}
   376  
   377  	executeWriteRequest(t, opts, promReq)
   378  
   379  	verifyIterValueAnnotationGraphite(t, capturedIter, annotation.GraphiteType_GRAPHITE_TIMER)
   380  	verifyIterValueAnnotationGraphite(t, capturedIter, annotation.GraphiteType_GRAPHITE_COUNTER)
   381  	verifyIterValueAnnotationGraphite(t, capturedIter, annotation.GraphiteType_GRAPHITE_GAUGE)
   382  	verifyIterValueAnnotationGraphite(t, capturedIter, annotation.GraphiteType_GRAPHITE_GAUGE)
   383  	verifyIterValueAnnotationGraphite(t, capturedIter, annotation.GraphiteType_GRAPHITE_TIMER)
   384  	verifyIterValueAnnotationGraphite(t, capturedIter, annotation.GraphiteType_GRAPHITE_COUNTER)
   385  
   386  	require.False(t, capturedIter.Next())
   387  	require.NoError(t, capturedIter.Error())
   388  }
   389  
   390  func TestPromWriteDisabledMetricsTypes(t *testing.T) {
   391  	ctrl := xtest.NewController(t)
   392  	defer ctrl.Finish()
   393  
   394  	var capturedIter ingest.DownsampleAndWriteIter
   395  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   396  	mockDownsamplerAndWriter.
   397  		EXPECT().
   398  		WriteBatch(gomock.Any(), gomock.Any(), gomock.Any()).
   399  		Do(func(_ context.Context, iter ingest.DownsampleAndWriteIter, _ ingest.WriteOptions) ingest.BatchError {
   400  			capturedIter = iter
   401  			return nil
   402  		})
   403  
   404  	opts := makeOptions(mockDownsamplerAndWriter).SetStoreMetricsType(false)
   405  
   406  	promReq := &prompb.WriteRequest{
   407  		Timeseries: []prompb.TimeSeries{
   408  			{Type: prompb.MetricType_COUNTER},
   409  			{},
   410  		},
   411  	}
   412  
   413  	executeWriteRequest(t, opts, promReq)
   414  
   415  	verifyIterValueNoAnnotation(t, capturedIter)
   416  	verifyIterValueNoAnnotation(t, capturedIter)
   417  
   418  	require.False(t, capturedIter.Next())
   419  	require.NoError(t, capturedIter.Error())
   420  }
   421  
   422  func TestPromWriteLiteralIsTooLongError(t *testing.T) {
   423  	ctrl := xtest.NewController(t)
   424  	defer ctrl.Finish()
   425  
   426  	opts := makeOptions(ingest.NewMockDownsamplerAndWriter(ctrl))
   427  	handler, err := NewPromWriteHandler(opts)
   428  	require.NoError(t, err)
   429  
   430  	veryLongLiteral := strings.Repeat("x", int(opts.TagOptions().MaxTagLiteralLength())+1)
   431  	promReq := &prompb.WriteRequest{
   432  		Timeseries: []prompb.TimeSeries{
   433  			{
   434  				Labels: []prompb.Label{
   435  					{Name: []byte("name1"), Value: []byte("value1")},
   436  					{Name: []byte("name2"), Value: []byte(veryLongLiteral)},
   437  				},
   438  			},
   439  		},
   440  	}
   441  
   442  	for i := 0; i < maxLiteralIsTooLongLogCount*2; i++ {
   443  		promReqBody := test.GeneratePromWriteRequestBody(t, promReq)
   444  		req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBody)
   445  		writer := httptest.NewRecorder()
   446  		handler.ServeHTTP(writer, req)
   447  		resp := writer.Result()
   448  		require.Equal(t, http.StatusBadRequest, resp.StatusCode)
   449  		require.NoError(t, resp.Body.Close())
   450  	}
   451  }
   452  
   453  func BenchmarkWriteDatapoints(b *testing.B) {
   454  	ctrl := xtest.NewController(b)
   455  	defer ctrl.Finish()
   456  
   457  	mockDownsamplerAndWriter := ingest.NewMockDownsamplerAndWriter(ctrl)
   458  	mockDownsamplerAndWriter.
   459  		EXPECT().
   460  		WriteBatch(gomock.Any(), gomock.Any(), gomock.Any()).
   461  		AnyTimes()
   462  
   463  	opts := makeOptions(mockDownsamplerAndWriter)
   464  	handler, err := NewPromWriteHandler(opts)
   465  	require.NoError(b, err)
   466  
   467  	promReq := test.GeneratePromWriteRequest()
   468  	promReqBody := test.GeneratePromWriteRequestBodyBytes(b, promReq)
   469  	promReqBodyReader := bytes.NewReader(nil)
   470  
   471  	for i := 0; i < b.N; i++ {
   472  		promReqBodyReader.Reset(promReqBody)
   473  		req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBodyReader)
   474  		handler.ServeHTTP(httptest.NewRecorder(), req)
   475  	}
   476  }
   477  
   478  func verifyIterValueAnnotation(
   479  	t *testing.T,
   480  	iter ingest.DownsampleAndWriteIter,
   481  	expectedMetricType annotation.OpenMetricsFamilyType,
   482  	expectedHandleValueResets bool,
   483  ) ingest.IterValue {
   484  	require.True(t, iter.Next())
   485  	value := iter.Current()
   486  
   487  	expectedPayload := annotation.Payload{
   488  		SourceFormat:                 annotation.SourceFormat_OPEN_METRICS,
   489  		OpenMetricsFamilyType:        expectedMetricType,
   490  		OpenMetricsHandleValueResets: expectedHandleValueResets,
   491  	}
   492  	assert.Equal(t, expectedPayload, unmarshalAnnotation(t, value.Annotation))
   493  
   494  	return value
   495  }
   496  
   497  func verifyIterValueAnnotationGraphite(
   498  	t *testing.T,
   499  	iter ingest.DownsampleAndWriteIter,
   500  	expectedMetricType annotation.GraphiteType,
   501  ) {
   502  	require.True(t, iter.Next())
   503  	value := iter.Current()
   504  
   505  	expectedPayload := annotation.Payload{
   506  		SourceFormat: annotation.SourceFormat_GRAPHITE,
   507  		GraphiteType: expectedMetricType,
   508  	}
   509  	assert.Equal(t, expectedPayload, unmarshalAnnotation(t, value.Annotation))
   510  }
   511  
   512  func verifyIterValueNoAnnotation(t *testing.T, iter ingest.DownsampleAndWriteIter) {
   513  	require.True(t, iter.Next())
   514  	value := iter.Current()
   515  	assert.Nil(t, value.Annotation)
   516  }
   517  
   518  func unmarshalAnnotation(t *testing.T, annot []byte) annotation.Payload {
   519  	payload := annotation.Payload{}
   520  	require.NoError(t, payload.Unmarshal(annot))
   521  	return payload
   522  }
   523  
   524  func executeWriteRequest(t *testing.T, handlerOpts options.HandlerOptions, promReq *prompb.WriteRequest) {
   525  	handler, err := NewPromWriteHandler(handlerOpts)
   526  	require.NoError(t, err)
   527  
   528  	promReqBody := test.GeneratePromWriteRequestBody(t, promReq)
   529  	req := httptest.NewRequest(PromWriteHTTPMethod, PromWriteURL, promReqBody)
   530  
   531  	writer := httptest.NewRecorder()
   532  	handler.ServeHTTP(writer, req)
   533  	resp := writer.Result()
   534  	require.Equal(t, http.StatusOK, resp.StatusCode)
   535  }