github.com/m3db/m3@v1.5.0/src/cmd/services/m3coordinator/downsample/metrics_appender_test.go (about)

     1  // Copyright (c) 2020 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 downsample
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"testing"
    27  
    28  	"github.com/m3db/m3/src/aggregator/aggregator"
    29  	"github.com/m3db/m3/src/metrics/matcher"
    30  	"github.com/m3db/m3/src/metrics/metadata"
    31  	"github.com/m3db/m3/src/metrics/metric/id"
    32  	"github.com/m3db/m3/src/metrics/metric/unaggregated"
    33  	"github.com/m3db/m3/src/metrics/rules"
    34  	"github.com/m3db/m3/src/x/checked"
    35  	"github.com/m3db/m3/src/x/pool"
    36  	"github.com/m3db/m3/src/x/serialize"
    37  	xtest "github.com/m3db/m3/src/x/test"
    38  	xtime "github.com/m3db/m3/src/x/time"
    39  
    40  	"github.com/golang/mock/gomock"
    41  	"github.com/stretchr/testify/assert"
    42  	"github.com/stretchr/testify/require"
    43  	"github.com/uber-go/tally"
    44  )
    45  
    46  func TestSamplesAppenderPoolResetsTagsAcrossSamples(t *testing.T) {
    47  	ctrl := xtest.NewController(t)
    48  	defer ctrl.Finish()
    49  
    50  	count := 3
    51  
    52  	poolOpts := pool.NewObjectPoolOptions().SetSize(1)
    53  
    54  	tagEncoderPool := serialize.NewTagEncoderPool(serialize.NewTagEncoderOptions(),
    55  		poolOpts)
    56  	tagEncoderPool.Init()
    57  
    58  	size := 1
    59  	tagDecoderPool := serialize.NewTagDecoderPool(
    60  		serialize.NewTagDecoderOptions(serialize.TagDecoderOptionsConfig{
    61  			CheckBytesWrapperPoolSize: &size,
    62  		}), poolOpts)
    63  	tagDecoderPool.Init()
    64  
    65  	appenderPool := newMetricsAppenderPool(poolOpts, serialize.NewTagSerializationLimits(), defaultMetricNameTagName)
    66  
    67  	for i := 0; i < count; i++ {
    68  		matcher := matcher.NewMockMatcher(ctrl)
    69  		matcher.EXPECT().ForwardMatch(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
    70  			DoAndReturn(func(encodedID id.ID, _, _ int64, _ rules.MatchOptions) (rules.MatchResult, error) {
    71  				// NB: ensure tags are cleared correctly between runs.
    72  				bs := encodedID.Bytes()
    73  
    74  				decoder := tagDecoderPool.Get()
    75  				decoder.Reset(checked.NewBytes(bs, nil))
    76  
    77  				var id string
    78  				for decoder.Next() {
    79  					tag := decoder.Current()
    80  					tagStr := fmt.Sprintf("%s-%s", tag.Name.String(), tag.Value.String())
    81  					if len(id) == 0 {
    82  						id = tagStr
    83  					} else {
    84  						id = fmt.Sprintf("%s,%s", id, tagStr)
    85  					}
    86  				}
    87  
    88  				decoder.Close()
    89  				return rules.NewMatchResult(1, 1,
    90  					metadata.StagedMetadatas{},
    91  					[]rules.IDWithMetadatas{
    92  						{
    93  							ID:        []byte(id),
    94  							Metadatas: metadata.StagedMetadatas{},
    95  						},
    96  					},
    97  					true,
    98  				), nil
    99  			})
   100  
   101  		appender := appenderPool.Get()
   102  		agg := aggregator.NewMockAggregator(ctrl)
   103  		appender.reset(metricsAppenderOptions{
   104  			tagEncoderPool: tagEncoderPool,
   105  			matcher:        matcher,
   106  			agg:            agg,
   107  			metrics: metricsAppenderMetrics{
   108  				processedCountNonRollup: tally.NoopScope.Counter("test-counter-non-rollup"),
   109  				processedCountRollup:    tally.NoopScope.Counter("test-counter-rollup"),
   110  				operationsCount:         tally.NoopScope.Counter("test-counter-operations"),
   111  			},
   112  		})
   113  		name := []byte(fmt.Sprint("foo", i))
   114  		value := []byte(fmt.Sprint("bar", i))
   115  		appender.AddTag(name, value)
   116  		a, err := appender.SamplesAppender(SampleAppenderOptions{})
   117  		require.NoError(t, err)
   118  
   119  		agg.EXPECT().AddUntimed(gomock.Any(), gomock.Any()).DoAndReturn(
   120  			func(u unaggregated.MetricUnion, _ metadata.StagedMetadatas) error {
   121  				if u.CounterVal != int64(i) {
   122  					return errors.New("wrong counter value")
   123  				}
   124  
   125  				// NB: expected ID is generated into human-readable form
   126  				// from tags in ForwardMatch mock above. Also include the m3 type, which is included when matching.
   127  				// nolint:scopelint
   128  				expected := fmt.Sprintf("__m3_prom_type__-unknown,__m3_type__-gauge,foo%d-bar%d", i, i)
   129  				if expected != u.ID.String() {
   130  					// NB: if this fails, appender is holding state after Finalize.
   131  					return fmt.Errorf("expected ID %s, got %s", expected, u.ID.String())
   132  				}
   133  
   134  				return nil
   135  			},
   136  		)
   137  
   138  		require.NoError(t, a.SamplesAppender.AppendUntimedCounterSample(xtime.Now(), int64(i), nil))
   139  
   140  		assert.False(t, a.IsDropPolicyApplied)
   141  		appender.Finalize()
   142  	}
   143  }
   144  
   145  func TestSamplesAppenderPoolResetsTagSimple(t *testing.T) {
   146  	ctrl := xtest.NewController(t)
   147  	defer ctrl.Finish()
   148  
   149  	poolOpts := pool.NewObjectPoolOptions().SetSize(1)
   150  	size := 1
   151  	tagDecoderPool := serialize.NewTagDecoderPool(
   152  		serialize.NewTagDecoderOptions(serialize.TagDecoderOptionsConfig{
   153  			CheckBytesWrapperPoolSize: &size,
   154  		}), poolOpts)
   155  	tagDecoderPool.Init()
   156  
   157  	appenderPool := newMetricsAppenderPool(poolOpts, serialize.NewTagSerializationLimits(), defaultMetricNameTagName)
   158  
   159  	appender := appenderPool.Get()
   160  	appender.AddTag([]byte("foo"), []byte("bar"))
   161  	assert.Equal(t, 1, len(appender.originalTags.names))
   162  	assert.Equal(t, 1, len(appender.originalTags.values))
   163  	appender.Finalize()
   164  
   165  	// NB: getting a new appender from the pool yields a clean appender.
   166  	appender = appenderPool.Get()
   167  	assert.Nil(t, appender.originalTags)
   168  	appender.Finalize()
   169  }