github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/cache/lruexpirecache.go (about)

     1  /*
     2  Copyright 2016 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  	"container/list"
    21  	"sync"
    22  	"time"
    23  )
    24  
    25  // Clock defines an interface for obtaining the current time
    26  type Clock interface {
    27  	Now() time.Time
    28  }
    29  
    30  // realClock implements the Clock interface by calling time.Now()
    31  type realClock struct{}
    32  
    33  func (realClock) Now() time.Time { return time.Now() }
    34  
    35  // LRUExpireCache is a cache that ensures the mostly recently accessed keys are returned with
    36  // a ttl beyond which keys are forcibly expired.
    37  type LRUExpireCache struct {
    38  	// clock is used to obtain the current time
    39  	clock Clock
    40  
    41  	lock sync.Mutex
    42  
    43  	maxSize      int
    44  	evictionList list.List
    45  	entries      map[interface{}]*list.Element
    46  }
    47  
    48  // NewLRUExpireCache creates an expiring cache with the given size
    49  func NewLRUExpireCache(maxSize int) *LRUExpireCache {
    50  	return NewLRUExpireCacheWithClock(maxSize, realClock{})
    51  }
    52  
    53  // NewLRUExpireCacheWithClock creates an expiring cache with the given size, using the specified clock to obtain the current time.
    54  func NewLRUExpireCacheWithClock(maxSize int, clock Clock) *LRUExpireCache {
    55  	if maxSize <= 0 {
    56  		panic("maxSize must be > 0")
    57  	}
    58  
    59  	return &LRUExpireCache{
    60  		clock:   clock,
    61  		maxSize: maxSize,
    62  		entries: map[interface{}]*list.Element{},
    63  	}
    64  }
    65  
    66  type cacheEntry struct {
    67  	key        interface{}
    68  	value      interface{}
    69  	expireTime time.Time
    70  }
    71  
    72  // Add adds the value to the cache at key with the specified maximum duration.
    73  func (c *LRUExpireCache) Add(key interface{}, value interface{}, ttl time.Duration) {
    74  	c.lock.Lock()
    75  	defer c.lock.Unlock()
    76  
    77  	// Key already exists
    78  	oldElement, ok := c.entries[key]
    79  	if ok {
    80  		c.evictionList.MoveToFront(oldElement)
    81  		oldElement.Value.(*cacheEntry).value = value
    82  		oldElement.Value.(*cacheEntry).expireTime = c.clock.Now().Add(ttl)
    83  		return
    84  	}
    85  
    86  	// Make space if necessary
    87  	if c.evictionList.Len() >= c.maxSize {
    88  		toEvict := c.evictionList.Back()
    89  		c.evictionList.Remove(toEvict)
    90  		delete(c.entries, toEvict.Value.(*cacheEntry).key)
    91  	}
    92  
    93  	// Add new entry
    94  	entry := &cacheEntry{
    95  		key:        key,
    96  		value:      value,
    97  		expireTime: c.clock.Now().Add(ttl),
    98  	}
    99  	element := c.evictionList.PushFront(entry)
   100  	c.entries[key] = element
   101  }
   102  
   103  // Get returns the value at the specified key from the cache if it exists and is not
   104  // expired, or returns false.
   105  func (c *LRUExpireCache) Get(key interface{}) (interface{}, bool) {
   106  	c.lock.Lock()
   107  	defer c.lock.Unlock()
   108  
   109  	element, ok := c.entries[key]
   110  	if !ok {
   111  		return nil, false
   112  	}
   113  
   114  	if c.clock.Now().After(element.Value.(*cacheEntry).expireTime) {
   115  		c.evictionList.Remove(element)
   116  		delete(c.entries, key)
   117  		return nil, false
   118  	}
   119  
   120  	c.evictionList.MoveToFront(element)
   121  
   122  	return element.Value.(*cacheEntry).value, true
   123  }
   124  
   125  // Remove removes the specified key from the cache if it exists
   126  func (c *LRUExpireCache) Remove(key interface{}) {
   127  	c.lock.Lock()
   128  	defer c.lock.Unlock()
   129  
   130  	element, ok := c.entries[key]
   131  	if !ok {
   132  		return
   133  	}
   134  
   135  	c.evictionList.Remove(element)
   136  	delete(c.entries, key)
   137  }
   138  
   139  // Keys returns all unexpired keys in the cache.
   140  //
   141  // Keep in mind that subsequent calls to Get() for any of the returned keys
   142  // might return "not found".
   143  //
   144  // Keys are returned ordered from least recently used to most recently used.
   145  func (c *LRUExpireCache) Keys() []interface{} {
   146  	c.lock.Lock()
   147  	defer c.lock.Unlock()
   148  
   149  	now := c.clock.Now()
   150  
   151  	val := make([]interface{}, 0, c.evictionList.Len())
   152  	for element := c.evictionList.Back(); element != nil; element = element.Prev() {
   153  		// Only return unexpired keys
   154  		if !now.After(element.Value.(*cacheEntry).expireTime) {
   155  			val = append(val, element.Value.(*cacheEntry).key)
   156  		}
   157  	}
   158  
   159  	return val
   160  }