github.com/ashishbhate/mattermost-server@v5.11.1+incompatible/utils/lru.go (about)

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