github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/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 }