github.com/m3db/m3@v1.5.0/src/metrics/rules/ruleset_test.go (about)

     1  // Copyright (c) 2017 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 rules
    22  
    23  import (
    24  	"bytes"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/metrics/aggregation"
    30  	merrors "github.com/m3db/m3/src/metrics/errors"
    31  	"github.com/m3db/m3/src/metrics/filters"
    32  	"github.com/m3db/m3/src/metrics/generated/proto/aggregationpb"
    33  	"github.com/m3db/m3/src/metrics/generated/proto/metricpb"
    34  	"github.com/m3db/m3/src/metrics/generated/proto/pipelinepb"
    35  	"github.com/m3db/m3/src/metrics/generated/proto/policypb"
    36  	"github.com/m3db/m3/src/metrics/generated/proto/rulepb"
    37  	"github.com/m3db/m3/src/metrics/matcher/namespace"
    38  	"github.com/m3db/m3/src/metrics/metadata"
    39  	"github.com/m3db/m3/src/metrics/metric"
    40  	"github.com/m3db/m3/src/metrics/metric/id"
    41  	"github.com/m3db/m3/src/metrics/pipeline"
    42  	"github.com/m3db/m3/src/metrics/policy"
    43  	"github.com/m3db/m3/src/metrics/rules/view"
    44  	"github.com/m3db/m3/src/metrics/rules/view/changes"
    45  	xbytes "github.com/m3db/m3/src/metrics/x/bytes"
    46  	"github.com/m3db/m3/src/query/models"
    47  	xerrors "github.com/m3db/m3/src/x/errors"
    48  	xtime "github.com/m3db/m3/src/x/time"
    49  
    50  	"github.com/google/go-cmp/cmp"
    51  	"github.com/google/go-cmp/cmp/cmpopts"
    52  	"github.com/stretchr/testify/require"
    53  )
    54  
    55  var (
    56  	testUser                 = "test_user"
    57  	testActiveRuleSetCmpOpts = []cmp.Option{
    58  		cmp.AllowUnexported(activeRuleSet{}),
    59  		cmp.AllowUnexported(mappingRule{}),
    60  		cmp.AllowUnexported(mappingRuleSnapshot{}),
    61  		cmp.AllowUnexported(rollupRule{}),
    62  		cmp.AllowUnexported(rollupRuleSnapshot{}),
    63  		cmpopts.IgnoreTypes(
    64  			activeRuleSet{}.tagsFilterOpts,
    65  			activeRuleSet{}.newRollupIDFn,
    66  			activeRuleSet{}.isRollupIDFn,
    67  		),
    68  		cmpopts.IgnoreInterfaces(struct{ filters.TagsFilter }{}),
    69  		cmpopts.IgnoreInterfaces(struct{ aggregation.TypesOptions }{}),
    70  	}
    71  	testRuleSetCmpOpts = []cmp.Option{
    72  		cmp.AllowUnexported(ruleSet{}),
    73  		cmp.AllowUnexported(mappingRule{}),
    74  		cmp.AllowUnexported(mappingRuleSnapshot{}),
    75  		cmp.AllowUnexported(rollupRule{}),
    76  		cmp.AllowUnexported(rollupRuleSnapshot{}),
    77  		cmpopts.IgnoreTypes(
    78  			ruleSet{}.tagsFilterOpts,
    79  			ruleSet{}.newRollupIDFn,
    80  			ruleSet{}.isRollupIDFn,
    81  		),
    82  		cmpopts.IgnoreInterfaces(struct{ filters.TagsFilter }{}),
    83  		cmpopts.IgnoreInterfaces(struct{ aggregation.TypesOptions }{}),
    84  	}
    85  )
    86  
    87  func TestRuleSetProperties(t *testing.T) {
    88  	opts := testRuleSetOptions()
    89  	version := 1
    90  	rs := &rulepb.RuleSet{
    91  		Uuid:               "ruleset",
    92  		Namespace:          "namespace",
    93  		CreatedAtNanos:     1234,
    94  		LastUpdatedAtNanos: 5678,
    95  		Tombstoned:         false,
    96  		CutoverNanos:       34923,
    97  	}
    98  	newRuleSet, err := NewRuleSetFromProto(version, rs, opts)
    99  	require.NoError(t, err)
   100  	ruleSet := newRuleSet.(*ruleSet)
   101  
   102  	require.Equal(t, "ruleset", ruleSet.uuid)
   103  	require.Equal(t, []byte("namespace"), ruleSet.Namespace())
   104  	require.Equal(t, 1, ruleSet.Version())
   105  	require.Equal(t, int64(34923), ruleSet.CutoverNanos())
   106  	require.Equal(t, false, ruleSet.Tombstoned())
   107  }
   108  
   109  func TestRuleSetActiveSet(t *testing.T) {
   110  	var (
   111  		version = 1
   112  		proto   = testRuleSetProto()
   113  		opts    = testRuleSetOptions()
   114  	)
   115  	res, err := NewRuleSetFromProto(version, proto, opts)
   116  	require.NoError(t, err)
   117  	rs := res.(*ruleSet)
   118  
   119  	inputs := []struct {
   120  		activeSetTimeNanos   int64
   121  		expectedMappingRules []*mappingRule
   122  		expectedRollupRules  []*rollupRule
   123  	}{
   124  		{
   125  			activeSetTimeNanos:   0,
   126  			expectedMappingRules: rs.mappingRules,
   127  			expectedRollupRules:  rs.rollupRules,
   128  		},
   129  		{
   130  			activeSetTimeNanos: 30000,
   131  			expectedMappingRules: []*mappingRule{
   132  				&mappingRule{
   133  					uuid:      rs.mappingRules[0].uuid,
   134  					snapshots: rs.mappingRules[0].snapshots[2:],
   135  				},
   136  				&mappingRule{
   137  					uuid:      rs.mappingRules[1].uuid,
   138  					snapshots: rs.mappingRules[1].snapshots[1:],
   139  				},
   140  				rs.mappingRules[2],
   141  				rs.mappingRules[3],
   142  				rs.mappingRules[4],
   143  			},
   144  			expectedRollupRules: []*rollupRule{
   145  				&rollupRule{
   146  					uuid:      rs.rollupRules[0].uuid,
   147  					snapshots: rs.rollupRules[0].snapshots[2:],
   148  				},
   149  				&rollupRule{
   150  					uuid:      rs.rollupRules[1].uuid,
   151  					snapshots: rs.rollupRules[1].snapshots[1:],
   152  				},
   153  				rs.rollupRules[2],
   154  				rs.rollupRules[3],
   155  				rs.rollupRules[4],
   156  				rs.rollupRules[5],
   157  			},
   158  		},
   159  		{
   160  			activeSetTimeNanos: 200000,
   161  			expectedMappingRules: []*mappingRule{
   162  				&mappingRule{
   163  					uuid:      rs.mappingRules[0].uuid,
   164  					snapshots: rs.mappingRules[0].snapshots[2:],
   165  				},
   166  				&mappingRule{
   167  					uuid:      rs.mappingRules[1].uuid,
   168  					snapshots: rs.mappingRules[1].snapshots[2:],
   169  				},
   170  				&mappingRule{
   171  					uuid:      rs.mappingRules[2].uuid,
   172  					snapshots: rs.mappingRules[2].snapshots[1:],
   173  				},
   174  				rs.mappingRules[3],
   175  				rs.mappingRules[4],
   176  			},
   177  			expectedRollupRules: []*rollupRule{
   178  				&rollupRule{
   179  					uuid:      rs.rollupRules[0].uuid,
   180  					snapshots: rs.rollupRules[0].snapshots[2:],
   181  				},
   182  				&rollupRule{
   183  					uuid:      rs.rollupRules[1].uuid,
   184  					snapshots: rs.rollupRules[1].snapshots[2:],
   185  				},
   186  				&rollupRule{
   187  					uuid:      rs.rollupRules[2].uuid,
   188  					snapshots: rs.rollupRules[2].snapshots[1:],
   189  				},
   190  				rs.rollupRules[3],
   191  				rs.rollupRules[4],
   192  				rs.rollupRules[5],
   193  			},
   194  		},
   195  	}
   196  
   197  	for _, input := range inputs {
   198  		as := rs.ActiveSet(input.activeSetTimeNanos).(*activeRuleSet)
   199  		expected := newActiveRuleSet(
   200  			version,
   201  			input.expectedMappingRules,
   202  			input.expectedRollupRules,
   203  			rs.tagsFilterOpts,
   204  			rs.newRollupIDFn,
   205  			rs.isRollupIDFn,
   206  		)
   207  		require.True(t, cmp.Equal(expected, as, testActiveRuleSetCmpOpts...))
   208  	}
   209  }
   210  
   211  func TestNewRuleSetFromProtoToProtoRoundtrip(t *testing.T) {
   212  	var (
   213  		version = 1
   214  		proto   = testRuleSetProto()
   215  		opts    = testRuleSetOptions()
   216  	)
   217  	rs, err := NewRuleSetFromProto(version, proto, opts)
   218  	require.NoError(t, err)
   219  	res, err := rs.Proto()
   220  	require.NoError(t, err)
   221  	require.Equal(t, proto.MappingRules[0].Snapshots[0], res.MappingRules[0].Snapshots[0])
   222  	require.Equal(t, proto, res)
   223  }
   224  
   225  func TestRuleSetMappingRules(t *testing.T) {
   226  	var (
   227  		version = 1
   228  		proto   = testRuleSetProto()
   229  		opts    = testRuleSetOptions()
   230  	)
   231  	res, err := NewRuleSetFromProto(version, proto, opts)
   232  	require.NoError(t, err)
   233  	rs := res.(*ruleSet)
   234  
   235  	mr, err := rs.MappingRules()
   236  	require.NoError(t, err)
   237  	require.True(t, len(mr) > 0)
   238  	for _, m := range rs.mappingRules {
   239  		require.Contains(t, mr, m.uuid)
   240  		mrv, err := m.mappingRuleView(len(m.snapshots) - 1)
   241  		require.NoError(t, err)
   242  		require.Equal(t, mr[m.uuid][0], mrv)
   243  	}
   244  }
   245  
   246  func TestRuleSetRollupRules(t *testing.T) {
   247  	var (
   248  		version = 1
   249  		proto   = testRuleSetProto()
   250  		opts    = testRuleSetOptions()
   251  	)
   252  	res, err := NewRuleSetFromProto(version, proto, opts)
   253  	require.NoError(t, err)
   254  	rs := res.(*ruleSet)
   255  
   256  	rr, err := rs.RollupRules()
   257  	require.NoError(t, err)
   258  	require.True(t, len(rr) > 0)
   259  	for _, r := range rs.rollupRules {
   260  		require.Contains(t, rr, r.uuid)
   261  		rrv, err := r.rollupRuleView(len(r.snapshots) - 1)
   262  		require.NoError(t, err)
   263  		require.Equal(t, rr[r.uuid][0], rrv)
   264  	}
   265  }
   266  
   267  func TestRuleSetLatest(t *testing.T) {
   268  	proto := &rulepb.RuleSet{
   269  		Namespace:    "testNamespace",
   270  		CutoverNanos: 998234000000,
   271  		MappingRules: testMappingRulesConfig(),
   272  		RollupRules:  testRollupRulesConfig(),
   273  	}
   274  	rs, err := NewRuleSetFromProto(123, proto, testRuleSetOptions())
   275  	require.NoError(t, err)
   276  	latest, err := rs.Latest()
   277  	require.NoError(t, err)
   278  
   279  	r1, err := pipeline.NewRollupOp(
   280  		pipeline.GroupByRollupType,
   281  		"rName1",
   282  		[]string{"rtagName1", "rtagName2"},
   283  		aggregation.DefaultID,
   284  	)
   285  	require.NoError(t, err)
   286  	r3, err := pipeline.NewRollupOp(
   287  		pipeline.GroupByRollupType,
   288  		"rName3",
   289  		[]string{"rtagName1", "rtagName2"},
   290  		aggregation.DefaultID,
   291  	)
   292  	require.NoError(t, err)
   293  	r4, err := pipeline.NewRollupOp(
   294  		pipeline.GroupByRollupType,
   295  		"rName4",
   296  		[]string{"rtagName1", "rtagName2"},
   297  		aggregation.DefaultID,
   298  	)
   299  	require.NoError(t, err)
   300  	r5, err := pipeline.NewRollupOp(
   301  		pipeline.GroupByRollupType,
   302  		"rName5",
   303  		[]string{"rtagName1"},
   304  		aggregation.DefaultID,
   305  	)
   306  	require.NoError(t, err)
   307  	r6, err := pipeline.NewRollupOp(
   308  		pipeline.GroupByRollupType,
   309  		"rName6",
   310  		[]string{"rtagName1", "rtagName2"},
   311  		aggregation.DefaultID,
   312  	)
   313  	require.NoError(t, err)
   314  
   315  	expected := view.RuleSet{
   316  		Namespace:     "testNamespace",
   317  		Version:       123,
   318  		CutoverMillis: 998234,
   319  		MappingRules: []view.MappingRule{
   320  			{
   321  				ID:            "mappingRule1",
   322  				Name:          "mappingRule1.snapshot3",
   323  				Tombstoned:    false,
   324  				Filter:        "mtagName1:mtagValue1",
   325  				AggregationID: aggregation.DefaultID,
   326  				StoragePolicies: policy.StoragePolicies{
   327  					policy.NewStoragePolicy(30*time.Second, xtime.Second, 6*time.Hour),
   328  				},
   329  				Tags: []models.Tag{},
   330  			},
   331  			{
   332  				ID:            "mappingRule3",
   333  				Name:          "mappingRule3.snapshot2",
   334  				Tombstoned:    false,
   335  				Filter:        "mtagName1:mtagValue1",
   336  				AggregationID: aggregation.DefaultID,
   337  				StoragePolicies: policy.StoragePolicies{
   338  					policy.NewStoragePolicy(10*time.Second, xtime.Second, 2*time.Hour),
   339  					policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   340  				},
   341  				Tags: []models.Tag{},
   342  			},
   343  			{
   344  				ID:            "mappingRule4",
   345  				Name:          "mappingRule4.snapshot1",
   346  				Tombstoned:    false,
   347  				Filter:        "mtagName1:mtagValue2",
   348  				AggregationID: aggregation.MustCompressTypes(aggregation.P999),
   349  				StoragePolicies: policy.StoragePolicies{
   350  					policy.NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour),
   351  				},
   352  				Tags: []models.Tag{},
   353  			},
   354  			{
   355  				ID:            "mappingRule5",
   356  				Name:          "mappingRule5.snapshot1",
   357  				Tombstoned:    false,
   358  				LastUpdatedBy: "test",
   359  				Filter:        "mtagName1:mtagValue1",
   360  				AggregationID: aggregation.DefaultID,
   361  				StoragePolicies: policy.StoragePolicies{
   362  					policy.NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour),
   363  				},
   364  				Tags: []models.Tag{},
   365  			},
   366  		},
   367  		RollupRules: []view.RollupRule{
   368  			{
   369  				ID:         "rollupRule1",
   370  				Name:       "rollupRule1.snapshot3",
   371  				Tombstoned: false,
   372  				Filter:     "rtagName1:rtagValue1 rtagName2:rtagValue2",
   373  				Tags:       []models.Tag{},
   374  				Targets: []view.RollupTarget{
   375  					{
   376  						Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   377  							{
   378  								Type:   pipeline.RollupOpType,
   379  								Rollup: r1,
   380  							},
   381  						}),
   382  						StoragePolicies: policy.StoragePolicies{
   383  							policy.NewStoragePolicy(30*time.Second, xtime.Second, 6*time.Hour),
   384  						},
   385  					},
   386  				},
   387  			},
   388  			{
   389  				ID:         "rollupRule3",
   390  				Name:       "rollupRule3.snapshot2",
   391  				Tombstoned: false,
   392  				Filter:     "rtagName1:rtagValue1 rtagName2:rtagValue2",
   393  				Tags:       []models.Tag{},
   394  				Targets: []view.RollupTarget{
   395  					{
   396  						Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   397  							{
   398  								Type:   pipeline.RollupOpType,
   399  								Rollup: r3,
   400  							},
   401  						}),
   402  						StoragePolicies: policy.StoragePolicies{
   403  							policy.NewStoragePolicy(10*time.Second, xtime.Second, 2*time.Hour),
   404  							policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   405  						},
   406  					},
   407  				},
   408  			},
   409  			{
   410  				ID:         "rollupRule4",
   411  				Name:       "rollupRule4.snapshot1",
   412  				Tombstoned: false,
   413  				Filter:     "rtagName1:rtagValue2",
   414  				Tags:       []models.Tag{},
   415  				Targets: []view.RollupTarget{
   416  					{
   417  						Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   418  							{
   419  								Type:   pipeline.RollupOpType,
   420  								Rollup: r4,
   421  							},
   422  						}),
   423  						StoragePolicies: policy.StoragePolicies{
   424  							policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   425  						},
   426  					},
   427  				},
   428  			},
   429  			{
   430  				ID:         "rollupRule5",
   431  				Name:       "rollupRule5.snapshot1",
   432  				Tombstoned: false,
   433  				Filter:     "rtagName1:rtagValue2",
   434  				Tags:       []models.Tag{},
   435  				Targets: []view.RollupTarget{
   436  					{
   437  						Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   438  							{
   439  								Type:   pipeline.RollupOpType,
   440  								Rollup: r5,
   441  							},
   442  						}),
   443  						StoragePolicies: policy.StoragePolicies{
   444  							policy.NewStoragePolicy(time.Second, xtime.Second, time.Minute),
   445  						},
   446  					},
   447  				},
   448  			},
   449  			{
   450  				ID:         "rollupRule6",
   451  				Name:       "rollupRule6.snapshot1",
   452  				Tombstoned: false,
   453  				Filter:     "rtagName1:rtagValue1 rtagName2:rtagValue2",
   454  				Tags:       []models.Tag{},
   455  				Targets: []view.RollupTarget{
   456  					{
   457  						Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   458  							{
   459  								Type:   pipeline.RollupOpType,
   460  								Rollup: r6,
   461  							},
   462  						}),
   463  						StoragePolicies: policy.StoragePolicies{
   464  							policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   465  						},
   466  					},
   467  				},
   468  			},
   469  		},
   470  	}
   471  	require.Equal(t, expected, latest)
   472  }
   473  
   474  func TestRuleSetClone(t *testing.T) {
   475  	var (
   476  		version = 1
   477  		proto   = testRuleSetProto()
   478  		opts    = testRuleSetOptions()
   479  	)
   480  	res, err := NewRuleSetFromProto(version, proto, opts)
   481  	require.NoError(t, err)
   482  	rs := res.(*ruleSet)
   483  
   484  	rsClone := rs.Clone().(*ruleSet)
   485  	require.True(t, cmp.Equal(rs, rsClone, testRuleSetCmpOpts...), cmp.Diff(rs, rsClone, testRuleSetCmpOpts...))
   486  	for i, m := range rs.mappingRules {
   487  		require.False(t, m == rsClone.mappingRules[i])
   488  	}
   489  	for i, r := range rs.rollupRules {
   490  		require.False(t, r == rsClone.rollupRules[i])
   491  	}
   492  
   493  	rsClone.mappingRules = []*mappingRule{}
   494  	rsClone.rollupRules = []*rollupRule{}
   495  	require.NotEqual(t, rs.mappingRules, rsClone.mappingRules)
   496  	require.NotEqual(t, rs.rollupRules, rsClone.rollupRules)
   497  }
   498  
   499  func TestRuleSetAddMappingRuleInvalidFilter(t *testing.T) {
   500  	var (
   501  		version = 1
   502  		proto   = testRuleSetProto()
   503  		opts    = testRuleSetOptions()
   504  	)
   505  	res, err := NewRuleSetFromProto(version, proto, opts)
   506  	require.NoError(t, err)
   507  	rs := res.(*ruleSet)
   508  
   509  	view := view.MappingRule{
   510  		Name:   "testInvalidFilter",
   511  		Filter: "tag1:value1 tag2:abc[def",
   512  		StoragePolicies: policy.StoragePolicies{
   513  			policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   514  		},
   515  	}
   516  	helper := NewRuleSetUpdateHelper(10)
   517  	newID, err := rs.AddMappingRule(view, helper.NewUpdateMetadata(time.Now().UnixNano(), testUser))
   518  	require.Error(t, err)
   519  	require.Empty(t, newID)
   520  	require.True(t, strings.Contains(err.Error(), "cannot add rule testInvalidFilter:"))
   521  	_, ok := xerrors.InnerError(err).(merrors.ValidationError)
   522  	require.True(t, ok)
   523  }
   524  
   525  func TestRuleSetAddMappingRuleNewRule(t *testing.T) {
   526  	var (
   527  		version = 1
   528  		proto   = testRuleSetProto()
   529  		opts    = testRuleSetOptions()
   530  	)
   531  	res, err := NewRuleSetFromProto(version, proto, opts)
   532  	require.NoError(t, err)
   533  	rs := res.(*ruleSet)
   534  
   535  	_, err = rs.getMappingRuleByName("foo")
   536  	require.Equal(t, errRuleNotFound, err)
   537  
   538  	view := view.MappingRule{
   539  		Name:   "foo",
   540  		Filter: "tag1:value tag2:value",
   541  		StoragePolicies: policy.StoragePolicies{
   542  			policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   543  		},
   544  	}
   545  	nowNanos := time.Now().UnixNano()
   546  	helper := NewRuleSetUpdateHelper(10)
   547  	newID, err := rs.AddMappingRule(view, helper.NewUpdateMetadata(nowNanos, testUser))
   548  	require.NoError(t, err)
   549  	mrs, err := rs.MappingRules()
   550  	require.NoError(t, err)
   551  	require.Contains(t, mrs, newID)
   552  
   553  	mr, err := rs.getMappingRuleByName("foo")
   554  	require.NoError(t, err)
   555  
   556  	expected := &mappingRuleSnapshot{
   557  		name:          "foo",
   558  		tombstoned:    false,
   559  		cutoverNanos:  nowNanos + 10,
   560  		rawFilter:     "tag1:value tag2:value",
   561  		aggregationID: aggregation.DefaultID,
   562  		storagePolicies: policy.StoragePolicies{
   563  			policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   564  		},
   565  		lastUpdatedBy:      testUser,
   566  		lastUpdatedAtNanos: nowNanos,
   567  	}
   568  	require.True(t, cmp.Equal(expected, mr.snapshots[len(mr.snapshots)-1], testMappingRuleSnapshotCmpOpts...))
   569  
   570  	require.Equal(t, nowNanos+10, rs.cutoverNanos)
   571  	require.Equal(t, testUser, rs.lastUpdatedBy)
   572  	require.Equal(t, nowNanos, rs.lastUpdatedAtNanos)
   573  }
   574  
   575  func TestRuleSetAddMappingRuleDuplicateRule(t *testing.T) {
   576  	var (
   577  		version = 1
   578  		proto   = testRuleSetProto()
   579  		opts    = testRuleSetOptions()
   580  	)
   581  	res, err := NewRuleSetFromProto(version, proto, opts)
   582  	require.NoError(t, err)
   583  	rs := res.(*ruleSet)
   584  
   585  	mr, err := rs.getMappingRuleByName("mappingRule5.snapshot1")
   586  	require.NoError(t, err)
   587  	require.NotNil(t, mr)
   588  
   589  	view := view.MappingRule{
   590  		Name:   "mappingRule5.snapshot1",
   591  		Filter: "tag1:value tag2:value",
   592  		StoragePolicies: policy.StoragePolicies{
   593  			policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   594  		},
   595  	}
   596  	nowNanos := time.Now().UnixNano()
   597  	helper := NewRuleSetUpdateHelper(10)
   598  	newID, err := rs.AddMappingRule(view, helper.NewUpdateMetadata(nowNanos, testUser))
   599  	require.Error(t, err)
   600  	require.Empty(t, newID)
   601  	err = xerrors.InnerError(err)
   602  	require.NotNil(t, err)
   603  	_, ok := err.(merrors.InvalidInputError) //nolint:errorlint
   604  	require.True(t, ok)
   605  }
   606  
   607  func TestRuleSetAddMappingRuleReviveRule(t *testing.T) {
   608  	var (
   609  		version = 1
   610  		proto   = testRuleSetProto()
   611  		opts    = testRuleSetOptions()
   612  	)
   613  	res, err := NewRuleSetFromProto(version, proto, opts)
   614  	require.NoError(t, err)
   615  	rs := res.(*ruleSet)
   616  
   617  	mr, err := rs.getMappingRuleByName("mappingRule2.snapshot3")
   618  	require.NoError(t, err)
   619  	require.NotNil(t, mr)
   620  
   621  	view := view.MappingRule{
   622  		Name:          "mappingRule2.snapshot3",
   623  		Filter:        "test:bar",
   624  		AggregationID: aggregation.MustCompressTypes(aggregation.Sum),
   625  		StoragePolicies: policy.StoragePolicies{
   626  			policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   627  		},
   628  	}
   629  	nowNanos := time.Now().UnixNano()
   630  	helper := NewRuleSetUpdateHelper(10)
   631  	newID, err := rs.AddMappingRule(view, helper.NewUpdateMetadata(nowNanos, testUser))
   632  	require.NoError(t, err)
   633  	mrs, err := rs.MappingRules()
   634  	require.NoError(t, err)
   635  	require.Contains(t, mrs, newID)
   636  
   637  	mr, err = rs.getMappingRuleByID(newID)
   638  	require.NoError(t, err)
   639  	require.Equal(t, mr.snapshots[len(mr.snapshots)-1].rawFilter, view.Filter)
   640  
   641  	expected := &mappingRuleSnapshot{
   642  		name:          "mappingRule2.snapshot3",
   643  		tombstoned:    false,
   644  		cutoverNanos:  nowNanos + 10,
   645  		rawFilter:     "test:bar",
   646  		aggregationID: aggregation.MustCompressTypes(aggregation.Sum),
   647  		storagePolicies: policy.StoragePolicies{
   648  			policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   649  		},
   650  		lastUpdatedBy:      testUser,
   651  		lastUpdatedAtNanos: nowNanos,
   652  	}
   653  	require.True(t, cmp.Equal(expected, mr.snapshots[len(mr.snapshots)-1], testMappingRuleSnapshotCmpOpts...))
   654  
   655  	require.Equal(t, nowNanos+10, rs.cutoverNanos)
   656  	require.Equal(t, testUser, rs.lastUpdatedBy)
   657  	require.Equal(t, nowNanos, rs.lastUpdatedAtNanos)
   658  }
   659  
   660  func TestRuleSetUpdateMappingRule(t *testing.T) {
   661  	var (
   662  		version = 1
   663  		proto   = testRuleSetProto()
   664  		opts    = testRuleSetOptions()
   665  	)
   666  	res, err := NewRuleSetFromProto(version, proto, opts)
   667  	require.NoError(t, err)
   668  	rs := res.(*ruleSet)
   669  
   670  	mr, err := rs.getMappingRuleByID("mappingRule5")
   671  	require.NoError(t, err)
   672  
   673  	mrs, err := rs.MappingRules()
   674  	require.NoError(t, err)
   675  	require.Contains(t, mrs, "mappingRule5")
   676  
   677  	view := view.MappingRule{
   678  		ID:     "mappingRule5",
   679  		Name:   "mappingRule5.snapshot2",
   680  		Filter: "tag3:value",
   681  		StoragePolicies: policy.StoragePolicies{
   682  			policy.NewStoragePolicy(time.Second, xtime.Second, time.Hour),
   683  		},
   684  	}
   685  	nowNanos := time.Now().UnixNano()
   686  	helper := NewRuleSetUpdateHelper(10)
   687  	err = rs.UpdateMappingRule(view, helper.NewUpdateMetadata(nowNanos, testUser))
   688  	require.NoError(t, err)
   689  
   690  	r, err := rs.getMappingRuleByID(mr.uuid)
   691  	require.NoError(t, err)
   692  
   693  	mrs, err = rs.MappingRules()
   694  	require.NoError(t, err)
   695  	require.Contains(t, mrs, r.uuid)
   696  
   697  	expected := &mappingRuleSnapshot{
   698  		name:          "mappingRule5.snapshot2",
   699  		tombstoned:    false,
   700  		cutoverNanos:  nowNanos + 10,
   701  		rawFilter:     "tag3:value",
   702  		aggregationID: aggregation.DefaultID,
   703  		storagePolicies: policy.StoragePolicies{
   704  			policy.NewStoragePolicy(time.Second, xtime.Second, time.Hour),
   705  		},
   706  		lastUpdatedBy:      testUser,
   707  		lastUpdatedAtNanos: nowNanos,
   708  	}
   709  	require.True(t, cmp.Equal(expected, r.snapshots[len(mr.snapshots)-1], testMappingRuleSnapshotCmpOpts...))
   710  
   711  	require.Equal(t, nowNanos+10, rs.cutoverNanos)
   712  	require.Equal(t, testUser, rs.lastUpdatedBy)
   713  	require.Equal(t, nowNanos, rs.lastUpdatedAtNanos)
   714  }
   715  
   716  func TestRuleSetDeleteMappingRule(t *testing.T) {
   717  	var (
   718  		version = 1
   719  		proto   = testRuleSetProto()
   720  		opts    = testRuleSetOptions()
   721  	)
   722  	res, err := NewRuleSetFromProto(version, proto, opts)
   723  	require.NoError(t, err)
   724  	rs := res.(*ruleSet)
   725  
   726  	mrs, err := rs.MappingRules()
   727  	require.NoError(t, err)
   728  	require.Contains(t, mrs, "mappingRule5")
   729  
   730  	m, err := rs.getMappingRuleByID("mappingRule5")
   731  	require.NoError(t, err)
   732  	require.NotNil(t, m)
   733  
   734  	nowNanos := time.Now().UnixNano()
   735  	helper := NewRuleSetUpdateHelper(10)
   736  	err = rs.DeleteMappingRule("mappingRule5", helper.NewUpdateMetadata(nowNanos, testUser))
   737  	require.NoError(t, err)
   738  
   739  	m, err = rs.getMappingRuleByID("mappingRule5")
   740  	require.NoError(t, err)
   741  	require.True(t, m.tombstoned())
   742  	require.Equal(t, nowNanos+10, m.snapshots[len(m.snapshots)-1].cutoverNanos)
   743  	require.Equal(t, aggregation.DefaultID, m.snapshots[len(m.snapshots)-1].aggregationID)
   744  	require.Nil(t, m.snapshots[len(m.snapshots)-1].storagePolicies)
   745  
   746  	mrs, err = rs.MappingRules()
   747  	require.NoError(t, err)
   748  	require.Contains(t, mrs, "mappingRule5")
   749  }
   750  
   751  func TestRuleSetAddRollupRuleNewRule(t *testing.T) {
   752  	var (
   753  		version = 1
   754  		proto   = testRuleSetProto()
   755  		opts    = testRuleSetOptions()
   756  	)
   757  	res, err := NewRuleSetFromProto(version, proto, opts)
   758  	require.NoError(t, err)
   759  	rs := res.(*ruleSet)
   760  
   761  	_, err = rs.getRollupRuleByName("foo")
   762  	require.Equal(t, errRuleNotFound, err)
   763  	r1, err := pipeline.NewRollupOp(
   764  		pipeline.GroupByRollupType,
   765  		"blah",
   766  		[]string{"a"},
   767  		aggregation.DefaultID,
   768  	)
   769  	require.NoError(t, err)
   770  
   771  	view := view.RollupRule{
   772  		Name:         "foo",
   773  		Filter:       "tag1:value tag2:value",
   774  		KeepOriginal: true,
   775  		Targets: []view.RollupTarget{
   776  			{
   777  				Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   778  					{
   779  						Type:   pipeline.RollupOpType,
   780  						Rollup: r1,
   781  					},
   782  				}),
   783  				StoragePolicies: policy.StoragePolicies{
   784  					policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   785  				},
   786  				ResendEnabled: true,
   787  			},
   788  		},
   789  	}
   790  	nowNanos := time.Now().UnixNano()
   791  	helper := NewRuleSetUpdateHelper(10)
   792  	newID, err := rs.AddRollupRule(view, helper.NewUpdateMetadata(nowNanos, testUser))
   793  	require.NoError(t, err)
   794  	rrs, err := rs.RollupRules()
   795  	require.Contains(t, rrs, newID)
   796  	require.NoError(t, err)
   797  
   798  	rr, err := rs.getRollupRuleByName("foo")
   799  	require.NoError(t, err)
   800  	require.Contains(t, rrs, rr.uuid)
   801  
   802  	expected := &rollupRuleSnapshot{
   803  		name:         "foo",
   804  		tombstoned:   false,
   805  		cutoverNanos: nowNanos + 10,
   806  		rawFilter:    "tag1:value tag2:value",
   807  		keepOriginal: true,
   808  		targets: []rollupTarget{
   809  			{
   810  				Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   811  					{
   812  						Type:   pipeline.RollupOpType,
   813  						Rollup: r1,
   814  					},
   815  				}),
   816  				StoragePolicies: policy.StoragePolicies{
   817  					policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   818  				},
   819  				ResendEnabled: true,
   820  			},
   821  		},
   822  		lastUpdatedBy:      testUser,
   823  		lastUpdatedAtNanos: nowNanos,
   824  	}
   825  	require.True(t, cmp.Equal(expected, rr.snapshots[len(rr.snapshots)-1], testRollupRuleSnapshotCmpOpts...))
   826  
   827  	require.Equal(t, nowNanos+10, rs.cutoverNanos)
   828  	require.Equal(t, testUser, rs.lastUpdatedBy)
   829  	require.Equal(t, nowNanos, rs.lastUpdatedAtNanos)
   830  }
   831  
   832  func TestRuleSetAddRollupRuleDuplicateRule(t *testing.T) {
   833  	var (
   834  		version = 1
   835  		proto   = testRuleSetProto()
   836  		opts    = testRuleSetOptions()
   837  	)
   838  	res, err := NewRuleSetFromProto(version, proto, opts)
   839  	require.NoError(t, err)
   840  	rs := res.(*ruleSet)
   841  
   842  	r, err := rs.getRollupRuleByID("rollupRule5")
   843  	require.NoError(t, err)
   844  	require.NotNil(t, r)
   845  	r1, err := pipeline.NewRollupOp(
   846  		pipeline.GroupByRollupType,
   847  		"blah",
   848  		[]string{"a"},
   849  		aggregation.DefaultID,
   850  	)
   851  	require.NoError(t, err)
   852  	view := view.RollupRule{
   853  		Name:   "rollupRule5.snapshot1",
   854  		Filter: "test:bar",
   855  		Targets: []view.RollupTarget{
   856  			{
   857  				Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   858  					{
   859  						Type:   pipeline.RollupOpType,
   860  						Rollup: r1,
   861  					},
   862  				}),
   863  				StoragePolicies: policy.StoragePolicies{
   864  					policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   865  				},
   866  			},
   867  		},
   868  	}
   869  	nowNanos := time.Now().UnixNano()
   870  	helper := NewRuleSetUpdateHelper(10)
   871  	newID, err := rs.AddRollupRule(view, helper.NewUpdateMetadata(nowNanos, testUser))
   872  	require.Error(t, err)
   873  	require.Empty(t, newID)
   874  	err = xerrors.InnerError(err)
   875  	require.NotNil(t, err)
   876  	_, ok := err.(merrors.InvalidInputError) //nolint:errorlint
   877  	require.True(t, ok)
   878  }
   879  
   880  func TestRuleSetAddRollupRuleReviveRule(t *testing.T) {
   881  	var (
   882  		version = 1
   883  		proto   = testRuleSetProto()
   884  		opts    = testRuleSetOptions()
   885  	)
   886  	res, err := NewRuleSetFromProto(version, proto, opts)
   887  	require.NoError(t, err)
   888  	rs := res.(*ruleSet)
   889  
   890  	rr, err := rs.getRollupRuleByID("rollupRule3")
   891  	require.NoError(t, err)
   892  	require.NotNil(t, rr)
   893  
   894  	view := view.RollupRule{
   895  		Name:   "rollupRule3.snapshot4",
   896  		Filter: "test:bar",
   897  		Targets: []view.RollupTarget{
   898  			{
   899  				Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   900  					{
   901  						Type:   pipeline.RollupOpType,
   902  						Rollup: rr1,
   903  					},
   904  				}),
   905  				StoragePolicies: policy.StoragePolicies{
   906  					policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   907  				},
   908  			},
   909  		},
   910  	}
   911  	nowNanos := time.Now().UnixNano()
   912  	helper := NewRuleSetUpdateHelper(10)
   913  	newID, err := rs.AddRollupRule(view, helper.NewUpdateMetadata(nowNanos, testUser))
   914  	require.NoError(t, err)
   915  	require.NotEmpty(t, newID)
   916  	rrs, err := rs.RollupRules()
   917  	require.NoError(t, err)
   918  	require.Contains(t, rrs, newID)
   919  
   920  	rr, err = rs.getRollupRuleByID(newID)
   921  	require.NoError(t, err)
   922  	require.Equal(t, rr.snapshots[len(rr.snapshots)-1].rawFilter, view.Filter)
   923  
   924  	expected := &rollupRuleSnapshot{
   925  		name:         "rollupRule3.snapshot4",
   926  		tombstoned:   false,
   927  		cutoverNanos: nowNanos + 10,
   928  		rawFilter:    "test:bar",
   929  		targets: []rollupTarget{
   930  			{
   931  				Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   932  					{
   933  						Type:   pipeline.RollupOpType,
   934  						Rollup: rr1,
   935  					},
   936  				}),
   937  				StoragePolicies: policy.StoragePolicies{
   938  					policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   939  				},
   940  			},
   941  		},
   942  		lastUpdatedBy:      testUser,
   943  		lastUpdatedAtNanos: nowNanos,
   944  	}
   945  	require.True(t, cmp.Equal(expected, rr.snapshots[len(rr.snapshots)-1], testRollupRuleSnapshotCmpOpts...))
   946  
   947  	require.Equal(t, nowNanos+10, rs.cutoverNanos)
   948  	require.Equal(t, testUser, rs.lastUpdatedBy)
   949  	require.Equal(t, nowNanos, rs.lastUpdatedAtNanos)
   950  }
   951  
   952  func TestRuleSetUpdateRollupRule(t *testing.T) {
   953  	var (
   954  		version = 1
   955  		proto   = testRuleSetProto()
   956  		opts    = testRuleSetOptions()
   957  	)
   958  	res, err := NewRuleSetFromProto(version, proto, opts)
   959  	require.NoError(t, err)
   960  	rs := res.(*ruleSet)
   961  
   962  	rr, err := rs.getRollupRuleByID("rollupRule5")
   963  	require.NoError(t, err)
   964  
   965  	rr1, err := pipeline.NewRollupOp(
   966  		pipeline.GroupByRollupType,
   967  		"blah",
   968  		[]string{"a"},
   969  		aggregation.DefaultID,
   970  	)
   971  	require.NoError(t, err)
   972  
   973  	view := view.RollupRule{
   974  		ID:           "rollupRule5",
   975  		Name:         "rollupRule5.snapshot2",
   976  		Filter:       "test:bar",
   977  		KeepOriginal: true,
   978  		Targets: []view.RollupTarget{
   979  			{
   980  				Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   981  					{
   982  						Type:   pipeline.RollupOpType,
   983  						Rollup: rr1,
   984  					},
   985  				}),
   986  				StoragePolicies: policy.StoragePolicies{
   987  					policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
   988  				},
   989  			},
   990  		},
   991  	}
   992  	nowNanos := time.Now().UnixNano()
   993  	helper := NewRuleSetUpdateHelper(10)
   994  	err = rs.UpdateRollupRule(view, helper.NewUpdateMetadata(nowNanos, testUser))
   995  	require.NoError(t, err)
   996  
   997  	r, err := rs.getRollupRuleByID(rr.uuid)
   998  	require.NoError(t, err)
   999  
  1000  	rrs, err := rs.RollupRules()
  1001  	require.NoError(t, err)
  1002  	require.Contains(t, rrs, r.uuid)
  1003  
  1004  	expected := &rollupRuleSnapshot{
  1005  		name:         "rollupRule5.snapshot2",
  1006  		tombstoned:   false,
  1007  		cutoverNanos: nowNanos + 10,
  1008  		rawFilter:    "test:bar",
  1009  		keepOriginal: true,
  1010  		targets: []rollupTarget{
  1011  			{
  1012  				Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
  1013  					{
  1014  						Type:   pipeline.RollupOpType,
  1015  						Rollup: rr1,
  1016  					},
  1017  				}),
  1018  				StoragePolicies: policy.StoragePolicies{
  1019  					policy.NewStoragePolicy(time.Minute, xtime.Minute, time.Hour),
  1020  				},
  1021  			},
  1022  		},
  1023  		lastUpdatedBy:      testUser,
  1024  		lastUpdatedAtNanos: nowNanos,
  1025  	}
  1026  	require.True(t, cmp.Equal(expected, r.snapshots[len(r.snapshots)-1], testRollupRuleSnapshotCmpOpts...))
  1027  
  1028  	require.Equal(t, nowNanos+10, rs.cutoverNanos)
  1029  	require.Equal(t, testUser, rs.lastUpdatedBy)
  1030  	require.Equal(t, nowNanos, rs.lastUpdatedAtNanos)
  1031  }
  1032  
  1033  func TestRuleSetDeleteRollupRule(t *testing.T) {
  1034  	var (
  1035  		version = 1
  1036  		proto   = testRuleSetProto()
  1037  		opts    = testRuleSetOptions()
  1038  	)
  1039  	res, err := NewRuleSetFromProto(version, proto, opts)
  1040  	require.NoError(t, err)
  1041  	rs := res.(*ruleSet)
  1042  
  1043  	rrs, err := rs.RollupRules()
  1044  	require.NoError(t, err)
  1045  	require.Contains(t, rrs, "rollupRule5")
  1046  
  1047  	rr, err := rs.getRollupRuleByID("rollupRule5")
  1048  	require.NoError(t, err)
  1049  
  1050  	nowNanos := time.Now().UnixNano()
  1051  	helper := NewRuleSetUpdateHelper(10)
  1052  	err = rs.DeleteRollupRule(rr.uuid, helper.NewUpdateMetadata(nowNanos, testUser))
  1053  	require.NoError(t, err)
  1054  
  1055  	rr, err = rs.getRollupRuleByName("rollupRule5.snapshot1")
  1056  	require.NoError(t, err)
  1057  	require.True(t, rr.tombstoned())
  1058  
  1059  	require.Equal(t, nowNanos+10, rr.snapshots[len(rr.snapshots)-1].cutoverNanos)
  1060  	require.Nil(t, rr.snapshots[len(rr.snapshots)-1].targets)
  1061  
  1062  	rrs, err = rs.RollupRules()
  1063  	require.NoError(t, err)
  1064  	require.Contains(t, rrs, "rollupRule5")
  1065  }
  1066  
  1067  func TestRuleSetDelete(t *testing.T) {
  1068  	var (
  1069  		version = 1
  1070  		proto   = testRuleSetProto()
  1071  		opts    = testRuleSetOptions()
  1072  	)
  1073  	res, err := NewRuleSetFromProto(version, proto, opts)
  1074  	require.NoError(t, err)
  1075  	rs := res.(*ruleSet)
  1076  
  1077  	nowNanos := time.Now().UnixNano()
  1078  	helper := NewRuleSetUpdateHelper(10)
  1079  	err = rs.Delete(helper.NewUpdateMetadata(nowNanos, testUser))
  1080  	require.NoError(t, err)
  1081  
  1082  	require.True(t, rs.Tombstoned())
  1083  	for _, m := range rs.mappingRules {
  1084  		require.True(t, m.tombstoned())
  1085  	}
  1086  
  1087  	for _, r := range rs.rollupRules {
  1088  		require.True(t, r.tombstoned())
  1089  	}
  1090  }
  1091  
  1092  func TestRuleSetRevive(t *testing.T) {
  1093  	var (
  1094  		version = 1
  1095  		proto   = testRuleSetProto()
  1096  		opts    = testRuleSetOptions()
  1097  	)
  1098  	res, err := NewRuleSetFromProto(version, proto, opts)
  1099  	require.NoError(t, err)
  1100  	rs := res.(*ruleSet)
  1101  
  1102  	nowNanos := time.Now().UnixNano()
  1103  	helper := NewRuleSetUpdateHelper(10)
  1104  	err = rs.Delete(helper.NewUpdateMetadata(nowNanos, testUser))
  1105  	require.NoError(t, err)
  1106  
  1107  	err = rs.Revive(helper.NewUpdateMetadata(nowNanos, testUser))
  1108  	require.NoError(t, err)
  1109  
  1110  	require.False(t, rs.Tombstoned())
  1111  	for _, m := range rs.mappingRules {
  1112  		require.True(t, m.tombstoned())
  1113  	}
  1114  
  1115  	for _, r := range rs.rollupRules {
  1116  		require.True(t, r.tombstoned())
  1117  	}
  1118  }
  1119  
  1120  func TestApplyRuleSetChanges(t *testing.T) {
  1121  	var (
  1122  		version = 1
  1123  		proto   = testRuleSetProto()
  1124  		opts    = testRuleSetOptions()
  1125  	)
  1126  	res, err := NewRuleSetFromProto(version, proto, opts)
  1127  	require.NoError(t, err)
  1128  	rs := res.(*ruleSet)
  1129  
  1130  	changes := changes.RuleSetChanges{
  1131  		MappingRuleChanges: []changes.MappingRuleChange{
  1132  			{
  1133  				Op: changes.AddOp,
  1134  				RuleData: &view.MappingRule{
  1135  					ID:   "mrID1",
  1136  					Name: "mappingRuleAdd",
  1137  				},
  1138  			},
  1139  			{
  1140  				Op:     changes.ChangeOp,
  1141  				RuleID: ptr("mappingRule1"),
  1142  				RuleData: &view.MappingRule{
  1143  					ID:   "mappingRule1",
  1144  					Name: "updatedMappingRule",
  1145  				},
  1146  			},
  1147  			{
  1148  				Op:     changes.DeleteOp,
  1149  				RuleID: ptr("mappingRule3"),
  1150  			},
  1151  		},
  1152  		RollupRuleChanges: []changes.RollupRuleChange{
  1153  			{
  1154  				Op: changes.AddOp,
  1155  				RuleData: &view.RollupRule{
  1156  					ID:   "rrID1",
  1157  					Name: "rollupRuleAdd",
  1158  				},
  1159  			},
  1160  			{
  1161  				Op:     changes.ChangeOp,
  1162  				RuleID: ptr("rollupRule1"),
  1163  				RuleData: &view.RollupRule{
  1164  					ID:   "rollupRule1",
  1165  					Name: "updatedRollupRule",
  1166  				},
  1167  			},
  1168  			{
  1169  				Op:     changes.DeleteOp,
  1170  				RuleID: ptr("rollupRule3"),
  1171  			},
  1172  		},
  1173  	}
  1174  
  1175  	nowNanos := time.Now().UnixNano()
  1176  	helper := NewRuleSetUpdateHelper(10)
  1177  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1178  	require.NoError(t, err)
  1179  
  1180  	_, err = rs.getMappingRuleByName("mappingRuleAdd")
  1181  	require.NoError(t, err)
  1182  	_, err = rs.getRollupRuleByName("rollupRuleAdd")
  1183  	require.NoError(t, err)
  1184  
  1185  	updatedMappingRule, err := rs.getMappingRuleByID("mappingRule1")
  1186  	require.NoError(t, err)
  1187  	name, err := updatedMappingRule.name()
  1188  	require.NoError(t, err)
  1189  	require.Equal(t, name, "updatedMappingRule")
  1190  	updatedRollupRule, err := rs.getRollupRuleByID("rollupRule1")
  1191  	require.NoError(t, err)
  1192  	name, err = updatedRollupRule.name()
  1193  	require.NoError(t, err)
  1194  	require.Equal(t, name, "updatedRollupRule")
  1195  
  1196  	tombstonedMappingRule, err := rs.getMappingRuleByID("mappingRule3")
  1197  	require.NoError(t, err)
  1198  	require.True(t, tombstonedMappingRule.tombstoned())
  1199  	tombstonedRollupRule, err := rs.getRollupRuleByID("rollupRule3")
  1200  	require.NoError(t, err)
  1201  	require.True(t, tombstonedRollupRule.tombstoned())
  1202  }
  1203  
  1204  func TestApplyMappingRuleChangesAddFailure(t *testing.T) {
  1205  	var (
  1206  		version = 1
  1207  		proto   = testRuleSetProto()
  1208  		opts    = testRuleSetOptions()
  1209  	)
  1210  	res, err := NewRuleSetFromProto(version, proto, opts)
  1211  	require.NoError(t, err)
  1212  	rs := res.(*ruleSet)
  1213  
  1214  	changes := changes.RuleSetChanges{
  1215  		MappingRuleChanges: []changes.MappingRuleChange{
  1216  			{
  1217  				Op: changes.AddOp,
  1218  				RuleData: &view.MappingRule{
  1219  					ID:   "mappingRule1",
  1220  					Name: "mappingRule1.snapshot3",
  1221  				},
  1222  			},
  1223  		},
  1224  	}
  1225  
  1226  	nowNanos := time.Now().UnixNano()
  1227  	helper := NewRuleSetUpdateHelper(10)
  1228  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1229  	require.Error(t, err)
  1230  	err = xerrors.InnerError(err)
  1231  	require.NotNil(t, err)
  1232  	_, ok := err.(merrors.InvalidInputError) //nolint:errorlint
  1233  	require.True(t, ok)
  1234  }
  1235  
  1236  func TestApplyRollupRuleChangesAddFailure(t *testing.T) {
  1237  	var (
  1238  		version = 1
  1239  		proto   = testRuleSetProto()
  1240  		opts    = testRuleSetOptions()
  1241  	)
  1242  	res, err := NewRuleSetFromProto(version, proto, opts)
  1243  	require.NoError(t, err)
  1244  	rs := res.(*ruleSet)
  1245  
  1246  	changes := changes.RuleSetChanges{
  1247  		RollupRuleChanges: []changes.RollupRuleChange{
  1248  			{
  1249  				Op: changes.AddOp,
  1250  				RuleData: &view.RollupRule{
  1251  					ID:   "rollupRule1",
  1252  					Name: "rollupRule1.snapshot3",
  1253  				},
  1254  			},
  1255  		},
  1256  	}
  1257  
  1258  	nowNanos := time.Now().UnixNano()
  1259  	helper := NewRuleSetUpdateHelper(10)
  1260  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1261  	require.Error(t, err)
  1262  	err = xerrors.InnerError(err)
  1263  	require.NotNil(t, err)
  1264  	_, ok := err.(merrors.InvalidInputError) //nolint:errorlint
  1265  	require.True(t, ok)
  1266  }
  1267  
  1268  func TestApplyMappingRuleChangesDeleteFailure(t *testing.T) {
  1269  	var (
  1270  		version = 1
  1271  		proto   = testRuleSetProto()
  1272  		opts    = testRuleSetOptions()
  1273  	)
  1274  	res, err := NewRuleSetFromProto(version, proto, opts)
  1275  	require.NoError(t, err)
  1276  	rs := res.(*ruleSet)
  1277  
  1278  	changes := changes.RuleSetChanges{
  1279  		MappingRuleChanges: []changes.MappingRuleChange{
  1280  			{
  1281  				Op:     changes.DeleteOp,
  1282  				RuleID: ptr("mappingRule2"),
  1283  			},
  1284  		},
  1285  	}
  1286  
  1287  	nowNanos := time.Now().UnixNano()
  1288  	helper := NewRuleSetUpdateHelper(10)
  1289  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1290  	require.Error(t, err)
  1291  	err = xerrors.InnerError(err)
  1292  	require.NotNil(t, err)
  1293  	_, ok := err.(merrors.InvalidInputError) //nolint:errorlint
  1294  	require.True(t, ok)
  1295  }
  1296  
  1297  func TestApplyRollupRuleChangesDeleteFailure(t *testing.T) {
  1298  	var (
  1299  		version = 1
  1300  		proto   = testRuleSetProto()
  1301  		opts    = testRuleSetOptions()
  1302  	)
  1303  	res, err := NewRuleSetFromProto(version, proto, opts)
  1304  	require.NoError(t, err)
  1305  	rs := res.(*ruleSet)
  1306  
  1307  	changes := changes.RuleSetChanges{
  1308  		RollupRuleChanges: []changes.RollupRuleChange{
  1309  			{
  1310  				Op:     changes.DeleteOp,
  1311  				RuleID: ptr("rollupRule2"),
  1312  			},
  1313  		},
  1314  	}
  1315  
  1316  	nowNanos := time.Now().UnixNano()
  1317  	helper := NewRuleSetUpdateHelper(10)
  1318  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1319  	require.Error(t, err)
  1320  	err = xerrors.InnerError(err)
  1321  	require.NotNil(t, err)
  1322  	_, ok := err.(merrors.InvalidInputError) //nolint:errorlint
  1323  	require.True(t, ok)
  1324  }
  1325  
  1326  func TestApplyMappingRuleChangesUpdateFailure(t *testing.T) {
  1327  	var (
  1328  		version = 1
  1329  		proto   = testRuleSetProto()
  1330  		opts    = testRuleSetOptions()
  1331  	)
  1332  	res, err := NewRuleSetFromProto(version, proto, opts)
  1333  	require.NoError(t, err)
  1334  	rs := res.(*ruleSet)
  1335  
  1336  	changes := changes.RuleSetChanges{
  1337  		MappingRuleChanges: []changes.MappingRuleChange{
  1338  			{
  1339  				Op:     changes.ChangeOp,
  1340  				RuleID: ptr("invalidMappingRule"),
  1341  				RuleData: &view.MappingRule{
  1342  					ID:   "invalidMappingRule",
  1343  					Name: "updatedMappingRule",
  1344  				},
  1345  			},
  1346  		},
  1347  	}
  1348  
  1349  	nowNanos := time.Now().UnixNano()
  1350  	helper := NewRuleSetUpdateHelper(10)
  1351  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1352  	require.Error(t, err)
  1353  	require.IsType(t, merrors.NewInvalidInputError(""), err)
  1354  }
  1355  
  1356  func TestApplyRollupRuleChangesUpdateFailure(t *testing.T) {
  1357  	var (
  1358  		version = 1
  1359  		proto   = testRuleSetProto()
  1360  		opts    = testRuleSetOptions()
  1361  	)
  1362  	res, err := NewRuleSetFromProto(version, proto, opts)
  1363  	require.NoError(t, err)
  1364  	rs := res.(*ruleSet)
  1365  
  1366  	changes := changes.RuleSetChanges{
  1367  		RollupRuleChanges: []changes.RollupRuleChange{
  1368  			{
  1369  				Op:     changes.ChangeOp,
  1370  				RuleID: ptr("rollupRule1"),
  1371  				RuleData: &view.RollupRule{
  1372  					ID:   "invalidRollupRule",
  1373  					Name: "updatedRollupRule",
  1374  				},
  1375  			},
  1376  		},
  1377  	}
  1378  
  1379  	nowNanos := time.Now().UnixNano()
  1380  	helper := NewRuleSetUpdateHelper(10)
  1381  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1382  	require.Error(t, err)
  1383  	require.IsType(t, merrors.NewInvalidInputError(""), err)
  1384  }
  1385  
  1386  func TestApplyMappingRuleWithInvalidOp(t *testing.T) {
  1387  	var (
  1388  		version = 1
  1389  		proto   = testRuleSetProto()
  1390  		opts    = testRuleSetOptions()
  1391  	)
  1392  	res, err := NewRuleSetFromProto(version, proto, opts)
  1393  	require.NoError(t, err)
  1394  	rs := res.(*ruleSet)
  1395  
  1396  	changes := changes.RuleSetChanges{
  1397  		MappingRuleChanges: []changes.MappingRuleChange{{}},
  1398  	}
  1399  
  1400  	nowNanos := time.Now().UnixNano()
  1401  	helper := NewRuleSetUpdateHelper(10)
  1402  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1403  	require.Error(t, err)
  1404  	require.IsType(t, merrors.NewInvalidInputError(""), err)
  1405  }
  1406  
  1407  func TestApplyRollupRuleWithInvalidOp(t *testing.T) {
  1408  	var (
  1409  		version = 1
  1410  		proto   = testRuleSetProto()
  1411  		opts    = testRuleSetOptions()
  1412  	)
  1413  	res, err := NewRuleSetFromProto(version, proto, opts)
  1414  	require.NoError(t, err)
  1415  	rs := res.(*ruleSet)
  1416  
  1417  	changes := changes.RuleSetChanges{
  1418  		RollupRuleChanges: []changes.RollupRuleChange{{}},
  1419  	}
  1420  
  1421  	nowNanos := time.Now().UnixNano()
  1422  	helper := NewRuleSetUpdateHelper(10)
  1423  	err = rs.ApplyRuleSetChanges(changes, helper.NewUpdateMetadata(nowNanos, testUser))
  1424  	require.Error(t, err)
  1425  	require.IsType(t, merrors.NewInvalidInputError(""), err)
  1426  }
  1427  
  1428  func testRuleSetProto() *rulepb.RuleSet {
  1429  	return &rulepb.RuleSet{
  1430  		Uuid:               "ruleset",
  1431  		Namespace:          "namespace",
  1432  		CreatedAtNanos:     1234,
  1433  		LastUpdatedAtNanos: 5678,
  1434  		LastUpdatedBy:      "someone",
  1435  		Tombstoned:         false,
  1436  		CutoverNanos:       34923,
  1437  		MappingRules:       testMappingRulesConfig(),
  1438  		RollupRules:        testRollupRulesConfig(),
  1439  	}
  1440  }
  1441  
  1442  func testMappingRulesConfig() []*rulepb.MappingRule {
  1443  	return []*rulepb.MappingRule{
  1444  		&rulepb.MappingRule{
  1445  			Uuid: "mappingRule1",
  1446  			Snapshots: []*rulepb.MappingRuleSnapshot{
  1447  				&rulepb.MappingRuleSnapshot{
  1448  					Name:         "mappingRule1.snapshot1",
  1449  					Tombstoned:   false,
  1450  					CutoverNanos: 10000,
  1451  					Filter:       "mtagName1:mtagValue1",
  1452  					StoragePolicies: []*policypb.StoragePolicy{
  1453  						&policypb.StoragePolicy{
  1454  							Resolution: policypb.Resolution{
  1455  								WindowSize: 10 * time.Second.Nanoseconds(),
  1456  								Precision:  time.Second.Nanoseconds(),
  1457  							},
  1458  							Retention: policypb.Retention{
  1459  								Period: 24 * time.Hour.Nanoseconds(),
  1460  							},
  1461  						},
  1462  					},
  1463  					Tags: []*metricpb.Tag{},
  1464  				},
  1465  				&rulepb.MappingRuleSnapshot{
  1466  					Name:         "mappingRule1.snapshot2",
  1467  					Tombstoned:   false,
  1468  					CutoverNanos: 20000,
  1469  					Filter:       "mtagName1:mtagValue1",
  1470  					StoragePolicies: []*policypb.StoragePolicy{
  1471  						&policypb.StoragePolicy{
  1472  							Resolution: policypb.Resolution{
  1473  								WindowSize: 10 * time.Second.Nanoseconds(),
  1474  								Precision:  time.Second.Nanoseconds(),
  1475  							},
  1476  							Retention: policypb.Retention{
  1477  								Period: 6 * time.Hour.Nanoseconds(),
  1478  							},
  1479  						},
  1480  						&policypb.StoragePolicy{
  1481  							Resolution: policypb.Resolution{
  1482  								WindowSize: 5 * time.Minute.Nanoseconds(),
  1483  								Precision:  time.Minute.Nanoseconds(),
  1484  							},
  1485  							Retention: policypb.Retention{
  1486  								Period: 48 * time.Hour.Nanoseconds(),
  1487  							},
  1488  						},
  1489  						&policypb.StoragePolicy{
  1490  							Resolution: policypb.Resolution{
  1491  								WindowSize: 10 * time.Minute.Nanoseconds(),
  1492  								Precision:  time.Minute.Nanoseconds(),
  1493  							},
  1494  							Retention: policypb.Retention{
  1495  								Period: 48 * time.Hour.Nanoseconds(),
  1496  							},
  1497  						},
  1498  					},
  1499  					Tags: []*metricpb.Tag{},
  1500  				},
  1501  				&rulepb.MappingRuleSnapshot{
  1502  					Name:         "mappingRule1.snapshot3",
  1503  					Tombstoned:   false,
  1504  					CutoverNanos: 30000,
  1505  					Filter:       "mtagName1:mtagValue1",
  1506  					StoragePolicies: []*policypb.StoragePolicy{
  1507  						&policypb.StoragePolicy{
  1508  							Resolution: policypb.Resolution{
  1509  								WindowSize: 30 * time.Second.Nanoseconds(),
  1510  								Precision:  time.Second.Nanoseconds(),
  1511  							},
  1512  							Retention: policypb.Retention{
  1513  								Period: 6 * time.Hour.Nanoseconds(),
  1514  							},
  1515  						},
  1516  					},
  1517  					Tags: []*metricpb.Tag{},
  1518  				},
  1519  			},
  1520  		},
  1521  		&rulepb.MappingRule{
  1522  			Uuid: "mappingRule2",
  1523  			Snapshots: []*rulepb.MappingRuleSnapshot{
  1524  				&rulepb.MappingRuleSnapshot{
  1525  					Name:         "mappingRule2.snapshot1",
  1526  					Tombstoned:   false,
  1527  					CutoverNanos: 15000,
  1528  					Filter:       "mtagName1:mtagValue1",
  1529  					StoragePolicies: []*policypb.StoragePolicy{
  1530  						&policypb.StoragePolicy{
  1531  							Resolution: policypb.Resolution{
  1532  								WindowSize: 10 * time.Second.Nanoseconds(),
  1533  								Precision:  time.Second.Nanoseconds(),
  1534  							},
  1535  							Retention: policypb.Retention{
  1536  								Period: 12 * time.Hour.Nanoseconds(),
  1537  							},
  1538  						},
  1539  					},
  1540  					Tags: []*metricpb.Tag{},
  1541  				},
  1542  				&rulepb.MappingRuleSnapshot{
  1543  					Name:         "mappingRule2.snapshot2",
  1544  					Tombstoned:   false,
  1545  					CutoverNanos: 22000,
  1546  					Filter:       "mtagName1:mtagValue1",
  1547  					AggregationTypes: []aggregationpb.AggregationType{
  1548  						aggregationpb.AggregationType_MIN,
  1549  					},
  1550  					StoragePolicies: []*policypb.StoragePolicy{
  1551  						&policypb.StoragePolicy{
  1552  							Resolution: policypb.Resolution{
  1553  								WindowSize: 10 * time.Second.Nanoseconds(),
  1554  								Precision:  time.Second.Nanoseconds(),
  1555  							},
  1556  							Retention: policypb.Retention{
  1557  								Period: 2 * time.Hour.Nanoseconds(),
  1558  							},
  1559  						},
  1560  						&policypb.StoragePolicy{
  1561  							Resolution: policypb.Resolution{
  1562  								WindowSize: int64(time.Minute),
  1563  								Precision:  int64(time.Minute),
  1564  							},
  1565  							Retention: policypb.Retention{
  1566  								Period: int64(time.Hour),
  1567  							},
  1568  						},
  1569  					},
  1570  					Tags: []*metricpb.Tag{},
  1571  				},
  1572  				&rulepb.MappingRuleSnapshot{
  1573  					Name:         "mappingRule2.snapshot3",
  1574  					Tombstoned:   true,
  1575  					CutoverNanos: 35000,
  1576  					Filter:       "mtagName1:mtagValue1",
  1577  					AggregationTypes: []aggregationpb.AggregationType{
  1578  						aggregationpb.AggregationType_MIN,
  1579  					},
  1580  					StoragePolicies: []*policypb.StoragePolicy{
  1581  						&policypb.StoragePolicy{
  1582  							Resolution: policypb.Resolution{
  1583  								WindowSize: int64(10 * time.Second),
  1584  								Precision:  int64(time.Second),
  1585  							},
  1586  							Retention: policypb.Retention{
  1587  								Period: int64(2 * time.Hour),
  1588  							},
  1589  						},
  1590  						&policypb.StoragePolicy{
  1591  							Resolution: policypb.Resolution{
  1592  								WindowSize: int64(time.Minute),
  1593  								Precision:  int64(time.Minute),
  1594  							},
  1595  							Retention: policypb.Retention{
  1596  								Period: int64(time.Hour),
  1597  							},
  1598  						},
  1599  					},
  1600  					Tags: []*metricpb.Tag{},
  1601  				},
  1602  			},
  1603  		},
  1604  		&rulepb.MappingRule{
  1605  			Uuid: "mappingRule3",
  1606  			Snapshots: []*rulepb.MappingRuleSnapshot{
  1607  				&rulepb.MappingRuleSnapshot{
  1608  					Name:         "mappingRule3.snapshot1",
  1609  					Tombstoned:   false,
  1610  					CutoverNanos: 22000,
  1611  					Filter:       "mtagName1:mtagValue1",
  1612  					AggregationTypes: []aggregationpb.AggregationType{
  1613  						aggregationpb.AggregationType_MAX,
  1614  					},
  1615  					StoragePolicies: []*policypb.StoragePolicy{
  1616  						&policypb.StoragePolicy{
  1617  							Resolution: policypb.Resolution{
  1618  								WindowSize: int64(10 * time.Second),
  1619  								Precision:  int64(time.Second),
  1620  							},
  1621  							Retention: policypb.Retention{
  1622  								Period: int64(12 * time.Hour),
  1623  							},
  1624  						},
  1625  						&policypb.StoragePolicy{
  1626  							Resolution: policypb.Resolution{
  1627  								WindowSize: int64(5 * time.Minute),
  1628  								Precision:  int64(time.Minute),
  1629  							},
  1630  							Retention: policypb.Retention{
  1631  								Period: int64(48 * time.Hour),
  1632  							},
  1633  						},
  1634  					},
  1635  					Tags: []*metricpb.Tag{},
  1636  				},
  1637  				&rulepb.MappingRuleSnapshot{
  1638  					Name:         "mappingRule3.snapshot2",
  1639  					Tombstoned:   false,
  1640  					CutoverNanos: 34000,
  1641  					Filter:       "mtagName1:mtagValue1",
  1642  					StoragePolicies: []*policypb.StoragePolicy{
  1643  						&policypb.StoragePolicy{
  1644  							Resolution: policypb.Resolution{
  1645  								WindowSize: int64(10 * time.Second),
  1646  								Precision:  int64(time.Second),
  1647  							},
  1648  							Retention: policypb.Retention{
  1649  								Period: int64(2 * time.Hour),
  1650  							},
  1651  						},
  1652  						&policypb.StoragePolicy{
  1653  							Resolution: policypb.Resolution{
  1654  								WindowSize: int64(time.Minute),
  1655  								Precision:  int64(time.Minute),
  1656  							},
  1657  							Retention: policypb.Retention{
  1658  								Period: int64(time.Hour),
  1659  							},
  1660  						},
  1661  					},
  1662  					Tags: []*metricpb.Tag{},
  1663  				},
  1664  			},
  1665  		},
  1666  		&rulepb.MappingRule{
  1667  			Uuid: "mappingRule4",
  1668  			Snapshots: []*rulepb.MappingRuleSnapshot{
  1669  				&rulepb.MappingRuleSnapshot{
  1670  					Name:         "mappingRule4.snapshot1",
  1671  					Tombstoned:   false,
  1672  					CutoverNanos: 24000,
  1673  					Filter:       "mtagName1:mtagValue2",
  1674  					AggregationTypes: []aggregationpb.AggregationType{
  1675  						aggregationpb.AggregationType_P999,
  1676  					},
  1677  					StoragePolicies: []*policypb.StoragePolicy{
  1678  						&policypb.StoragePolicy{
  1679  							Resolution: policypb.Resolution{
  1680  								WindowSize: int64(10 * time.Second),
  1681  								Precision:  int64(time.Second),
  1682  							},
  1683  							Retention: policypb.Retention{
  1684  								Period: int64(24 * time.Hour),
  1685  							},
  1686  						},
  1687  					},
  1688  					Tags: []*metricpb.Tag{},
  1689  				},
  1690  			},
  1691  		},
  1692  		&rulepb.MappingRule{
  1693  			Uuid: "mappingRule5",
  1694  			Snapshots: []*rulepb.MappingRuleSnapshot{
  1695  				&rulepb.MappingRuleSnapshot{
  1696  					Name:               "mappingRule5.snapshot1",
  1697  					Tombstoned:         false,
  1698  					CutoverNanos:       100000,
  1699  					LastUpdatedAtNanos: 123456,
  1700  					LastUpdatedBy:      "test",
  1701  					Filter:             "mtagName1:mtagValue1",
  1702  					StoragePolicies: []*policypb.StoragePolicy{
  1703  						&policypb.StoragePolicy{
  1704  							Resolution: policypb.Resolution{
  1705  								WindowSize: int64(10 * time.Second),
  1706  								Precision:  int64(time.Second),
  1707  							},
  1708  							Retention: policypb.Retention{
  1709  								Period: int64(24 * time.Hour),
  1710  							},
  1711  						},
  1712  					},
  1713  					Tags: []*metricpb.Tag{},
  1714  				},
  1715  			},
  1716  		},
  1717  	}
  1718  }
  1719  
  1720  func testRollupRulesConfig() []*rulepb.RollupRule {
  1721  	return []*rulepb.RollupRule{
  1722  		&rulepb.RollupRule{
  1723  			Uuid: "rollupRule1",
  1724  			Snapshots: []*rulepb.RollupRuleSnapshot{
  1725  				&rulepb.RollupRuleSnapshot{
  1726  					Name:         "rollupRule1.snapshot1",
  1727  					Tombstoned:   false,
  1728  					CutoverNanos: 10000,
  1729  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  1730  					TargetsV2: []*rulepb.RollupTargetV2{
  1731  						&rulepb.RollupTargetV2{
  1732  							Pipeline: &pipelinepb.Pipeline{
  1733  								Ops: []pipelinepb.PipelineOp{
  1734  									{
  1735  										Type: pipelinepb.PipelineOp_ROLLUP,
  1736  										Rollup: &pipelinepb.RollupOp{
  1737  											NewName: "rName1",
  1738  											Tags:    []string{"rtagName1", "rtagName2"},
  1739  										},
  1740  									},
  1741  								},
  1742  							},
  1743  							StoragePolicies: []*policypb.StoragePolicy{
  1744  								&policypb.StoragePolicy{
  1745  									Resolution: policypb.Resolution{
  1746  										WindowSize: int64(10 * time.Second),
  1747  										Precision:  int64(time.Second),
  1748  									},
  1749  									Retention: policypb.Retention{
  1750  										Period: int64(24 * time.Hour),
  1751  									},
  1752  								},
  1753  							},
  1754  						},
  1755  					},
  1756  					Tags: []*metricpb.Tag{},
  1757  				},
  1758  				&rulepb.RollupRuleSnapshot{
  1759  					Name:         "rollupRule1.snapshot2",
  1760  					Tombstoned:   false,
  1761  					CutoverNanos: 20000,
  1762  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  1763  					TargetsV2: []*rulepb.RollupTargetV2{
  1764  						&rulepb.RollupTargetV2{
  1765  							Pipeline: &pipelinepb.Pipeline{
  1766  								Ops: []pipelinepb.PipelineOp{
  1767  									{
  1768  										Type: pipelinepb.PipelineOp_ROLLUP,
  1769  										Rollup: &pipelinepb.RollupOp{
  1770  											NewName: "rName1",
  1771  											Tags:    []string{"rtagName1", "rtagName2"},
  1772  										},
  1773  									},
  1774  								},
  1775  							},
  1776  							StoragePolicies: []*policypb.StoragePolicy{
  1777  								&policypb.StoragePolicy{
  1778  									Resolution: policypb.Resolution{
  1779  										WindowSize: int64(10 * time.Second),
  1780  										Precision:  int64(time.Second),
  1781  									},
  1782  									Retention: policypb.Retention{
  1783  										Period: int64(6 * time.Hour),
  1784  									},
  1785  								},
  1786  								&policypb.StoragePolicy{
  1787  									Resolution: policypb.Resolution{
  1788  										WindowSize: int64(5 * time.Minute),
  1789  										Precision:  int64(time.Minute),
  1790  									},
  1791  									Retention: policypb.Retention{
  1792  										Period: int64(48 * time.Hour),
  1793  									},
  1794  								},
  1795  								&policypb.StoragePolicy{
  1796  									Resolution: policypb.Resolution{
  1797  										WindowSize: int64(10 * time.Minute),
  1798  										Precision:  int64(time.Minute),
  1799  									},
  1800  									Retention: policypb.Retention{
  1801  										Period: int64(48 * time.Hour),
  1802  									},
  1803  								},
  1804  							},
  1805  						},
  1806  					},
  1807  					Tags: []*metricpb.Tag{},
  1808  				},
  1809  				&rulepb.RollupRuleSnapshot{
  1810  					Name:         "rollupRule1.snapshot3",
  1811  					Tombstoned:   false,
  1812  					CutoverNanos: 30000,
  1813  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  1814  					TargetsV2: []*rulepb.RollupTargetV2{
  1815  						&rulepb.RollupTargetV2{
  1816  							Pipeline: &pipelinepb.Pipeline{
  1817  								Ops: []pipelinepb.PipelineOp{
  1818  									{
  1819  										Type: pipelinepb.PipelineOp_ROLLUP,
  1820  										Rollup: &pipelinepb.RollupOp{
  1821  											NewName: "rName1",
  1822  											Tags:    []string{"rtagName1", "rtagName2"},
  1823  										},
  1824  									},
  1825  								},
  1826  							},
  1827  							StoragePolicies: []*policypb.StoragePolicy{
  1828  								&policypb.StoragePolicy{
  1829  									Resolution: policypb.Resolution{
  1830  										WindowSize: int64(30 * time.Second),
  1831  										Precision:  int64(time.Second),
  1832  									},
  1833  									Retention: policypb.Retention{
  1834  										Period: int64(6 * time.Hour),
  1835  									},
  1836  								},
  1837  							},
  1838  						},
  1839  					},
  1840  					Tags: []*metricpb.Tag{},
  1841  				},
  1842  			},
  1843  		},
  1844  		&rulepb.RollupRule{
  1845  			Uuid: "rollupRule2",
  1846  			Snapshots: []*rulepb.RollupRuleSnapshot{
  1847  				&rulepb.RollupRuleSnapshot{
  1848  					Name:         "rollupRule2.snapshot1",
  1849  					Tombstoned:   false,
  1850  					CutoverNanos: 15000,
  1851  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  1852  					TargetsV2: []*rulepb.RollupTargetV2{
  1853  						&rulepb.RollupTargetV2{
  1854  							Pipeline: &pipelinepb.Pipeline{
  1855  								Ops: []pipelinepb.PipelineOp{
  1856  									{
  1857  										Type: pipelinepb.PipelineOp_ROLLUP,
  1858  										Rollup: &pipelinepb.RollupOp{
  1859  											NewName: "rName2",
  1860  											Tags:    []string{"rtagName1", "rtagName2"},
  1861  										},
  1862  									},
  1863  								},
  1864  							},
  1865  							StoragePolicies: []*policypb.StoragePolicy{
  1866  								&policypb.StoragePolicy{
  1867  									Resolution: policypb.Resolution{
  1868  										WindowSize: int64(10 * time.Second),
  1869  										Precision:  int64(time.Second),
  1870  									},
  1871  									Retention: policypb.Retention{
  1872  										Period: int64(12 * time.Hour),
  1873  									},
  1874  								},
  1875  							},
  1876  						},
  1877  					},
  1878  					Tags: []*metricpb.Tag{},
  1879  				},
  1880  				&rulepb.RollupRuleSnapshot{
  1881  					Name:         "rollupRule2.snapshot2",
  1882  					Tombstoned:   false,
  1883  					CutoverNanos: 22000,
  1884  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  1885  					TargetsV2: []*rulepb.RollupTargetV2{
  1886  						&rulepb.RollupTargetV2{
  1887  							Pipeline: &pipelinepb.Pipeline{
  1888  								Ops: []pipelinepb.PipelineOp{
  1889  									{
  1890  										Type: pipelinepb.PipelineOp_ROLLUP,
  1891  										Rollup: &pipelinepb.RollupOp{
  1892  											NewName: "rName2",
  1893  											Tags:    []string{"rtagName1", "rtagName2"},
  1894  										},
  1895  									},
  1896  								},
  1897  							},
  1898  							StoragePolicies: []*policypb.StoragePolicy{
  1899  								&policypb.StoragePolicy{
  1900  									Resolution: policypb.Resolution{
  1901  										WindowSize: int64(10 * time.Second),
  1902  										Precision:  int64(time.Second),
  1903  									},
  1904  									Retention: policypb.Retention{
  1905  										Period: int64(2 * time.Hour),
  1906  									},
  1907  								},
  1908  								&policypb.StoragePolicy{
  1909  									Resolution: policypb.Resolution{
  1910  										WindowSize: int64(time.Minute),
  1911  										Precision:  int64(time.Minute),
  1912  									},
  1913  									Retention: policypb.Retention{
  1914  										Period: int64(time.Hour),
  1915  									},
  1916  								},
  1917  							},
  1918  						},
  1919  					},
  1920  					Tags: []*metricpb.Tag{},
  1921  				},
  1922  				&rulepb.RollupRuleSnapshot{
  1923  					Name:         "rollupRule2.snapshot3",
  1924  					Tombstoned:   true,
  1925  					CutoverNanos: 35000,
  1926  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  1927  					TargetsV2: []*rulepb.RollupTargetV2{
  1928  						&rulepb.RollupTargetV2{
  1929  							Pipeline: &pipelinepb.Pipeline{
  1930  								Ops: []pipelinepb.PipelineOp{
  1931  									{
  1932  										Type: pipelinepb.PipelineOp_ROLLUP,
  1933  										Rollup: &pipelinepb.RollupOp{
  1934  											NewName: "rName2",
  1935  											Tags:    []string{"rtagName1", "rtagName2"},
  1936  										},
  1937  									},
  1938  								},
  1939  							},
  1940  							StoragePolicies: []*policypb.StoragePolicy{
  1941  								&policypb.StoragePolicy{
  1942  									Resolution: policypb.Resolution{
  1943  										WindowSize: int64(time.Minute),
  1944  										Precision:  int64(time.Minute),
  1945  									},
  1946  									Retention: policypb.Retention{
  1947  										Period: int64(time.Hour),
  1948  									},
  1949  								},
  1950  							},
  1951  						},
  1952  					},
  1953  					Tags: []*metricpb.Tag{},
  1954  				},
  1955  			},
  1956  		},
  1957  		&rulepb.RollupRule{
  1958  			Uuid: "rollupRule3",
  1959  			Snapshots: []*rulepb.RollupRuleSnapshot{
  1960  				&rulepb.RollupRuleSnapshot{
  1961  					Name:         "rollupRule3.snapshot1",
  1962  					Tombstoned:   false,
  1963  					CutoverNanos: 22000,
  1964  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  1965  					TargetsV2: []*rulepb.RollupTargetV2{
  1966  						&rulepb.RollupTargetV2{
  1967  							Pipeline: &pipelinepb.Pipeline{
  1968  								Ops: []pipelinepb.PipelineOp{
  1969  									{
  1970  										Type: pipelinepb.PipelineOp_ROLLUP,
  1971  										Rollup: &pipelinepb.RollupOp{
  1972  											NewName: "rName3",
  1973  											Tags:    []string{"rtagName1", "rtagName2"},
  1974  										},
  1975  									},
  1976  								},
  1977  							},
  1978  							StoragePolicies: []*policypb.StoragePolicy{
  1979  								&policypb.StoragePolicy{
  1980  									Resolution: policypb.Resolution{
  1981  										WindowSize: int64(10 * time.Second),
  1982  										Precision:  int64(time.Second),
  1983  									},
  1984  									Retention: policypb.Retention{
  1985  										Period: int64(12 * time.Hour),
  1986  									},
  1987  								},
  1988  								&policypb.StoragePolicy{
  1989  									Resolution: policypb.Resolution{
  1990  										WindowSize: int64(time.Minute),
  1991  										Precision:  int64(time.Minute),
  1992  									},
  1993  									Retention: policypb.Retention{
  1994  										Period: int64(24 * time.Hour),
  1995  									},
  1996  								},
  1997  								&policypb.StoragePolicy{
  1998  									Resolution: policypb.Resolution{
  1999  										WindowSize: int64(5 * time.Minute),
  2000  										Precision:  int64(time.Minute),
  2001  									},
  2002  									Retention: policypb.Retention{
  2003  										Period: int64(48 * time.Hour),
  2004  									},
  2005  								},
  2006  							},
  2007  						},
  2008  						&rulepb.RollupTargetV2{
  2009  							Pipeline: &pipelinepb.Pipeline{
  2010  								Ops: []pipelinepb.PipelineOp{
  2011  									{
  2012  										Type: pipelinepb.PipelineOp_ROLLUP,
  2013  										Rollup: &pipelinepb.RollupOp{
  2014  											NewName: "rName3",
  2015  											Tags:    []string{"rtagName1"},
  2016  										},
  2017  									},
  2018  								},
  2019  							},
  2020  							StoragePolicies: []*policypb.StoragePolicy{
  2021  								&policypb.StoragePolicy{
  2022  									Resolution: policypb.Resolution{
  2023  										WindowSize: int64(10 * time.Second),
  2024  										Precision:  int64(time.Second),
  2025  									},
  2026  									Retention: policypb.Retention{
  2027  										Period: int64(24 * time.Hour),
  2028  									},
  2029  								},
  2030  							},
  2031  						},
  2032  					},
  2033  					Tags: []*metricpb.Tag{},
  2034  				},
  2035  				&rulepb.RollupRuleSnapshot{
  2036  					Name:         "rollupRule3.snapshot2",
  2037  					Tombstoned:   false,
  2038  					CutoverNanos: 34000,
  2039  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  2040  					TargetsV2: []*rulepb.RollupTargetV2{
  2041  						&rulepb.RollupTargetV2{
  2042  							Pipeline: &pipelinepb.Pipeline{
  2043  								Ops: []pipelinepb.PipelineOp{
  2044  									{
  2045  										Type: pipelinepb.PipelineOp_ROLLUP,
  2046  										Rollup: &pipelinepb.RollupOp{
  2047  											NewName: "rName3",
  2048  											Tags:    []string{"rtagName1", "rtagName2"},
  2049  										},
  2050  									},
  2051  								},
  2052  							},
  2053  							StoragePolicies: []*policypb.StoragePolicy{
  2054  								&policypb.StoragePolicy{
  2055  									Resolution: policypb.Resolution{
  2056  										WindowSize: int64(10 * time.Second),
  2057  										Precision:  int64(time.Second),
  2058  									},
  2059  									Retention: policypb.Retention{
  2060  										Period: int64(2 * time.Hour),
  2061  									},
  2062  								},
  2063  								&policypb.StoragePolicy{
  2064  									Resolution: policypb.Resolution{
  2065  										WindowSize: int64(time.Minute),
  2066  										Precision:  int64(time.Minute),
  2067  									},
  2068  									Retention: policypb.Retention{
  2069  										Period: int64(time.Hour),
  2070  									},
  2071  								},
  2072  							},
  2073  						},
  2074  					},
  2075  					Tags: []*metricpb.Tag{},
  2076  				},
  2077  			},
  2078  		},
  2079  		&rulepb.RollupRule{
  2080  			Uuid: "rollupRule4",
  2081  			Snapshots: []*rulepb.RollupRuleSnapshot{
  2082  				&rulepb.RollupRuleSnapshot{
  2083  					Name:         "rollupRule4.snapshot1",
  2084  					Tombstoned:   false,
  2085  					CutoverNanos: 24000,
  2086  					Filter:       "rtagName1:rtagValue2",
  2087  					TargetsV2: []*rulepb.RollupTargetV2{
  2088  						&rulepb.RollupTargetV2{
  2089  							Pipeline: &pipelinepb.Pipeline{
  2090  								Ops: []pipelinepb.PipelineOp{
  2091  									{
  2092  										Type: pipelinepb.PipelineOp_ROLLUP,
  2093  										Rollup: &pipelinepb.RollupOp{
  2094  											NewName: "rName4",
  2095  											Tags:    []string{"rtagName1", "rtagName2"},
  2096  										},
  2097  									},
  2098  								},
  2099  							},
  2100  							StoragePolicies: []*policypb.StoragePolicy{
  2101  								&policypb.StoragePolicy{
  2102  									Resolution: policypb.Resolution{
  2103  										WindowSize: int64(time.Minute),
  2104  										Precision:  int64(time.Minute),
  2105  									},
  2106  									Retention: policypb.Retention{
  2107  										Period: int64(time.Hour),
  2108  									},
  2109  								},
  2110  							},
  2111  						},
  2112  					},
  2113  					Tags: []*metricpb.Tag{},
  2114  				},
  2115  			},
  2116  		},
  2117  		&rulepb.RollupRule{
  2118  			Uuid: "rollupRule5",
  2119  			Snapshots: []*rulepb.RollupRuleSnapshot{
  2120  				&rulepb.RollupRuleSnapshot{
  2121  					Name:         "rollupRule5.snapshot1",
  2122  					Tombstoned:   false,
  2123  					CutoverNanos: 24000,
  2124  					Filter:       "rtagName1:rtagValue2",
  2125  					TargetsV2: []*rulepb.RollupTargetV2{
  2126  						&rulepb.RollupTargetV2{
  2127  							Pipeline: &pipelinepb.Pipeline{
  2128  								Ops: []pipelinepb.PipelineOp{
  2129  									{
  2130  										Type: pipelinepb.PipelineOp_ROLLUP,
  2131  										Rollup: &pipelinepb.RollupOp{
  2132  											NewName: "rName5",
  2133  											Tags:    []string{"rtagName1"},
  2134  										},
  2135  									},
  2136  								},
  2137  							},
  2138  							StoragePolicies: []*policypb.StoragePolicy{
  2139  								&policypb.StoragePolicy{
  2140  									Resolution: policypb.Resolution{
  2141  										WindowSize: int64(time.Second),
  2142  										Precision:  int64(time.Second),
  2143  									},
  2144  									Retention: policypb.Retention{
  2145  										Period: int64(time.Minute),
  2146  									},
  2147  								},
  2148  							},
  2149  						},
  2150  					},
  2151  					Tags: []*metricpb.Tag{},
  2152  				},
  2153  			},
  2154  		},
  2155  		&rulepb.RollupRule{
  2156  			Uuid: "rollupRule6",
  2157  			Snapshots: []*rulepb.RollupRuleSnapshot{
  2158  				&rulepb.RollupRuleSnapshot{
  2159  					Name:         "rollupRule6.snapshot1",
  2160  					Tombstoned:   false,
  2161  					CutoverNanos: 100000,
  2162  					Filter:       "rtagName1:rtagValue1 rtagName2:rtagValue2",
  2163  					TargetsV2: []*rulepb.RollupTargetV2{
  2164  						&rulepb.RollupTargetV2{
  2165  							Pipeline: &pipelinepb.Pipeline{
  2166  								Ops: []pipelinepb.PipelineOp{
  2167  									{
  2168  										Type: pipelinepb.PipelineOp_ROLLUP,
  2169  										Rollup: &pipelinepb.RollupOp{
  2170  											NewName: "rName6",
  2171  											Tags:    []string{"rtagName1", "rtagName2"},
  2172  										},
  2173  									},
  2174  								},
  2175  							},
  2176  							StoragePolicies: []*policypb.StoragePolicy{
  2177  								&policypb.StoragePolicy{
  2178  									Resolution: policypb.Resolution{
  2179  										WindowSize: int64(time.Minute),
  2180  										Precision:  int64(time.Minute),
  2181  									},
  2182  									Retention: policypb.Retention{
  2183  										Period: int64(time.Hour),
  2184  									},
  2185  								},
  2186  							},
  2187  						},
  2188  					},
  2189  					Tags: []*metricpb.Tag{},
  2190  				},
  2191  			},
  2192  		},
  2193  	}
  2194  }
  2195  
  2196  func testMatchOptions() MatchOptions {
  2197  	return MatchOptions{
  2198  		NameAndTagsFn: func(b []byte) ([]byte, []byte, error) {
  2199  			idx := bytes.Index(b, []byte("|"))
  2200  			if idx == -1 {
  2201  				return nil, b, nil
  2202  			}
  2203  			return b[:idx], b[idx+1:], nil
  2204  		},
  2205  		SortedTagIteratorFn: filters.NewMockSortedTagIterator,
  2206  	}
  2207  }
  2208  
  2209  func testTagsFilterOptions() filters.TagsFilterOptions {
  2210  	return filters.TagsFilterOptions{
  2211  		NameTagKey: []byte("name"),
  2212  		NameAndTagsFn: func(b []byte) ([]byte, []byte, error) {
  2213  			idx := bytes.Index(b, []byte("|"))
  2214  			if idx == -1 {
  2215  				return nil, b, nil
  2216  			}
  2217  			return b[:idx], b[idx+1:], nil
  2218  		},
  2219  		SortedTagIteratorFn: filters.NewMockSortedTagIterator,
  2220  	}
  2221  }
  2222  
  2223  func mockNewID(name []byte, tags []id.TagPair) []byte {
  2224  	if len(tags) == 0 {
  2225  		return name
  2226  	}
  2227  	var buf bytes.Buffer
  2228  	buf.Write(name)
  2229  	if len(tags) > 0 {
  2230  		buf.WriteString("|")
  2231  		for idx, p := range tags {
  2232  			buf.Write(p.Name)
  2233  			buf.WriteString("=")
  2234  			buf.Write(p.Value)
  2235  			if idx < len(tags)-1 {
  2236  				buf.WriteString(",")
  2237  			}
  2238  		}
  2239  	}
  2240  	return buf.Bytes()
  2241  }
  2242  
  2243  func testRuleSetOptions() Options {
  2244  	return NewOptions().
  2245  		SetTagsFilterOptions(testTagsFilterOptions()).
  2246  		SetNewRollupIDFn(mockNewID)
  2247  }
  2248  
  2249  func b(v string) []byte       { return []byte(v) }
  2250  func bs(v ...string) [][]byte { return xbytes.ArraysFromStringArray(v) }
  2251  func ptr(str string) *string  { return &str }
  2252  
  2253  type testMatchInput struct {
  2254  	id                    string
  2255  	matchFrom             int64
  2256  	matchTo               int64
  2257  	metricType            metric.Type      // reverse matching only
  2258  	aggregationType       aggregation.Type // reverse matching only
  2259  	expireAtNanos         int64
  2260  	forExistingIDResult   metadata.StagedMetadatas
  2261  	forNewRollupIDsResult []IDWithMetadatas
  2262  	keepOriginal          bool
  2263  }
  2264  
  2265  func (t testMatchInput) ID() id.ID {
  2266  	return namespace.NewTestID(t.id, "ns")
  2267  }