github.com/mjibson/goon@v1.1.0/cache.go (about)

     1  /*
     2   * Copyright (c) 2012 The Goon Authors
     3   *
     4   * Permission to use, copy, modify, and distribute this software for any
     5   * purpose with or without fee is hereby granted, provided that the above
     6   * copyright notice and this permission notice appear in all copies.
     7   *
     8   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     9   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    10   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    11   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    12   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    13   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    14   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    15   */
    16  
    17  package goon
    18  
    19  import (
    20  	"container/list"
    21  	"reflect"
    22  	"sync"
    23  )
    24  
    25  var cachedValueOverhead int
    26  
    27  func init() {
    28  	// Calculate the platform dependant overhead size for keeping a value in cache
    29  	var elem list.Element
    30  	var ci cacheItem
    31  	cachedValueOverhead += int(reflect.TypeOf(elem).Size())   // list.Element in cache.accessed
    32  	cachedValueOverhead += int(reflect.TypeOf(&elem).Size())  // *list.Element in cache.elements
    33  	cachedValueOverhead += int(reflect.TypeOf(ci.key).Size()) // string in cache.elements as key
    34  	cachedValueOverhead += int(reflect.TypeOf(ci).Size())     // cacheItem pointed to by list.Element.Value
    35  	// In addition to the above overhead, the total cache value size must include
    36  	// the length of the key string in bytes and the cap of the value []byte
    37  }
    38  
    39  type cacheItem struct {
    40  	key   string
    41  	value []byte
    42  }
    43  
    44  type cache struct {
    45  	lock     sync.Mutex
    46  	elements map[string]*list.Element // access via key
    47  	accessed list.List                // most recently accessed in front
    48  	size     int                      // Total size of all the values in the cache
    49  	limit    int                      // Maximum size allowed
    50  }
    51  
    52  const defaultCacheLimit = 16 << 20 // 16 MiB
    53  
    54  func newCache(limit int) *cache {
    55  	return &cache{elements: map[string]*list.Element{}, limit: limit}
    56  }
    57  
    58  func (c *cache) setLimit(limit int) {
    59  	c.lock.Lock()
    60  	c.limit = limit
    61  	c.meetLimitUnderLock()
    62  	c.lock.Unlock()
    63  }
    64  
    65  // meetLimit must be called under cache.lock
    66  func (c *cache) meetLimitUnderLock() {
    67  	for c.size > c.limit {
    68  		e := c.accessed.Back()
    69  		if e == nil {
    70  			break
    71  		}
    72  		c.deleteExistingUnderLock(e)
    73  	}
    74  }
    75  
    76  // setUnderLock must be called under cache.lock
    77  func (c *cache) setUnderLock(item *cacheItem) {
    78  	// Check if there's already an entry for this key
    79  	if e, ok := c.elements[item.key]; ok {
    80  		// There already exists a value for this key, so update it
    81  		ci := e.Value.(*cacheItem)
    82  		c.size += cap(item.value) - cap(ci.value)
    83  		// Make sure that item.key is the same pointer as the map key,
    84  		// as this will ensure faster map lookup via pointer equality.
    85  		// Not doing so would also lead to double memory usage, as the two key
    86  		// pointers would be pointing to the same contents in different places.
    87  		item.key = ci.key
    88  		e.Value = item
    89  		c.accessed.MoveToFront(e)
    90  	} else {
    91  		// Brand new key, so add it
    92  		c.size += cachedValueOverhead + len(item.key) + cap(item.value)
    93  		c.elements[item.key] = c.accessed.PushFront(item)
    94  	}
    95  }
    96  
    97  // Set takes ownership of item and treats it as immutable
    98  func (c *cache) Set(item *cacheItem) {
    99  	c.lock.Lock()
   100  	c.setUnderLock(item)
   101  	c.meetLimitUnderLock()
   102  	c.lock.Unlock()
   103  }
   104  
   105  // SetMulti takes ownership of the individual items and treats them as immutable
   106  func (c *cache) SetMulti(items []*cacheItem) {
   107  	c.lock.Lock()
   108  	for _, item := range items {
   109  		c.setUnderLock(item)
   110  	}
   111  	c.meetLimitUnderLock()
   112  	c.lock.Unlock()
   113  }
   114  
   115  // deleteExistingUnderLock must be called under cache.lock
   116  // The specified element must be non-nil and be guaranteed to exist in the cache
   117  func (c *cache) deleteExistingUnderLock(e *list.Element) {
   118  	ci := e.Value.(*cacheItem)
   119  	c.size -= cachedValueOverhead + len(ci.key) + cap(ci.value)
   120  	delete(c.elements, ci.key)
   121  	c.accessed.Remove(e)
   122  }
   123  
   124  // deleteUnderLock must be called under cache.lock
   125  func (c *cache) deleteUnderLock(key string) {
   126  	if e, ok := c.elements[key]; ok {
   127  		c.deleteExistingUnderLock(e)
   128  	}
   129  }
   130  
   131  func (c *cache) Delete(key string) {
   132  	c.lock.Lock()
   133  	c.deleteUnderLock(key)
   134  	c.lock.Unlock()
   135  }
   136  
   137  func (c *cache) DeleteMulti(keys []string) {
   138  	c.lock.Lock()
   139  	for _, key := range keys {
   140  		c.deleteUnderLock(key)
   141  	}
   142  	c.lock.Unlock()
   143  }
   144  
   145  // getUnderLock must be called under cache.lock
   146  func (c *cache) getUnderLock(key string) []byte {
   147  	if e, ok := c.elements[key]; ok {
   148  		c.accessed.MoveToFront(e)
   149  		return (e.Value.(*cacheItem)).value
   150  	}
   151  	return nil
   152  }
   153  
   154  // The cache retains ownership of the []byte, so consider it immutable
   155  func (c *cache) Get(key string) []byte {
   156  	c.lock.Lock()
   157  	result := c.getUnderLock(key)
   158  	c.lock.Unlock()
   159  	return result
   160  }
   161  
   162  // The cache retains ownership of the []byte, so consider it immutable
   163  func (c *cache) GetMulti(keys []string) [][]byte {
   164  	c.lock.Lock()
   165  	result := make([][]byte, 0, len(keys))
   166  	for _, key := range keys {
   167  		result = append(result, c.getUnderLock(key))
   168  	}
   169  	c.lock.Unlock()
   170  	return result
   171  }
   172  
   173  func (c *cache) Flush() {
   174  	c.lock.Lock()
   175  	c.size = 0
   176  	c.elements = map[string]*list.Element{}
   177  	c.accessed.Init()
   178  	c.lock.Unlock()
   179  }