github.com/m3db/m3@v1.5.0/src/metrics/matcher/cache/list_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 cache
    22  
    23  import (
    24  	"fmt"
    25  	"math"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/metrics/aggregation"
    30  	"github.com/m3db/m3/src/metrics/matcher/namespace"
    31  	"github.com/m3db/m3/src/metrics/metadata"
    32  	"github.com/m3db/m3/src/metrics/metric/id"
    33  	"github.com/m3db/m3/src/metrics/policy"
    34  	"github.com/m3db/m3/src/metrics/rules"
    35  	xtime "github.com/m3db/m3/src/x/time"
    36  
    37  	"github.com/stretchr/testify/require"
    38  )
    39  
    40  var (
    41  	testForExistingID = metadata.StagedMetadatas{
    42  		metadata.StagedMetadata{
    43  			CutoverNanos: 0,
    44  			Tombstoned:   false,
    45  			Metadata: metadata.Metadata{
    46  				Pipelines: []metadata.PipelineMetadata{
    47  					{
    48  						AggregationID: aggregation.DefaultID,
    49  						StoragePolicies: policy.StoragePolicies{
    50  							policy.NewStoragePolicy(20*time.Second, xtime.Second, 6*time.Hour),
    51  							policy.NewStoragePolicy(time.Minute, xtime.Minute, 2*24*time.Hour),
    52  							policy.NewStoragePolicy(10*time.Minute, xtime.Minute, 25*24*time.Hour),
    53  						},
    54  					},
    55  				},
    56  			},
    57  		},
    58  		metadata.StagedMetadata{
    59  			CutoverNanos: 0,
    60  			Tombstoned:   true,
    61  			Metadata: metadata.Metadata{
    62  				Pipelines: []metadata.PipelineMetadata{
    63  					{
    64  						AggregationID: aggregation.DefaultID,
    65  						StoragePolicies: policy.StoragePolicies{
    66  							policy.NewStoragePolicy(time.Second, xtime.Second, time.Hour),
    67  						},
    68  					},
    69  				},
    70  			},
    71  		},
    72  	}
    73  	testForNewRollupIDs = []rules.IDWithMetadatas{
    74  		{
    75  			ID:        []byte("rID1"),
    76  			Metadatas: metadata.DefaultStagedMetadatas,
    77  		},
    78  		{
    79  			ID: []byte("rID2"),
    80  			Metadatas: metadata.StagedMetadatas{
    81  				metadata.StagedMetadata{
    82  					CutoverNanos: 0,
    83  					Tombstoned:   false,
    84  					Metadata: metadata.Metadata{
    85  						Pipelines: []metadata.PipelineMetadata{
    86  							{
    87  								AggregationID: aggregation.DefaultID,
    88  								StoragePolicies: policy.StoragePolicies{
    89  									policy.NewStoragePolicy(20*time.Second, xtime.Second, 6*time.Hour),
    90  									policy.NewStoragePolicy(time.Minute, xtime.Minute, 2*24*time.Hour),
    91  									policy.NewStoragePolicy(10*time.Minute, xtime.Minute, 25*24*time.Hour),
    92  								},
    93  							},
    94  						},
    95  					},
    96  				},
    97  				metadata.StagedMetadata{
    98  					CutoverNanos: 0,
    99  					Tombstoned:   true,
   100  					Metadata: metadata.Metadata{
   101  						Pipelines: []metadata.PipelineMetadata{
   102  							{
   103  								AggregationID: aggregation.DefaultID,
   104  								StoragePolicies: policy.StoragePolicies{
   105  									policy.NewStoragePolicy(time.Second, xtime.Second, time.Hour),
   106  								},
   107  							},
   108  						},
   109  					},
   110  				},
   111  			},
   112  		},
   113  	}
   114  	testValidResults = []rules.MatchResult{
   115  		rules.NewMatchResult(0, math.MaxInt64, nil, nil, true),
   116  		rules.NewMatchResult(0, math.MaxInt64, testForExistingID, testForNewRollupIDs, true),
   117  	}
   118  	testExpiredResults = []rules.MatchResult{
   119  		rules.NewMatchResult(0, 0, nil, nil, true),
   120  		rules.NewMatchResult(0, 0, testForExistingID, testForNewRollupIDs, true),
   121  	}
   122  )
   123  
   124  func TestElementShouldExpire(t *testing.T) {
   125  	now := time.Unix(0, 1234)
   126  	e := &element{}
   127  	for _, input := range []struct {
   128  		expiryNanos int64
   129  		expected    bool
   130  	}{
   131  		{expiryNanos: 1233, expected: true},
   132  		{expiryNanos: 1234, expected: true},
   133  		{expiryNanos: 1235, expected: false},
   134  	} {
   135  		e.expiryNanos = input.expiryNanos
   136  		require.Equal(t, input.expected, e.ShouldPromote(now))
   137  	}
   138  }
   139  
   140  func TestListPushFront(t *testing.T) {
   141  	var (
   142  		l      list
   143  		iter   = 10
   144  		inputs = make([]testValue, iter)
   145  	)
   146  	for i := 0; i < iter; i++ {
   147  		namespace := []byte(fmt.Sprintf("namespace%d", i))
   148  		id := []byte(fmt.Sprintf("foo%d", i))
   149  		result := testValidResults[i%2]
   150  		inputs[iter-i-1] = testValue{namespace: namespace, id: id, result: result}
   151  		l.PushFront(&element{
   152  			namespace: namespace,
   153  			id:        id,
   154  			result:    result,
   155  		})
   156  	}
   157  
   158  	// Pushing front a nil pointer is a no-op.
   159  	l.PushFront(nil)
   160  
   161  	// Pushing front a deleted element is a no-op.
   162  	l.PushFront(&element{
   163  		namespace: []byte("deletedNs"),
   164  		result:    testValidResults[0],
   165  		deleted:   true,
   166  	})
   167  
   168  	validateList(t, &l, inputs)
   169  }
   170  
   171  func TestListPushBack(t *testing.T) {
   172  	var (
   173  		l      list
   174  		iter   = 10
   175  		inputs = make([]testValue, iter)
   176  	)
   177  	for i := 0; i < iter; i++ {
   178  		namespace := []byte(fmt.Sprintf("namespace%d", i))
   179  		id := []byte(fmt.Sprintf("foo%d", i))
   180  		result := testValidResults[i%2]
   181  		inputs[i] = testValue{namespace: namespace, id: id, result: result}
   182  		l.PushBack(&element{
   183  			namespace: namespace,
   184  			id:        id,
   185  			result:    result,
   186  		})
   187  	}
   188  
   189  	// Pushing back a nil pointer is a no-op.
   190  	l.PushBack(nil)
   191  
   192  	// Pushing back a deleted element is a no-op.
   193  	l.PushBack(&element{
   194  		namespace: []byte("deletedNs"),
   195  		result:    testValidResults[0],
   196  		deleted:   true,
   197  	})
   198  
   199  	validateList(t, &l, inputs)
   200  }
   201  
   202  func TestListRemove(t *testing.T) {
   203  	var (
   204  		l      list
   205  		iter   = 10
   206  		inputs = make([]testValue, iter)
   207  	)
   208  	for i := 0; i < iter; i++ {
   209  		namespace := []byte(fmt.Sprintf("namespace%d", i))
   210  		id := []byte(fmt.Sprintf("foo%d", i))
   211  		result := testValidResults[i%2]
   212  		inputs[i] = testValue{namespace: namespace, id: id, result: result}
   213  		l.PushBack(&element{
   214  			namespace: namespace,
   215  			id:        id,
   216  			result:    result,
   217  		})
   218  	}
   219  
   220  	// Removing a nil pointer is no-op.
   221  	l.Remove(nil)
   222  
   223  	// Removing a deleted element is a no-op.
   224  	l.Remove(&element{
   225  		namespace: []byte("deletedNs"),
   226  		result:    testValidResults[0],
   227  		deleted:   true,
   228  	})
   229  
   230  	for i := 0; i < iter; i++ {
   231  		elem := l.Front()
   232  		l.Remove(elem)
   233  		require.Nil(t, elem.prev)
   234  		require.Nil(t, elem.next)
   235  		validateList(t, &l, inputs[i+1:])
   236  	}
   237  }
   238  
   239  func TestListMoveToFront(t *testing.T) {
   240  	var (
   241  		l      list
   242  		iter   = 10
   243  		inputs = make([]testValue, iter)
   244  	)
   245  	for i := 0; i < iter; i++ {
   246  		namespace := []byte(fmt.Sprintf("namespace%d", i))
   247  		id := []byte(fmt.Sprintf("foo%d", i))
   248  		result := testValidResults[i%2]
   249  		inputs[i] = testValue{namespace: namespace, id: id, result: result}
   250  		l.PushBack(&element{
   251  			namespace: namespace,
   252  			id:        id,
   253  			result:    result,
   254  		})
   255  	}
   256  
   257  	// Moving a nil pointer to front is a no-op.
   258  	l.MoveToFront(nil)
   259  
   260  	// Moving a deleted element to front is a no-op.
   261  	l.MoveToFront(&element{
   262  		namespace: []byte("deletedNs"),
   263  		result:    testValidResults[0],
   264  		deleted:   true,
   265  	})
   266  
   267  	// Starting from the back, moving elements to front one at a time.
   268  	var prev, curr, last *element
   269  	for {
   270  		if curr == last && curr != nil {
   271  			break
   272  		}
   273  		if last == nil {
   274  			last = l.Back()
   275  			curr = last
   276  		}
   277  		prev = curr.prev
   278  		l.MoveToFront(curr)
   279  		require.Equal(t, l.head, curr)
   280  		require.Nil(t, curr.prev)
   281  		curr = prev
   282  	}
   283  	validateList(t, &l, inputs)
   284  }
   285  
   286  type testValue struct {
   287  	namespace []byte
   288  	id        []byte
   289  	result    rules.MatchResult
   290  }
   291  
   292  func (t testValue) ID() id.ID {
   293  	return namespace.NewTestID(string(t.id), string(t.namespace))
   294  }
   295  
   296  func validateList(t *testing.T, l *list, expected []testValue) {
   297  	require.Equal(t, len(expected), l.Len())
   298  	i := 0
   299  	for elem := l.Front(); elem != nil; elem = elem.next {
   300  		require.Equal(t, expected[i].namespace, elem.namespace)
   301  		require.Equal(t, expected[i].id, elem.id)
   302  		require.Equal(t, expected[i].result, elem.result)
   303  		i++
   304  	}
   305  	if len(expected) == 0 {
   306  		require.Nil(t, l.head)
   307  		require.Nil(t, l.tail)
   308  	} else {
   309  		require.Equal(t, l.Front(), l.head)
   310  		require.Nil(t, l.head.prev)
   311  		require.Equal(t, l.Back(), l.tail)
   312  		require.Nil(t, l.tail.next)
   313  	}
   314  }