github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/services/cache/lru/lru.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  // This files was copied/modified from https://github.com/hashicorp/golang-lru
     5  // which was (see below)
     6  
     7  // This package provides a simple LRU cache. It is based on the
     8  // LRU implementation in groupcache:
     9  // https://github.com/golang/groupcache/tree/master/lru
    10  
    11  package lru
    12  
    13  import (
    14  	"container/list"
    15  	"sync"
    16  	"time"
    17  
    18  	"github.com/vnforks/kid/v5/services/cache"
    19  )
    20  
    21  // Cache is a thread-safe fixed size LRU cache.
    22  type Cache struct {
    23  	size                   int
    24  	evictList              *list.List
    25  	items                  map[string]*list.Element
    26  	lock                   sync.RWMutex
    27  	name                   string
    28  	defaultExpiry          int64
    29  	invalidateClusterEvent string
    30  	currentGeneration      int64
    31  	len                    int
    32  }
    33  
    34  // CacheProvider is an implementation of cache.Provider to create a new Lru Cache
    35  type CacheProvider struct{}
    36  
    37  // NewCache creates a new lru.Cache with given size.
    38  func (c *CacheProvider) NewCache(size int) cache.Cache {
    39  	return New(size)
    40  }
    41  
    42  // NewCacheWithParams creates a new lru.Cache with the given parameters.
    43  func (c *CacheProvider) NewCacheWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) cache.Cache {
    44  	return NewWithParams(size, name, defaultExpiry, invalidateClusterEvent)
    45  }
    46  
    47  // Connect opens a new connection to the cache using specific provider parameters.
    48  func (c *CacheProvider) Connect() {
    49  
    50  }
    51  
    52  // Close releases any resources used by the cache provider.
    53  func (c *CacheProvider) Close() {
    54  
    55  }
    56  
    57  // entry is used to hold a value in the evictList.
    58  type entry struct {
    59  	key        string
    60  	value      interface{}
    61  	expires    time.Time
    62  	generation int64
    63  }
    64  
    65  // New creates an LRU of the given size.
    66  func New(size int) *Cache {
    67  	return &Cache{
    68  		size:      size,
    69  		evictList: list.New(),
    70  		items:     make(map[string]*list.Element, size),
    71  	}
    72  }
    73  
    74  // NewWithParams creates an LRU with the given parameters.
    75  func NewWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache {
    76  	lru := New(size)
    77  	lru.name = name
    78  	lru.defaultExpiry = defaultExpiry
    79  	lru.invalidateClusterEvent = invalidateClusterEvent
    80  	return lru
    81  }
    82  
    83  // Purge is used to completely clear the cache.
    84  func (c *Cache) Purge() {
    85  	c.lock.Lock()
    86  	defer c.lock.Unlock()
    87  
    88  	c.len = 0
    89  	c.currentGeneration++
    90  }
    91  
    92  // Add adds the given key and value to the store without an expiry.
    93  func (c *Cache) Add(key string, value interface{}) {
    94  	c.AddWithExpiresInSecs(key, value, 0)
    95  }
    96  
    97  // AddWithDefaultExpires adds the given key and value to the store with the default expiry.
    98  func (c *Cache) AddWithDefaultExpires(key string, value interface{}) {
    99  	c.AddWithExpiresInSecs(key, value, c.defaultExpiry)
   100  }
   101  
   102  // AddWithExpiresInSecs adds the given key and value to the cache with the given expiry.
   103  func (c *Cache) AddWithExpiresInSecs(key string, value interface{}, expireAtSecs int64) {
   104  	c.lock.Lock()
   105  	defer c.lock.Unlock()
   106  
   107  	c.add(key, value, time.Duration(expireAtSecs)*time.Second)
   108  }
   109  
   110  func (c *Cache) add(key string, value interface{}, ttl time.Duration) {
   111  	var expires time.Time
   112  	if ttl > 0 {
   113  		expires = time.Now().Add(ttl)
   114  	}
   115  
   116  	// Check for existing item, ignoring expiry since we'd update anyway.
   117  	if ent, ok := c.items[key]; ok {
   118  		c.evictList.MoveToFront(ent)
   119  		e := ent.Value.(*entry)
   120  		e.value = value
   121  		e.expires = expires
   122  		if e.generation != c.currentGeneration {
   123  			e.generation = c.currentGeneration
   124  			c.len++
   125  		}
   126  		return
   127  	}
   128  
   129  	// Add new item
   130  	ent := &entry{key, value, expires, c.currentGeneration}
   131  	entry := c.evictList.PushFront(ent)
   132  	c.items[key] = entry
   133  	c.len++
   134  
   135  	if c.evictList.Len() > c.size {
   136  		c.removeElement(c.evictList.Back())
   137  	}
   138  }
   139  
   140  // Get returns the value stored in the cache for a key, or nil if no value is present. The ok result indicates whether value was found in the cache.
   141  func (c *Cache) Get(key string) (value interface{}, ok bool) {
   142  	c.lock.Lock()
   143  	defer c.lock.Unlock()
   144  
   145  	return c.getValue(key)
   146  }
   147  
   148  func (c *Cache) getValue(key string) (value interface{}, ok bool) {
   149  	if ent, ok := c.items[key]; ok {
   150  		e := ent.Value.(*entry)
   151  
   152  		if e.generation != c.currentGeneration || (!e.expires.IsZero() && time.Now().After(e.expires)) {
   153  			c.removeElement(ent)
   154  			return nil, false
   155  		}
   156  
   157  		c.evictList.MoveToFront(ent)
   158  		return ent.Value.(*entry).value, true
   159  	}
   160  
   161  	return nil, false
   162  }
   163  
   164  // GetOrAdd returns the existing value for the key if present. Otherwise, it stores and returns the given value. The loaded result is true if the value was loaded, false if stored.
   165  // This API intentionally deviates from the Add-only variants above for simplicity. We should simplify the entire API in the future.
   166  func (c *Cache) GetOrAdd(key string, value interface{}, ttl time.Duration) (actual interface{}, loaded bool) {
   167  	c.lock.Lock()
   168  	defer c.lock.Unlock()
   169  
   170  	// Check for existing item
   171  	if actualValue, ok := c.getValue(key); ok {
   172  		return actualValue, true
   173  	}
   174  
   175  	c.add(key, value, ttl)
   176  
   177  	return value, false
   178  }
   179  
   180  // Remove deletes the value for a key.
   181  func (c *Cache) Remove(key string) {
   182  	c.lock.Lock()
   183  	defer c.lock.Unlock()
   184  
   185  	if ent, ok := c.items[key]; ok {
   186  		c.removeElement(ent)
   187  	}
   188  }
   189  
   190  // Keys returns a slice of the keys in the cache, from oldest to newest.
   191  func (c *Cache) Keys() []string {
   192  	c.lock.RLock()
   193  	defer c.lock.RUnlock()
   194  
   195  	keys := make([]string, c.len)
   196  	i := 0
   197  	for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
   198  		e := ent.Value.(*entry)
   199  		if e.generation == c.currentGeneration {
   200  			keys[i] = e.key
   201  			i++
   202  		}
   203  	}
   204  
   205  	return keys
   206  }
   207  
   208  // Len returns the number of items in the cache.
   209  func (c *Cache) Len() int {
   210  	c.lock.RLock()
   211  	defer c.lock.RUnlock()
   212  	return c.len
   213  }
   214  
   215  // Name identifies this cache instance among others in the system.
   216  func (c *Cache) Name() string {
   217  	return c.name
   218  }
   219  
   220  // GetInvalidateClusterEvent returns the cluster event configured when this cache was created.
   221  func (c *Cache) GetInvalidateClusterEvent() string {
   222  	return c.invalidateClusterEvent
   223  }
   224  
   225  func (c *Cache) removeElement(e *list.Element) {
   226  	c.evictList.Remove(e)
   227  	kv := e.Value.(*entry)
   228  	if kv.generation == c.currentGeneration {
   229  		c.len--
   230  	}
   231  	delete(c.items, kv.key)
   232  }