github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/utils/cache/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"go.uber.org/zap"
    10  )
    11  
    12  // ExpirationNotifier is a function which will be called every time a cache
    13  // expires an item
    14  type ExpirationNotifier func(id interface{}, item interface{})
    15  
    16  // DataStore is the interface to a datastore.
    17  type DataStore interface {
    18  	Add(u interface{}, value interface{}) (err error)
    19  	AddOrUpdate(u interface{}, value interface{}) bool
    20  	Get(u interface{}) (i interface{}, err error)
    21  	GetReset(u interface{}, duration time.Duration) (interface{}, error)
    22  	Remove(u interface{}) (err error)
    23  	RemoveWithDelay(u interface{}, duration time.Duration) (err error)
    24  	LockedModify(u interface{}, add func(a, b interface{}) interface{}, increment interface{}) (interface{}, error)
    25  	SetTimeOut(u interface{}, timeout time.Duration) (err error)
    26  	KeyList() []interface{}
    27  	ToString() string
    28  }
    29  
    30  // Cache is the structure that involves the map of entries. The cache
    31  // provides a sync mechanism and allows multiple clients at the same time.
    32  type Cache struct {
    33  	name     string
    34  	data     map[interface{}]entry
    35  	lifetime time.Duration
    36  	sync.RWMutex
    37  	expirer ExpirationNotifier
    38  	max     int
    39  }
    40  
    41  // entry is a single line in the datastore that includes the actual entry
    42  // and the time that entry was created or updated
    43  type entry struct {
    44  	value     interface{}
    45  	timestamp time.Time
    46  	timer     *time.Timer
    47  	expirer   ExpirationNotifier
    48  }
    49  
    50  // cacheRegistry keeps handles of all caches initialized through this library
    51  // for book keeping
    52  type cacheRegistry struct {
    53  	sync.RWMutex
    54  	items map[string]*Cache
    55  }
    56  
    57  var registry *cacheRegistry
    58  
    59  func init() {
    60  
    61  	registry = &cacheRegistry{
    62  		items: make(map[string]*Cache),
    63  	}
    64  }
    65  
    66  // Add adds a cache to a registry
    67  func (r *cacheRegistry) Add(c *Cache) {
    68  	r.Lock()
    69  	defer r.Unlock()
    70  
    71  	r.items[c.name] = c
    72  }
    73  
    74  // ToString generates information about all caches initialized through this lib
    75  func (r *cacheRegistry) ToString() string {
    76  	r.Lock()
    77  	defer r.Unlock()
    78  
    79  	buffer := fmt.Sprintf("Cache Registry: %d\n", len(r.items))
    80  	buffer += fmt.Sprintf(" %32s : %s\n\n", "Cache Name", "max/curr")
    81  	for k, c := range r.items {
    82  		buffer += fmt.Sprintf(" %32s : %s\n", k, c.ToString())
    83  	}
    84  	return buffer
    85  }
    86  
    87  // NewCache creates a new data cache
    88  func NewCache(name string) *Cache {
    89  
    90  	return NewCacheWithExpirationNotifier(name, -1, nil)
    91  }
    92  
    93  // NewCacheWithExpiration creates a new data cache
    94  func NewCacheWithExpiration(name string, lifetime time.Duration) *Cache {
    95  
    96  	return NewCacheWithExpirationNotifier(name, lifetime, nil)
    97  }
    98  
    99  // NewCacheWithExpirationNotifier creates a new data cache with notifier
   100  func NewCacheWithExpirationNotifier(name string, lifetime time.Duration, expirer ExpirationNotifier) *Cache {
   101  
   102  	c := &Cache{
   103  		name:     name,
   104  		data:     make(map[interface{}]entry),
   105  		lifetime: lifetime,
   106  		expirer:  expirer,
   107  	}
   108  	c.max = len(c.data)
   109  	registry.Add(c)
   110  	return c
   111  }
   112  
   113  // ToString generates information about all caches initialized through this lib
   114  func ToString() string {
   115  
   116  	return registry.ToString()
   117  }
   118  
   119  // ToString provides statistics about this cache
   120  func (c *Cache) ToString() string {
   121  	c.Lock()
   122  	defer c.Unlock()
   123  
   124  	return fmt.Sprintf("%d/%d", c.max, len(c.data))
   125  }
   126  
   127  // Add stores an entry into the cache and updates the timestamp
   128  func (c *Cache) Add(u interface{}, value interface{}) (err error) {
   129  
   130  	var timer *time.Timer
   131  	if c.lifetime != -1 {
   132  		timer = time.AfterFunc(c.lifetime, func() {
   133  			if err := c.removeNotify(u, true); err != nil {
   134  				zap.L().Warn("Failed to remove item", zap.String("key", fmt.Sprintf("%v", u)))
   135  			}
   136  		})
   137  	}
   138  
   139  	t := time.Now()
   140  
   141  	c.Lock()
   142  	defer c.Unlock()
   143  
   144  	if _, ok := c.data[u]; !ok {
   145  
   146  		c.data[u] = entry{
   147  			value:     value,
   148  			timestamp: t,
   149  			timer:     timer,
   150  			expirer:   c.expirer,
   151  		}
   152  		if len(c.data) > c.max {
   153  			c.max = len(c.data)
   154  		}
   155  		return nil
   156  	}
   157  
   158  	return errors.New("item exists: use update")
   159  }
   160  
   161  // GetReset  changes the value of an entry into the cache and updates the timestamp
   162  func (c *Cache) GetReset(u interface{}, duration time.Duration) (interface{}, error) {
   163  
   164  	c.Lock()
   165  	defer c.Unlock()
   166  
   167  	if line, ok := c.data[u]; ok {
   168  
   169  		if c.lifetime != -1 && line.timer != nil {
   170  			if duration > 0 {
   171  				line.timer.Reset(duration)
   172  			} else {
   173  				line.timer.Reset(c.lifetime)
   174  			}
   175  		}
   176  
   177  		return line.value, nil
   178  	}
   179  
   180  	return nil, errors.New("cannot read item: not found")
   181  }
   182  
   183  // Update changes the value of an entry into the cache and updates the timestamp
   184  func (c *Cache) Update(u interface{}, value interface{}) (err error) {
   185  
   186  	var timer *time.Timer
   187  	if c.lifetime != -1 {
   188  		timer = time.AfterFunc(c.lifetime, func() {
   189  			if err := c.removeNotify(u, true); err != nil {
   190  				zap.L().Warn("Failed to remove item", zap.String("key", fmt.Sprintf("%v", u)))
   191  			}
   192  		})
   193  	}
   194  
   195  	t := time.Now()
   196  
   197  	c.Lock()
   198  	defer c.Unlock()
   199  
   200  	if _, ok := c.data[u]; ok {
   201  
   202  		if c.data[u].timer != nil {
   203  			c.data[u].timer.Stop()
   204  		}
   205  
   206  		c.data[u] = entry{
   207  			value:     value,
   208  			timestamp: t,
   209  			timer:     timer,
   210  			expirer:   c.expirer,
   211  		}
   212  
   213  		return nil
   214  	}
   215  
   216  	return errors.New("cannot update item: not found")
   217  }
   218  
   219  // AddOrUpdate adds a new value in the cache or updates the existing value
   220  // if needed. If an update happens the timestamp is also updated.
   221  // Returns true if key was updated.
   222  func (c *Cache) AddOrUpdate(u interface{}, value interface{}) (updated bool) {
   223  
   224  	var timer *time.Timer
   225  	if c.lifetime != -1 {
   226  		timer = time.AfterFunc(c.lifetime, func() {
   227  			if err := c.removeNotify(u, true); err != nil {
   228  				zap.L().Warn("Failed to remove item", zap.String("key", fmt.Sprintf("%v", u)))
   229  			}
   230  		})
   231  	}
   232  
   233  	t := time.Now()
   234  
   235  	c.Lock()
   236  	defer c.Unlock()
   237  
   238  	if _, updated = c.data[u]; updated {
   239  		if c.data[u].timer != nil {
   240  			c.data[u].timer.Stop()
   241  		}
   242  	}
   243  
   244  	c.data[u] = entry{
   245  		value:     value,
   246  		timestamp: t,
   247  		timer:     timer,
   248  		expirer:   c.expirer,
   249  	}
   250  	if len(c.data) > c.max {
   251  		c.max = len(c.data)
   252  	}
   253  
   254  	return updated
   255  }
   256  
   257  // SetTimeOut sets the time out of an entry to a new value
   258  func (c *Cache) SetTimeOut(u interface{}, timeout time.Duration) (err error) {
   259  	c.Lock()
   260  	defer c.Unlock()
   261  
   262  	if _, ok := c.data[u]; !ok {
   263  		return errors.New("item is already deleted")
   264  	}
   265  
   266  	c.data[u].timer.Reset(timeout)
   267  
   268  	return nil
   269  }
   270  
   271  // Get retrieves the entry from the cache
   272  func (c *Cache) Get(u interface{}) (i interface{}, err error) {
   273  
   274  	c.Lock()
   275  	defer c.Unlock()
   276  
   277  	if _, ok := c.data[u]; !ok {
   278  		return nil, errors.New("not found")
   279  	}
   280  
   281  	return c.data[u].value, nil
   282  }
   283  
   284  // KeyList returns all the keys that are currently stored in the cache.
   285  func (c *Cache) KeyList() []interface{} {
   286  	c.Lock()
   287  	defer c.Unlock()
   288  
   289  	list := []interface{}{}
   290  	for k := range c.data {
   291  		list = append(list, k)
   292  	}
   293  	return list
   294  }
   295  
   296  // removeNotify removes the entry from the cache and optionally notifies.
   297  // returns error if not there
   298  func (c *Cache) removeNotify(u interface{}, notify bool) (err error) {
   299  
   300  	c.Lock()
   301  
   302  	val, ok := c.data[u]
   303  	if !ok {
   304  		c.Unlock()
   305  		return errors.New("not found")
   306  	}
   307  
   308  	if val.timer != nil {
   309  		val.timer.Stop()
   310  	}
   311  	delete(c.data, u)
   312  	c.Unlock()
   313  
   314  	if notify && val.expirer != nil {
   315  		val.expirer(u, val.value)
   316  	}
   317  	return nil
   318  }
   319  
   320  // Remove removes the entry from the cache and returns error if not there
   321  func (c *Cache) Remove(u interface{}) (err error) {
   322  
   323  	return c.removeNotify(u, false)
   324  }
   325  
   326  // RemoveWithDelay removes the entry from the cache after a certain duration
   327  func (c *Cache) RemoveWithDelay(u interface{}, duration time.Duration) error {
   328  	if duration == -1 {
   329  		return c.Remove(u)
   330  	}
   331  
   332  	c.Lock()
   333  	defer c.Unlock()
   334  
   335  	e, ok := c.data[u]
   336  
   337  	if !ok {
   338  		return errors.New("cannot remove item with delay: not found")
   339  	}
   340  
   341  	timer := time.AfterFunc(duration, func() {
   342  		if err := c.Remove(u); err != nil {
   343  			zap.L().Warn("Failed to remove item with delay", zap.String("key", fmt.Sprintf("%v", u)), zap.String("delay", duration.String()))
   344  		}
   345  	})
   346  
   347  	t := time.Now()
   348  
   349  	if c.data[u].timer != nil {
   350  		c.data[u].timer.Stop()
   351  	}
   352  
   353  	c.data[u] = entry{
   354  		value:     e.value,
   355  		timestamp: t,
   356  		timer:     timer,
   357  		expirer:   c.expirer,
   358  	}
   359  
   360  	return nil
   361  
   362  }
   363  
   364  // SizeOf returns the number of elements in the cache
   365  func (c *Cache) SizeOf() int {
   366  
   367  	c.Lock()
   368  	defer c.Unlock()
   369  
   370  	return len(c.data)
   371  }
   372  
   373  // LockedModify locks the data store
   374  func (c *Cache) LockedModify(u interface{}, add func(a, b interface{}) interface{}, increment interface{}) (interface{}, error) {
   375  
   376  	var timer *time.Timer
   377  	if c.lifetime != -1 {
   378  		timer = time.AfterFunc(c.lifetime, func() {
   379  			if err := c.removeNotify(u, true); err != nil {
   380  				zap.L().Warn("Failed to remove item", zap.String("key", fmt.Sprintf("%v", u)))
   381  			}
   382  		})
   383  	}
   384  
   385  	t := time.Now()
   386  
   387  	c.Lock()
   388  	defer c.Unlock()
   389  
   390  	e, ok := c.data[u]
   391  	if !ok {
   392  		return nil, errors.New("not found")
   393  	}
   394  
   395  	if e.timer != nil {
   396  		e.timer.Stop()
   397  	}
   398  
   399  	e.value = add(e.value, increment)
   400  	e.timer = timer
   401  	e.timestamp = t
   402  	e.expirer = c.expirer
   403  
   404  	c.data[u] = e
   405  
   406  	return e.value, nil
   407  
   408  }