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

     1  // Copyright (c) 2019 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 ingest
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/cmd/services/m3coordinator/downsample"
    30  	"github.com/m3db/m3/src/dbnode/client"
    31  	"github.com/m3db/m3/src/metrics/aggregation"
    32  	"github.com/m3db/m3/src/metrics/policy"
    33  	"github.com/m3db/m3/src/query/models"
    34  	"github.com/m3db/m3/src/query/storage"
    35  	"github.com/m3db/m3/src/query/storage/m3"
    36  	testm3 "github.com/m3db/m3/src/query/test/m3"
    37  	"github.com/m3db/m3/src/query/ts"
    38  	xerrors "github.com/m3db/m3/src/x/errors"
    39  	"github.com/m3db/m3/src/x/ident"
    40  	"github.com/m3db/m3/src/x/instrument"
    41  	xsync "github.com/m3db/m3/src/x/sync"
    42  	xtime "github.com/m3db/m3/src/x/time"
    43  
    44  	"github.com/golang/mock/gomock"
    45  	"github.com/stretchr/testify/require"
    46  )
    47  
    48  var (
    49  	// Created by init().
    50  	testWorkerPool xsync.PooledWorkerPool
    51  
    52  	source = ts.SourceTypePrometheus
    53  
    54  	testTags1 = models.NewTags(3, nil).AddTags(
    55  		[]models.Tag{
    56  			{
    57  				Name:  []byte("test_1_key_1"),
    58  				Value: []byte("test_1_value_1"),
    59  			},
    60  			{
    61  				Name:  []byte("test_1_key_2"),
    62  				Value: []byte("test_1_value_2"),
    63  			},
    64  			{
    65  				Name:  []byte("test_1_key_3"),
    66  				Value: []byte("test_1_value_3"),
    67  			},
    68  		},
    69  	)
    70  	testTags2 = models.NewTags(3, nil).AddTags(
    71  		[]models.Tag{
    72  			{
    73  				Name:  []byte("test_2_key_1"),
    74  				Value: []byte("test_2_value_1"),
    75  			},
    76  			{
    77  				Name:  []byte("test_2_key_2"),
    78  				Value: []byte("test_2_value_2"),
    79  			},
    80  			{
    81  				Name:  []byte("test_2_key_3"),
    82  				Value: []byte("test_2_value_3"),
    83  			},
    84  		},
    85  	)
    86  	testBadTags = models.NewTags(3, nil).AddTags([]models.Tag{
    87  		{
    88  			Name:  []byte("standard_tag"),
    89  			Value: []byte("standard_tag_value"),
    90  		},
    91  		{
    92  			Name:  []byte("duplicate_tag"),
    93  			Value: []byte("duplicate_tag_value0"),
    94  		},
    95  		{
    96  			Name:  []byte("duplicate_tag"),
    97  			Value: []byte("duplicate_tag_value1"),
    98  		},
    99  	})
   100  
   101  	testDatapoints1 = []ts.Datapoint{
   102  		{
   103  			Timestamp: xtime.UnixNano(0),
   104  			Value:     0,
   105  		},
   106  		{
   107  			Timestamp: xtime.UnixNano(1),
   108  			Value:     1,
   109  		},
   110  		{
   111  			Timestamp: xtime.UnixNano(2),
   112  			Value:     2,
   113  		},
   114  	}
   115  	testDatapoints2 = []ts.Datapoint{
   116  		{
   117  			Timestamp: xtime.UnixNano(3),
   118  			Value:     3,
   119  		},
   120  		{
   121  			Timestamp: xtime.UnixNano(4),
   122  			Value:     4,
   123  		},
   124  		{
   125  			Timestamp: xtime.UnixNano(5),
   126  			Value:     5,
   127  		},
   128  	}
   129  
   130  	testAnnotation1 = []byte("first")
   131  	testAnnotation2 = []byte("second")
   132  
   133  	testAttributesGauge = ts.SeriesAttributes{
   134  		M3Type: ts.M3MetricTypeGauge,
   135  	}
   136  	testAttributesCounter = ts.SeriesAttributes{
   137  		M3Type: ts.M3MetricTypeCounter,
   138  	}
   139  	testAttributesTimer = ts.SeriesAttributes{
   140  		M3Type: ts.M3MetricTypeTimer,
   141  	}
   142  
   143  	testEntries = []testIterEntry{
   144  		{tags: testTags1, datapoints: testDatapoints1, attributes: testAttributesGauge, annotation: testAnnotation1},
   145  		{tags: testTags2, datapoints: testDatapoints2, attributes: testAttributesGauge, annotation: testAnnotation2},
   146  	}
   147  
   148  	testEntries2 = []testIterEntry{
   149  		{tags: testTags1, datapoints: testDatapoints1, attributes: testAttributesCounter, annotation: testAnnotation1},
   150  		{tags: testTags2, datapoints: testDatapoints2, attributes: testAttributesTimer, annotation: testAnnotation2},
   151  	}
   152  
   153  	defaultOverride = WriteOptions{}
   154  
   155  	zeroDownsamplerAppenderOpts = downsample.SampleAppenderOptions{}
   156  )
   157  
   158  type testIter struct {
   159  	idx       int
   160  	entries   []testIterEntry
   161  	metadatas []ts.Metadata
   162  }
   163  
   164  type testIterEntry struct {
   165  	tags       models.Tags
   166  	datapoints []ts.Datapoint
   167  	annotation []byte
   168  	attributes ts.SeriesAttributes
   169  }
   170  
   171  func newTestIter(entries []testIterEntry) *testIter {
   172  	return &testIter{
   173  		idx:       -1,
   174  		entries:   entries,
   175  		metadatas: make([]ts.Metadata, 10),
   176  	}
   177  }
   178  
   179  func (i *testIter) Next() bool {
   180  	i.idx++
   181  	return i.idx < len(i.entries)
   182  }
   183  
   184  func (i *testIter) Current() IterValue {
   185  	if len(i.entries) == 0 || i.idx < 0 || i.idx >= len(i.entries) {
   186  		return IterValue{
   187  			Tags:       models.EmptyTags(),
   188  			Attributes: ts.DefaultSeriesAttributes(),
   189  		}
   190  	}
   191  
   192  	curr := i.entries[i.idx]
   193  	value := IterValue{
   194  		Tags:       curr.tags,
   195  		Datapoints: curr.datapoints,
   196  		Attributes: curr.attributes,
   197  		Unit:       xtime.Second,
   198  		Annotation: curr.annotation,
   199  	}
   200  	if i.idx < len(i.metadatas) {
   201  		value.Metadata = i.metadatas[i.idx]
   202  	}
   203  	return value
   204  }
   205  
   206  func (i *testIter) Reset() error {
   207  	i.idx = -1
   208  	return nil
   209  }
   210  
   211  func (i *testIter) Error() error {
   212  	return nil
   213  }
   214  
   215  func (i *testIter) SetCurrentMetadata(metadata ts.Metadata) {
   216  	i.metadatas[i.idx] = metadata
   217  }
   218  
   219  func TestDownsampleAndWrite(t *testing.T) {
   220  	ctrl := gomock.NewController(t)
   221  	defer ctrl.Finish()
   222  
   223  	downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl,
   224  		testDownsamplerAndWriterOptions{})
   225  
   226  	expectDefaultDownsampling(ctrl, testDatapoints1, downsampler, zeroDownsamplerAppenderOpts)
   227  	expectDefaultStorageWrites(session, testDatapoints1, testAnnotation1)
   228  
   229  	err := downAndWrite.Write(
   230  		context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, defaultOverride, source)
   231  	require.NoError(t, err)
   232  }
   233  
   234  func TestDownsampleAndWriteWithBadTags(t *testing.T) {
   235  	ctrl := gomock.NewController(t)
   236  	defer ctrl.Finish()
   237  
   238  	downAndWrite, _, _ := newTestDownsamplerAndWriter(t, ctrl,
   239  		testDownsamplerAndWriterOptions{})
   240  
   241  	err := downAndWrite.Write(
   242  		context.Background(), testBadTags, testDatapoints1, xtime.Second, testAnnotation1, defaultOverride, source)
   243  	require.Error(t, err)
   244  
   245  	// Make sure we get a validation error for downsample code path
   246  	// and for the raw unaggregate write code path.
   247  	multiErr, ok := xerrors.GetInnerMultiError(err)
   248  	require.True(t, ok)
   249  	require.Equal(t, 2, multiErr.NumErrors())
   250  	// Make sure all are invalid params errors.
   251  	for _, err := range multiErr.Errors() {
   252  		require.True(t, xerrors.IsInvalidParams(err))
   253  	}
   254  }
   255  
   256  func TestDownsampleAndWriteWithDownsampleOverridesAndNoMappingRules(t *testing.T) {
   257  	ctrl := gomock.NewController(t)
   258  	defer ctrl.Finish()
   259  
   260  	downAndWrite, _, session := newTestDownsamplerAndWriter(t, ctrl,
   261  		testDownsamplerAndWriterOptions{})
   262  
   263  	// We're overriding the downsampling with zero mapping rules, so we expect no data to be sent
   264  	// to the downsampler, but everything to be written to storage.
   265  	overrides := WriteOptions{
   266  		DownsampleOverride:     true,
   267  		DownsampleMappingRules: nil,
   268  	}
   269  
   270  	expectDefaultStorageWrites(session, testDatapoints1, testAnnotation1)
   271  
   272  	err := downAndWrite.Write(context.Background(),
   273  		testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source)
   274  	require.NoError(t, err)
   275  }
   276  
   277  func TestDownsampleAndWriteWithDownsampleOverridesAndMappingRules(t *testing.T) {
   278  	ctrl := gomock.NewController(t)
   279  	defer ctrl.Finish()
   280  
   281  	downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl,
   282  		testDownsamplerAndWriterOptions{})
   283  
   284  	// We're overriding the downsampling with mapping rules, so we expect data to be
   285  	// sent to the downsampler, as well as everything being written to storage.
   286  	mappingRules := []downsample.AutoMappingRule{
   287  		{
   288  			Aggregations: []aggregation.Type{aggregation.Mean},
   289  			Policies: []policy.StoragePolicy{
   290  				policy.NewStoragePolicy(
   291  					time.Minute, xtime.Second, 48*time.Hour),
   292  			},
   293  		},
   294  	}
   295  	overrides := WriteOptions{
   296  		DownsampleOverride:     true,
   297  		DownsampleMappingRules: mappingRules,
   298  	}
   299  
   300  	expectedSamplesAppenderOptions := downsample.SampleAppenderOptions{
   301  		Override: true,
   302  		OverrideRules: downsample.SamplesAppenderOverrideRules{
   303  			MappingRules: mappingRules,
   304  		},
   305  	}
   306  
   307  	expectDefaultDownsampling(ctrl, testDatapoints1, downsampler, expectedSamplesAppenderOptions)
   308  	expectDefaultStorageWrites(session, testDatapoints1, testAnnotation1)
   309  
   310  	err := downAndWrite.Write(
   311  		context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source)
   312  	require.NoError(t, err)
   313  }
   314  
   315  func TestDownsampleAndWriteWithDownsampleOverridesAndDropMappingRules(t *testing.T) {
   316  	ctrl := gomock.NewController(t)
   317  	defer ctrl.Finish()
   318  
   319  	downAndWrite, downsampler, _ := newTestDownsamplerAndWriter(t, ctrl,
   320  		testDownsamplerAndWriterOptions{})
   321  
   322  	// We're overriding the downsampling with mapping rules, so we expect data to be
   323  	// sent to the downsampler, as well as everything being written to storage.
   324  	mappingRules := []downsample.AutoMappingRule{
   325  		{
   326  			Aggregations: []aggregation.Type{aggregation.Mean},
   327  			Policies: []policy.StoragePolicy{
   328  				policy.NewStoragePolicy(
   329  					time.Minute, xtime.Second, 48*time.Hour),
   330  			},
   331  		},
   332  	}
   333  	overrides := WriteOptions{
   334  		DownsampleOverride:     true,
   335  		DownsampleMappingRules: mappingRules,
   336  	}
   337  
   338  	expectedSamplesAppenderOptions := downsample.SampleAppenderOptions{
   339  		Override: true,
   340  		OverrideRules: downsample.SamplesAppenderOverrideRules{
   341  			MappingRules: mappingRules,
   342  		},
   343  	}
   344  
   345  	var (
   346  		mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl)
   347  		mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl)
   348  	)
   349  
   350  	mockMetricsAppender.
   351  		EXPECT().
   352  		SamplesAppender(expectedSamplesAppenderOptions).
   353  		Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender, IsDropPolicyApplied: true}, nil)
   354  	for _, tag := range testTags1.Tags {
   355  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   356  	}
   357  
   358  	for _, dp := range testDatapoints1 {
   359  		mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1)
   360  	}
   361  	downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil)
   362  
   363  	mockMetricsAppender.EXPECT().Finalize()
   364  
   365  	err := downAndWrite.Write(
   366  		context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source)
   367  	require.NoError(t, err)
   368  }
   369  
   370  func TestDownsampleAndWriteWithWriteOverridesAndNoStoragePolicies(t *testing.T) {
   371  	ctrl := gomock.NewController(t)
   372  	defer ctrl.Finish()
   373  
   374  	downAndWrite, downsampler, _ := newTestDownsamplerAndWriter(t, ctrl,
   375  		testDownsamplerAndWriterOptions{})
   376  
   377  	// We're overriding the write with zero storage policies, so we expect no data to be sent
   378  	// to the storage, but everything to be written to the downsampler with the default settings.
   379  	overrides := WriteOptions{
   380  		WriteOverride:        true,
   381  		WriteStoragePolicies: nil,
   382  	}
   383  
   384  	expectDefaultDownsampling(ctrl, testDatapoints1, downsampler, zeroDownsamplerAppenderOpts)
   385  
   386  	err := downAndWrite.Write(
   387  		context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source)
   388  	require.NoError(t, err)
   389  }
   390  
   391  func TestDownsampleAndWriteWithWriteOverridesAndStoragePolicies(t *testing.T) {
   392  	ctrl := gomock.NewController(t)
   393  	defer ctrl.Finish()
   394  
   395  	aggregatedNamespaces := []m3.AggregatedClusterNamespaceDefinition{
   396  		{
   397  			NamespaceID: ident.StringID("1m:48h"),
   398  			Resolution:  time.Minute,
   399  			Retention:   48 * time.Hour,
   400  		},
   401  		{
   402  			NamespaceID: ident.StringID("10:24h"),
   403  			Resolution:  10 * time.Second,
   404  			Retention:   24 * time.Hour,
   405  		},
   406  	}
   407  	downAndWrite, downsampler, session := newTestDownsamplerAndWriterWithAggregatedNamespace(
   408  		t, ctrl, aggregatedNamespaces)
   409  
   410  	// We're overriding the write with storage policies, so we expect data to be sent to the
   411  	// storage with the specified policies, but everything to be written to the downsampler
   412  	// with the default settings.
   413  	overrides := WriteOptions{
   414  		WriteOverride: true,
   415  		WriteStoragePolicies: []policy.StoragePolicy{
   416  			policy.NewStoragePolicy(
   417  				time.Minute, xtime.Second, 48*time.Hour),
   418  			policy.NewStoragePolicy(
   419  				10*time.Second, xtime.Second, 24*time.Hour),
   420  		},
   421  	}
   422  
   423  	expectDefaultDownsampling(ctrl, testDatapoints1, downsampler, zeroDownsamplerAppenderOpts)
   424  
   425  	// All the datapoints will get written for each of the namespaces.
   426  	for range aggregatedNamespaces {
   427  		for _, dp := range testDatapoints1 {
   428  			session.EXPECT().WriteTagged(
   429  				gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), gomock.Any())
   430  		}
   431  	}
   432  
   433  	err := downAndWrite.Write(
   434  		context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source)
   435  	require.NoError(t, err)
   436  }
   437  
   438  func TestDownsampleAndWriteNoDownsampler(t *testing.T) {
   439  	ctrl := gomock.NewController(t)
   440  	defer ctrl.Finish()
   441  
   442  	downAndWrite, _, session := newTestDownsamplerAndWriterWithEnabled(t, ctrl, false,
   443  		testDownsamplerAndWriterOptions{})
   444  
   445  	expectDefaultStorageWrites(session, testDatapoints1, testAnnotation1)
   446  
   447  	err := downAndWrite.Write(
   448  		context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, defaultOverride, source)
   449  	require.NoError(t, err)
   450  }
   451  
   452  func TestDownsampleAndWriteBatch(t *testing.T) {
   453  	ctrl := gomock.NewController(t)
   454  	defer ctrl.Finish()
   455  
   456  	downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl,
   457  		testDownsamplerAndWriterOptions{})
   458  
   459  	var (
   460  		mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl)
   461  		mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl)
   462  	)
   463  
   464  	mockMetricsAppender.
   465  		EXPECT().
   466  		SamplesAppender(zeroDownsamplerAppenderOpts).
   467  		Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).Times(2)
   468  	for _, tag := range testTags1.Tags {
   469  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   470  	}
   471  	for _, dp := range testDatapoints1 {
   472  		mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1)
   473  	}
   474  	for _, tag := range testTags2.Tags {
   475  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   476  	}
   477  	for _, dp := range testDatapoints2 {
   478  		mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation2)
   479  	}
   480  	downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil)
   481  
   482  	mockMetricsAppender.EXPECT().NextMetric().Times(2)
   483  	mockMetricsAppender.EXPECT().Finalize()
   484  
   485  	for _, entry := range testEntries {
   486  		for _, dp := range entry.datapoints {
   487  			session.EXPECT().WriteTagged(
   488  				gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation,
   489  			)
   490  		}
   491  	}
   492  
   493  	iter := newTestIter(testEntries)
   494  	err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{})
   495  	require.NoError(t, err)
   496  }
   497  
   498  func TestDownsampleAndWriteBatchBadTags(t *testing.T) {
   499  	ctrl := gomock.NewController(t)
   500  	defer ctrl.Finish()
   501  
   502  	downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl,
   503  		testDownsamplerAndWriterOptions{})
   504  
   505  	var (
   506  		mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl)
   507  		mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl)
   508  	)
   509  
   510  	entries := []testIterEntry{
   511  		{tags: testBadTags, datapoints: testDatapoints1, attributes: testAttributesGauge, annotation: testAnnotation1},
   512  		{tags: testTags2, datapoints: testDatapoints2, attributes: testAttributesGauge, annotation: testAnnotation2},
   513  	}
   514  
   515  	// Only expect to write non-bad tags.
   516  	mockMetricsAppender.
   517  		EXPECT().
   518  		SamplesAppender(zeroDownsamplerAppenderOpts).
   519  		Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).Times(1)
   520  	for _, tag := range testTags2.Tags {
   521  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   522  	}
   523  	for _, dp := range testDatapoints2 {
   524  		mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation2)
   525  	}
   526  	downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil)
   527  
   528  	mockMetricsAppender.EXPECT().NextMetric().Times(2)
   529  	mockMetricsAppender.EXPECT().Finalize()
   530  
   531  	// Only expect to write non-bad tags.
   532  	for _, entry := range testEntries[1:] {
   533  		for _, dp := range entry.datapoints {
   534  			session.EXPECT().WriteTagged(
   535  				gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation,
   536  			)
   537  		}
   538  	}
   539  
   540  	iter := newTestIter(entries)
   541  	err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{})
   542  	require.Error(t, err)
   543  
   544  	// Make sure we get a validation error for downsample code path
   545  	// and for the raw unaggregate write code path.
   546  	multiErr, ok := err.(xerrors.MultiError)
   547  	require.True(t, ok)
   548  	require.Equal(t, 2, multiErr.NumErrors())
   549  	// Make sure all are invalid params errors.
   550  	for _, err := range multiErr.Errors() {
   551  		require.True(t, xerrors.IsInvalidParams(err))
   552  	}
   553  }
   554  
   555  func TestDownsampleAndWriteBatchDifferentTypes(t *testing.T) {
   556  	ctrl := gomock.NewController(t)
   557  	defer ctrl.Finish()
   558  
   559  	downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl,
   560  		testDownsamplerAndWriterOptions{})
   561  
   562  	var (
   563  		mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl)
   564  		mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl)
   565  	)
   566  
   567  	mockMetricsAppender.
   568  		EXPECT().
   569  		SamplesAppender(downsample.SampleAppenderOptions{
   570  			SeriesAttributes: ts.SeriesAttributes{M3Type: ts.M3MetricTypeCounter},
   571  		}).Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).
   572  		Times(1)
   573  	mockMetricsAppender.
   574  		EXPECT().
   575  		SamplesAppender(downsample.SampleAppenderOptions{
   576  			SeriesAttributes: ts.SeriesAttributes{M3Type: ts.M3MetricTypeTimer},
   577  		}).Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).
   578  		Times(1)
   579  	for _, tag := range testTags1.Tags {
   580  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   581  	}
   582  	for _, dp := range testDatapoints1 {
   583  		mockSamplesAppender.EXPECT().AppendCounterSample(dp.Timestamp, int64(dp.Value), testAnnotation1)
   584  	}
   585  	for _, tag := range testTags2.Tags {
   586  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   587  	}
   588  	for _, dp := range testDatapoints2 {
   589  		mockSamplesAppender.EXPECT().AppendTimerSample(dp.Timestamp, dp.Value, testAnnotation2)
   590  	}
   591  	downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil)
   592  
   593  	mockMetricsAppender.EXPECT().NextMetric().Times(2)
   594  	mockMetricsAppender.EXPECT().Finalize()
   595  
   596  	for _, entry := range testEntries2 {
   597  		for _, dp := range entry.datapoints {
   598  			session.EXPECT().WriteTagged(
   599  				gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation,
   600  			)
   601  		}
   602  	}
   603  
   604  	iter := newTestIter(testEntries2)
   605  	err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{})
   606  	require.NoError(t, err)
   607  }
   608  
   609  func TestDownsampleAndWriteBatchSingleDrop(t *testing.T) {
   610  	ctrl := gomock.NewController(t)
   611  	defer ctrl.Finish()
   612  
   613  	downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl,
   614  		testDownsamplerAndWriterOptions{})
   615  
   616  	var (
   617  		mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl)
   618  		mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl)
   619  	)
   620  
   621  	mockMetricsAppender.
   622  		EXPECT().
   623  		SamplesAppender(zeroDownsamplerAppenderOpts).
   624  		Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender, IsDropPolicyApplied: true}, nil).Times(1)
   625  	mockMetricsAppender.
   626  		EXPECT().
   627  		SamplesAppender(zeroDownsamplerAppenderOpts).
   628  		Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).Times(1)
   629  	for _, tag := range testTags1.Tags {
   630  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   631  	}
   632  	for _, dp := range testDatapoints1 {
   633  		mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1)
   634  	}
   635  	for _, tag := range testTags2.Tags {
   636  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   637  	}
   638  	for _, dp := range testDatapoints2 {
   639  		mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation2)
   640  	}
   641  	downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil)
   642  
   643  	mockMetricsAppender.EXPECT().NextMetric().Times(2)
   644  	mockMetricsAppender.EXPECT().Finalize()
   645  
   646  	for _, dp := range testEntries[1].datapoints {
   647  		session.EXPECT().WriteTagged(
   648  			gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), testEntries[1].annotation,
   649  		)
   650  	}
   651  
   652  	iter := newTestIter(testEntries)
   653  	err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{})
   654  	require.NoError(t, err)
   655  }
   656  
   657  func TestDownsampleAndWriteBatchDropTimestamp(t *testing.T) {
   658  	ctrl := gomock.NewController(t)
   659  	defer ctrl.Finish()
   660  
   661  	downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl,
   662  		testDownsamplerAndWriterOptions{})
   663  
   664  	var (
   665  		mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl)
   666  		mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl)
   667  	)
   668  
   669  	mockMetricsAppender.
   670  		EXPECT().
   671  		SamplesAppender(zeroDownsamplerAppenderOpts).
   672  		Return(downsample.SamplesAppenderResult{
   673  			SamplesAppender:     mockSamplesAppender,
   674  			ShouldDropTimestamp: true,
   675  		}, nil).Times(1)
   676  	mockMetricsAppender.
   677  		EXPECT().
   678  		SamplesAppender(zeroDownsamplerAppenderOpts).
   679  		Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).Times(1)
   680  	for _, tag := range testTags1.Tags {
   681  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   682  	}
   683  	for _, dp := range testDatapoints1 {
   684  		mockSamplesAppender.EXPECT().AppendUntimedGaugeSample(dp.Timestamp, dp.Value, testAnnotation1)
   685  	}
   686  	for _, tag := range testTags2.Tags {
   687  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   688  	}
   689  	for _, dp := range testDatapoints2 {
   690  		mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation2)
   691  	}
   692  	downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil)
   693  
   694  	mockMetricsAppender.EXPECT().NextMetric().Times(2)
   695  	mockMetricsAppender.EXPECT().Finalize()
   696  
   697  	for _, dp := range testEntries[0].datapoints {
   698  		session.EXPECT().WriteTagged(
   699  			gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), testEntries[0].annotation,
   700  		)
   701  	}
   702  
   703  	for _, dp := range testEntries[1].datapoints {
   704  		session.EXPECT().WriteTagged(
   705  			gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), testEntries[1].annotation,
   706  		)
   707  	}
   708  
   709  	iter := newTestIter(testEntries)
   710  	err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{})
   711  	require.NoError(t, err)
   712  }
   713  
   714  func TestDownsampleAndWriteBatchNoDownsampler(t *testing.T) {
   715  	ctrl := gomock.NewController(t)
   716  	defer ctrl.Finish()
   717  
   718  	downAndWrite, _, session := newTestDownsamplerAndWriterWithEnabled(t, ctrl, false,
   719  		testDownsamplerAndWriterOptions{})
   720  
   721  	for _, entry := range testEntries {
   722  		for _, dp := range entry.datapoints {
   723  			session.EXPECT().WriteTagged(
   724  				gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation,
   725  			)
   726  		}
   727  	}
   728  
   729  	iter := newTestIter(testEntries)
   730  	err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{})
   731  	require.NoError(t, err)
   732  }
   733  
   734  func TestDownsampleAndWriteBatchOverrideDownsampleRules(t *testing.T) {
   735  	ctrl := gomock.NewController(t)
   736  	defer ctrl.Finish()
   737  
   738  	downAndWrite, downsampler, _ := newTestDownsamplerAndWriter(t, ctrl,
   739  		testDownsamplerAndWriterOptions{})
   740  
   741  	var (
   742  		mockSamplesAppender  = downsample.NewMockSamplesAppender(ctrl)
   743  		mockMetricsAppender  = downsample.NewMockMetricsAppender(ctrl)
   744  		overrideMappingRules = []downsample.AutoMappingRule{
   745  			{
   746  				Aggregations: []aggregation.Type{
   747  					aggregation.Sum,
   748  				},
   749  				Policies: policy.StoragePolicies{
   750  					policy.MustParseStoragePolicy("1h:30d"),
   751  				},
   752  			},
   753  		}
   754  	)
   755  
   756  	mockMetricsAppender.
   757  		EXPECT().
   758  		SamplesAppender(downsample.SampleAppenderOptions{
   759  			Override: true,
   760  			OverrideRules: downsample.SamplesAppenderOverrideRules{
   761  				MappingRules: overrideMappingRules,
   762  			},
   763  		}).
   764  		Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil)
   765  
   766  	entries := testEntries[:1]
   767  	for _, entry := range entries {
   768  		for _, tag := range entry.tags.Tags {
   769  			mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   770  		}
   771  		// We will also get the common gauge tag.
   772  		for _, dp := range entry.datapoints {
   773  			mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1)
   774  		}
   775  	}
   776  	downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil)
   777  
   778  	mockMetricsAppender.EXPECT().NextMetric()
   779  	mockMetricsAppender.EXPECT().Finalize()
   780  
   781  	iter := newTestIter(entries)
   782  	err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{
   783  		DownsampleOverride:     true,
   784  		DownsampleMappingRules: overrideMappingRules,
   785  		WriteOverride:          true,
   786  		WriteStoragePolicies:   nil,
   787  	})
   788  	require.NoError(t, err)
   789  }
   790  
   791  func TestDownsampleAndWriteBatchOverrideStoragePolicies(t *testing.T) {
   792  	ctrl := gomock.NewController(t)
   793  	defer ctrl.Finish()
   794  
   795  	testOpts := testDownsamplerAndWriterOptions{
   796  		aggregatedNamespaces: []m3.AggregatedClusterNamespaceDefinition{
   797  			{
   798  				NamespaceID: ident.StringID("namespace_10m_7d"),
   799  				Resolution:  10 * time.Minute,
   800  				Retention:   7 * 24 * time.Hour,
   801  			},
   802  			{
   803  				NamespaceID: ident.StringID("namespace_1h_60d"),
   804  				Resolution:  time.Hour,
   805  				Retention:   60 * 24 * time.Hour,
   806  			},
   807  		},
   808  	}
   809  	downAndWrite, _, session := newTestDownsamplerAndWriter(t, ctrl, testOpts)
   810  
   811  	entries := testEntries[:1]
   812  	for _, namespace := range testOpts.aggregatedNamespaces {
   813  		for _, entry := range entries {
   814  			for _, dp := range entry.datapoints {
   815  				namespaceMatcher := ident.NewIDMatcher(namespace.NamespaceID.String())
   816  				session.EXPECT().WriteTagged(
   817  					namespaceMatcher, gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation,
   818  				)
   819  			}
   820  		}
   821  	}
   822  
   823  	iter := newTestIter(entries)
   824  	err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{
   825  		DownsampleOverride:     true,
   826  		DownsampleMappingRules: nil,
   827  		WriteOverride:          true,
   828  		WriteStoragePolicies: policy.StoragePolicies{
   829  			policy.MustParseStoragePolicy("10m:7d"),
   830  			policy.MustParseStoragePolicy("1h:60d"),
   831  		},
   832  	})
   833  	require.NoError(t, err)
   834  }
   835  
   836  func expectDefaultDownsampling(
   837  	ctrl *gomock.Controller, datapoints []ts.Datapoint,
   838  	downsampler *downsample.MockDownsampler, downsampleOpts downsample.SampleAppenderOptions) {
   839  	var (
   840  		mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl)
   841  		mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl)
   842  	)
   843  
   844  	mockMetricsAppender.
   845  		EXPECT().
   846  		SamplesAppender(downsampleOpts).
   847  		Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil)
   848  	for _, tag := range testTags1.Tags {
   849  		mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value)
   850  	}
   851  
   852  	for _, dp := range datapoints {
   853  		mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1)
   854  	}
   855  	downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil)
   856  
   857  	mockMetricsAppender.EXPECT().Finalize()
   858  }
   859  
   860  func expectDefaultStorageWrites(session *client.MockSession, datapoints []ts.Datapoint, annotation []byte) {
   861  	for _, dp := range datapoints {
   862  		session.EXPECT().WriteTagged(
   863  			gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), annotation)
   864  	}
   865  }
   866  
   867  type testDownsamplerAndWriterOptions struct {
   868  	aggregatedNamespaces []m3.AggregatedClusterNamespaceDefinition
   869  }
   870  
   871  func newTestDownsamplerAndWriter(
   872  	t *testing.T,
   873  	ctrl *gomock.Controller,
   874  	opts testDownsamplerAndWriterOptions,
   875  ) (*downsamplerAndWriter, *downsample.MockDownsampler, *client.MockSession) {
   876  	return newTestDownsamplerAndWriterWithEnabled(t, ctrl, true, opts)
   877  }
   878  
   879  func newTestDownsamplerAndWriterWithEnabled(
   880  	t *testing.T,
   881  	ctrl *gomock.Controller,
   882  	enabled bool,
   883  	opts testDownsamplerAndWriterOptions,
   884  ) (*downsamplerAndWriter, *downsample.MockDownsampler, *client.MockSession) {
   885  	var (
   886  		storage storage.Storage
   887  		session *client.MockSession
   888  	)
   889  	if ns := opts.aggregatedNamespaces; len(ns) > 0 {
   890  		storage, session = testm3.NewStorageAndSessionWithAggregatedNamespaces(t, ctrl, ns)
   891  	} else {
   892  		storage, session = testm3.NewStorageAndSession(t, ctrl)
   893  	}
   894  	downsampler := downsample.NewMockDownsampler(ctrl)
   895  	downsampler.EXPECT().Enabled().Return(enabled)
   896  	return NewDownsamplerAndWriter(storage, downsampler, testWorkerPool,
   897  		instrument.NewOptions()).(*downsamplerAndWriter), downsampler, session
   898  }
   899  
   900  func newTestDownsamplerAndWriterWithAggregatedNamespace(
   901  	t *testing.T,
   902  	ctrl *gomock.Controller,
   903  	aggregatedNamespaces []m3.AggregatedClusterNamespaceDefinition,
   904  ) (*downsamplerAndWriter, *downsample.MockDownsampler, *client.MockSession) {
   905  	storage, session := testm3.NewStorageAndSessionWithAggregatedNamespaces(
   906  		t, ctrl, aggregatedNamespaces)
   907  	downsampler := downsample.NewMockDownsampler(ctrl)
   908  	downsampler.EXPECT().Enabled().Return(true)
   909  	return NewDownsamplerAndWriter(storage, downsampler, testWorkerPool,
   910  		instrument.NewOptions()).(*downsamplerAndWriter), downsampler, session
   911  }
   912  
   913  func init() {
   914  	var err error
   915  	testWorkerPool, err = xsync.NewPooledWorkerPool(
   916  		16,
   917  		xsync.NewPooledWorkerPoolOptions().
   918  			SetGrowOnDemand(true),
   919  	)
   920  
   921  	if err != nil {
   922  		panic(fmt.Sprintf("unable to create pooled worker pool: %v", err))
   923  	}
   924  
   925  	testWorkerPool.Init()
   926  }