github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+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  // Caching Interface
    17  type ObjectCache interface {
    18  	AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64)
    19  	AddWithDefaultExpires(key, value interface{})
    20  	Purge()
    21  	Get(key interface{}) (value interface{}, ok bool)
    22  	Remove(key interface{})
    23  	Len() int
    24  	Name() string
    25  	GetInvalidateClusterEvent() string
    26  }
    27  
    28  // Cache is a thread-safe fixed size LRU cache.
    29  type Cache struct {
    30  	size                   int
    31  	evictList              *list.List
    32  	items                  map[interface{}]*list.Element
    33  	lock                   sync.RWMutex
    34  	name                   string
    35  	defaultExpiry          int64
    36  	invalidateClusterEvent string
    37  	currentGeneration      int64
    38  	len                    int
    39  }
    40  
    41  // entry is used to hold a value in the evictList
    42  type entry struct {
    43  	key          interface{}
    44  	value        interface{}
    45  	expireAtSecs int64
    46  	generation   int64
    47  }
    48  
    49  // New creates an LRU of the given size
    50  func NewLru(size int) *Cache {
    51  	return &Cache{
    52  		size:      size,
    53  		evictList: list.New(),
    54  		items:     make(map[interface{}]*list.Element, size),
    55  	}
    56  }
    57  
    58  func NewLruWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache {
    59  	lru := NewLru(size)
    60  	lru.name = name
    61  	lru.defaultExpiry = defaultExpiry
    62  	lru.invalidateClusterEvent = invalidateClusterEvent
    63  	return lru
    64  }
    65  
    66  // Purge is used to completely clear the cache
    67  func (c *Cache) Purge() {
    68  	c.lock.Lock()
    69  	defer c.lock.Unlock()
    70  
    71  	c.len = 0
    72  	c.currentGeneration++
    73  }
    74  
    75  func (c *Cache) Add(key, value interface{}) {
    76  	c.AddWithExpiresInSecs(key, value, 0)
    77  }
    78  
    79  func (c *Cache) AddWithDefaultExpires(key, value interface{}) {
    80  	c.AddWithExpiresInSecs(key, value, c.defaultExpiry)
    81  }
    82  
    83  func (c *Cache) AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) {
    84  	c.lock.Lock()
    85  	defer c.lock.Unlock()
    86  
    87  	if expireAtSecs > 0 {
    88  		expireAtSecs = (time.Now().UnixNano() / int64(time.Second)) + expireAtSecs
    89  	}
    90  
    91  	// Check for existing item
    92  	if ent, ok := c.items[key]; ok {
    93  		c.evictList.MoveToFront(ent)
    94  		e := ent.Value.(*entry)
    95  		e.value = value
    96  		e.expireAtSecs = expireAtSecs
    97  		if e.generation != c.currentGeneration {
    98  			e.generation = c.currentGeneration
    99  			c.len++
   100  		}
   101  		return
   102  	}
   103  
   104  	// Add new item
   105  	ent := &entry{key, value, expireAtSecs, c.currentGeneration}
   106  	entry := c.evictList.PushFront(ent)
   107  	c.items[key] = entry
   108  	c.len++
   109  
   110  	if c.evictList.Len() > c.size {
   111  		c.removeElement(c.evictList.Back())
   112  	}
   113  }
   114  
   115  func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
   116  	c.lock.Lock()
   117  	defer c.lock.Unlock()
   118  
   119  	if ent, ok := c.items[key]; ok {
   120  		e := ent.Value.(*entry)
   121  
   122  		if e.generation != c.currentGeneration || (e.expireAtSecs > 0 && (time.Now().UnixNano()/int64(time.Second)) > e.expireAtSecs) {
   123  			c.removeElement(ent)
   124  			return nil, false
   125  		}
   126  
   127  		c.evictList.MoveToFront(ent)
   128  		return ent.Value.(*entry).value, true
   129  	}
   130  
   131  	return nil, false
   132  }
   133  
   134  func (c *Cache) Remove(key interface{}) {
   135  	c.lock.Lock()
   136  	defer c.lock.Unlock()
   137  
   138  	if ent, ok := c.items[key]; ok {
   139  		c.removeElement(ent)
   140  	}
   141  }
   142  
   143  // Keys returns a slice of the keys in the cache, from oldest to newest.
   144  func (c *Cache) Keys() []interface{} {
   145  	c.lock.RLock()
   146  	defer c.lock.RUnlock()
   147  
   148  	keys := make([]interface{}, c.len)
   149  	i := 0
   150  	for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
   151  		e := ent.Value.(*entry)
   152  		if e.generation == c.currentGeneration {
   153  			keys[i] = e.key
   154  			i++
   155  		}
   156  	}
   157  
   158  	return keys
   159  }
   160  
   161  // Len returns the number of items in the cache.
   162  func (c *Cache) Len() int {
   163  	c.lock.RLock()
   164  	defer c.lock.RUnlock()
   165  	return c.len
   166  }
   167  
   168  func (c *Cache) Name() string {
   169  	return c.name
   170  }
   171  
   172  func (c *Cache) GetInvalidateClusterEvent() string {
   173  	return c.invalidateClusterEvent
   174  }
   175  
   176  // removeElement is used to remove a given list element from the cache
   177  func (c *Cache) removeElement(e *list.Element) {
   178  	c.evictList.Remove(e)
   179  	kv := e.Value.(*entry)
   180  	if kv.generation == c.currentGeneration {
   181  		c.len--
   182  	}
   183  	delete(c.items, kv.key)
   184  }