k8s.io/client-go@v0.31.1/tools/cache/thread_safe_store_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cache 18 19 import ( 20 "fmt" 21 "strings" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func TestThreadSafeStoreDeleteRemovesEmptySetsFromIndex(t *testing.T) { 29 testIndexer := "testIndexer" 30 31 indexers := Indexers{ 32 testIndexer: func(obj interface{}) (strings []string, e error) { 33 indexes := []string{obj.(string)} 34 return indexes, nil 35 }, 36 } 37 38 indices := Indices{} 39 store := NewThreadSafeStore(indexers, indices).(*threadSafeMap) 40 41 testKey := "testKey" 42 43 store.Add(testKey, testKey) 44 45 // Assumption check, there should be a set for the `testKey` with one element in the added index 46 set := store.index.indices[testIndexer][testKey] 47 48 if len(set) != 1 { 49 t.Errorf("Initial assumption of index backing string set having 1 element failed. Actual elements: %d", len(set)) 50 return 51 } 52 53 store.Delete(testKey) 54 set, present := store.index.indices[testIndexer][testKey] 55 56 if present { 57 t.Errorf("Index backing string set not deleted from index. Set length: %d", len(set)) 58 } 59 } 60 61 func TestThreadSafeStoreAddKeepsNonEmptySetPostDeleteFromIndex(t *testing.T) { 62 testIndexer := "testIndexer" 63 testIndex := "testIndex" 64 65 indexers := Indexers{ 66 testIndexer: func(obj interface{}) (strings []string, e error) { 67 indexes := []string{testIndex} 68 return indexes, nil 69 }, 70 } 71 72 indices := Indices{} 73 store := NewThreadSafeStore(indexers, indices).(*threadSafeMap) 74 75 store.Add("retain", "retain") 76 store.Add("delete", "delete") 77 78 // Assumption check, there should be a set for the `testIndex` with two elements 79 set := store.index.indices[testIndexer][testIndex] 80 81 if len(set) != 2 { 82 t.Errorf("Initial assumption of index backing string set having 2 elements failed. Actual elements: %d", len(set)) 83 return 84 } 85 86 store.Delete("delete") 87 set, present := store.index.indices[testIndexer][testIndex] 88 89 if !present { 90 t.Errorf("Index backing string set erroneously deleted from index.") 91 return 92 } 93 94 if len(set) != 1 { 95 t.Errorf("Index backing string set has incorrect length, expect 1. Set length: %d", len(set)) 96 } 97 } 98 99 func TestThreadSafeStoreIndexingFunctionsWithMultipleValues(t *testing.T) { 100 testIndexer := "testIndexer" 101 102 indexers := Indexers{ 103 testIndexer: func(obj interface{}) ([]string, error) { 104 return strings.Split(obj.(string), ","), nil 105 }, 106 } 107 108 indices := Indices{} 109 store := NewThreadSafeStore(indexers, indices).(*threadSafeMap) 110 111 store.Add("key1", "foo") 112 store.Add("key2", "bar") 113 114 assert := assert.New(t) 115 116 compare := func(key string, expected []string) error { 117 values := store.index.indices[testIndexer][key].List() 118 if cmp.Equal(values, expected) { 119 return nil 120 } 121 return fmt.Errorf("unexpected index for key %s, diff=%s", key, cmp.Diff(values, expected)) 122 } 123 124 assert.NoError(compare("foo", []string{"key1"})) 125 assert.NoError(compare("bar", []string{"key2"})) 126 127 store.Update("key2", "foo,bar") 128 129 assert.NoError(compare("foo", []string{"key1", "key2"})) 130 assert.NoError(compare("bar", []string{"key2"})) 131 132 store.Update("key1", "foo,bar") 133 134 assert.NoError(compare("foo", []string{"key1", "key2"})) 135 assert.NoError(compare("bar", []string{"key1", "key2"})) 136 137 store.Add("key3", "foo,bar,baz") 138 139 assert.NoError(compare("foo", []string{"key1", "key2", "key3"})) 140 assert.NoError(compare("bar", []string{"key1", "key2", "key3"})) 141 assert.NoError(compare("baz", []string{"key3"})) 142 143 store.Update("key1", "foo") 144 145 assert.NoError(compare("foo", []string{"key1", "key2", "key3"})) 146 assert.NoError(compare("bar", []string{"key2", "key3"})) 147 assert.NoError(compare("baz", []string{"key3"})) 148 149 store.Update("key2", "bar") 150 151 assert.NoError(compare("foo", []string{"key1", "key3"})) 152 assert.NoError(compare("bar", []string{"key2", "key3"})) 153 assert.NoError(compare("baz", []string{"key3"})) 154 155 store.Delete("key1") 156 157 assert.NoError(compare("foo", []string{"key3"})) 158 assert.NoError(compare("bar", []string{"key2", "key3"})) 159 assert.NoError(compare("baz", []string{"key3"})) 160 161 store.Delete("key3") 162 163 assert.NoError(compare("foo", []string{})) 164 assert.NoError(compare("bar", []string{"key2"})) 165 assert.NoError(compare("baz", []string{})) 166 } 167 168 func BenchmarkIndexer(b *testing.B) { 169 testIndexer := "testIndexer" 170 171 indexers := Indexers{ 172 testIndexer: func(obj interface{}) (strings []string, e error) { 173 indexes := []string{obj.(string)} 174 return indexes, nil 175 }, 176 } 177 178 indices := Indices{} 179 store := NewThreadSafeStore(indexers, indices).(*threadSafeMap) 180 181 // The following benchmark imitates what is happening in indexes 182 // used in storage layer, where indexing is mostly static (e.g. 183 // indexing objects by their (namespace, name)). 184 // The 5000 number imitates indexing nodes in 5000-node cluster. 185 objectCount := 5000 186 objects := make([]string, 0, 5000) 187 for i := 0; i < objectCount; i++ { 188 objects = append(objects, fmt.Sprintf("object-number-%d", i)) 189 } 190 191 b.ResetTimer() 192 for i := 0; i < b.N; i++ { 193 store.Update(objects[i%objectCount], objects[i%objectCount]) 194 } 195 }