go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/internal/registry/registry_impl.go (about) 1 // Copyright (c) 2018 Cisco and/or its affiliates. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package registry 16 17 import ( 18 "container/list" 19 20 "fmt" 21 22 . "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 23 "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/utils" 24 ) 25 26 const ( 27 // maxKeyCacheSize is the maximum number of key->descriptor entries the registry 28 // will cache. 29 maxKeyCacheSize = 500 30 ) 31 32 // registry is an implementation of Registry for descriptors. 33 type registry struct { 34 descriptors map[string]*KVDescriptor // descriptor name -> descriptor 35 descriptorList []*KVDescriptor // ordered by retrieve dependencies 36 upToDateDescList bool // true if descriptorList is in sync with descriptors 37 keyToCacheEntry map[string]*list.Element // key -> cache entry 38 keyCache *list.List // doubly linked list of cached entries key->descriptor 39 } 40 41 // cacheEntry encapsulates data for one entry in registry.keyCache 42 type cacheEntry struct { 43 key string 44 descriptor *KVDescriptor 45 } 46 47 // NewRegistry creates a new instance of registry. 48 func NewRegistry() Registry { 49 return ®istry{ 50 descriptors: make(map[string]*KVDescriptor), 51 keyToCacheEntry: make(map[string]*list.Element), 52 keyCache: list.New(), 53 } 54 } 55 56 // RegisterDescriptor add new descriptor into the registry. 57 func (reg *registry) RegisterDescriptor(descriptor *KVDescriptor) { 58 reg.descriptors[descriptor.Name] = descriptor 59 reg.upToDateDescList = false 60 } 61 62 // GetAllDescriptors returns all registered descriptors. 63 func (reg *registry) GetAllDescriptors() (descriptors []*KVDescriptor) { 64 if reg.upToDateDescList { 65 return reg.descriptorList 66 } 67 68 // collect descriptor retrieve dependencies 69 deps := make(map[string]utils.KeySet) 70 descNames := utils.NewMapBasedKeySet() 71 for _, descriptor := range reg.descriptors { 72 descNames.Add(descriptor.Name) 73 deps[descriptor.Name] = utils.NewMapBasedKeySet(descriptor.RetrieveDependencies...) 74 } 75 76 // order topologically respecting dependencies. 77 orderedNames := utils.TopologicalOrder(descNames, deps, true, false) 78 reg.descriptorList = []*KVDescriptor{} 79 for _, descName := range orderedNames { 80 reg.descriptorList = append(reg.descriptorList, reg.descriptors[descName]) 81 } 82 83 reg.upToDateDescList = true 84 return reg.descriptorList 85 } 86 87 // GetDescriptor returns descriptor with the given name. 88 func (reg *registry) GetDescriptor(name string) *KVDescriptor { 89 descriptor, has := reg.descriptors[name] 90 if !has { 91 return nil 92 } 93 return descriptor 94 } 95 96 // GetDescriptorForKey returns descriptor handling the given key. 97 func (reg *registry) GetDescriptorForKey(key string) *KVDescriptor { 98 elem, cached := reg.keyToCacheEntry[key] 99 if cached { 100 // get descriptor from the cache 101 entry := elem.Value.(*cacheEntry) 102 reg.keyCache.MoveToFront(elem) 103 return entry.descriptor 104 } 105 if reg.keyCache.Len() == maxKeyCacheSize { 106 // the cache is full => remove the last used key 107 toRemove := reg.keyCache.Back() 108 toRemoveKey := toRemove.Value.(*cacheEntry).key 109 delete(reg.keyToCacheEntry, toRemoveKey) 110 reg.keyCache.Remove(toRemove) 111 } 112 // find the descriptor 113 var keyDescriptor *KVDescriptor 114 for _, descriptor := range reg.descriptors { 115 if descriptor.KeySelector(key) { 116 if keyDescriptor != nil { 117 panic(fmt.Sprintf("key %s is selected by both %s and %s descriptors", 118 key, keyDescriptor.Name, descriptor.Name)) 119 } 120 keyDescriptor = descriptor 121 } 122 } 123 // add entry to cache 124 entry := &cacheEntry{key: key, descriptor: keyDescriptor} 125 elem = reg.keyCache.PushFront(entry) 126 reg.keyToCacheEntry[key] = elem 127 return keyDescriptor 128 }