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 &registry{
    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  }