2019-07-26 19:09:13 +08:00
|
|
|
package cache
|
|
|
|
|
|
|
|
// Modified by https://github.com/die-net/lrucache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"container/list"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Option is part of Functional Options Pattern
|
|
|
|
type Option func(*LruCache)
|
|
|
|
|
2019-12-05 00:17:24 +08:00
|
|
|
// EvictCallback is used to get a callback when a cache entry is evicted
|
|
|
|
type EvictCallback func(key interface{}, value interface{})
|
|
|
|
|
|
|
|
// WithEvict set the evict callback
|
|
|
|
func WithEvict(cb EvictCallback) Option {
|
|
|
|
return func(l *LruCache) {
|
|
|
|
l.onEvict = cb
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-26 19:09:13 +08:00
|
|
|
// WithUpdateAgeOnGet update expires when Get element
|
|
|
|
func WithUpdateAgeOnGet() Option {
|
|
|
|
return func(l *LruCache) {
|
|
|
|
l.updateAgeOnGet = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithAge defined element max age (second)
|
|
|
|
func WithAge(maxAge int64) Option {
|
|
|
|
return func(l *LruCache) {
|
|
|
|
l.maxAge = maxAge
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithSize defined max length of LruCache
|
|
|
|
func WithSize(maxSize int) Option {
|
|
|
|
return func(l *LruCache) {
|
|
|
|
l.maxSize = maxSize
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// LruCache is a thread-safe, in-memory lru-cache that evicts the
|
|
|
|
// least recently used entries from memory when (if set) the entries are
|
|
|
|
// older than maxAge (in seconds). Use the New constructor to create one.
|
|
|
|
type LruCache struct {
|
|
|
|
maxAge int64
|
|
|
|
maxSize int
|
|
|
|
mu sync.Mutex
|
|
|
|
cache map[interface{}]*list.Element
|
|
|
|
lru *list.List // Front is least-recent
|
|
|
|
updateAgeOnGet bool
|
2019-12-05 00:17:24 +08:00
|
|
|
onEvict EvictCallback
|
2019-07-26 19:09:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewLRUCache creates an LruCache
|
|
|
|
func NewLRUCache(options ...Option) *LruCache {
|
|
|
|
lc := &LruCache{
|
|
|
|
lru: list.New(),
|
|
|
|
cache: make(map[interface{}]*list.Element),
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, option := range options {
|
|
|
|
option(lc)
|
|
|
|
}
|
|
|
|
|
|
|
|
return lc
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the interface{} representation of a cached response and a bool
|
|
|
|
// set to true if the key was found.
|
|
|
|
func (c *LruCache) Get(key interface{}) (interface{}, bool) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
le, ok := c.cache[key]
|
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.maxAge > 0 && le.Value.(*entry).expires <= time.Now().Unix() {
|
|
|
|
c.deleteElement(le)
|
|
|
|
c.maybeDeleteOldest()
|
|
|
|
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
c.lru.MoveToBack(le)
|
|
|
|
entry := le.Value.(*entry)
|
|
|
|
if c.maxAge > 0 && c.updateAgeOnGet {
|
|
|
|
entry.expires = time.Now().Unix() + c.maxAge
|
|
|
|
}
|
|
|
|
value := entry.value
|
|
|
|
|
|
|
|
return value, true
|
|
|
|
}
|
|
|
|
|
2019-10-11 14:01:16 +08:00
|
|
|
// Exist returns if key exist in cache but not put item to the head of linked list
|
|
|
|
func (c *LruCache) Exist(key interface{}) bool {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
_, ok := c.cache[key]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2019-07-26 19:09:13 +08:00
|
|
|
// Set stores the interface{} representation of a response for a given key.
|
|
|
|
func (c *LruCache) Set(key interface{}, value interface{}) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
expires := int64(0)
|
|
|
|
if c.maxAge > 0 {
|
|
|
|
expires = time.Now().Unix() + c.maxAge
|
|
|
|
}
|
|
|
|
|
|
|
|
if le, ok := c.cache[key]; ok {
|
|
|
|
c.lru.MoveToBack(le)
|
|
|
|
e := le.Value.(*entry)
|
|
|
|
e.value = value
|
|
|
|
e.expires = expires
|
|
|
|
} else {
|
|
|
|
e := &entry{key: key, value: value, expires: expires}
|
|
|
|
c.cache[key] = c.lru.PushBack(e)
|
|
|
|
|
|
|
|
if c.maxSize > 0 {
|
|
|
|
if len := c.lru.Len(); len > c.maxSize {
|
|
|
|
c.deleteElement(c.lru.Front())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.maybeDeleteOldest()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete removes the value associated with a key.
|
|
|
|
func (c *LruCache) Delete(key string) {
|
|
|
|
c.mu.Lock()
|
|
|
|
|
|
|
|
if le, ok := c.cache[key]; ok {
|
|
|
|
c.deleteElement(le)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *LruCache) maybeDeleteOldest() {
|
|
|
|
if c.maxAge > 0 {
|
|
|
|
now := time.Now().Unix()
|
|
|
|
for le := c.lru.Front(); le != nil && le.Value.(*entry).expires <= now; le = c.lru.Front() {
|
|
|
|
c.deleteElement(le)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *LruCache) deleteElement(le *list.Element) {
|
|
|
|
c.lru.Remove(le)
|
|
|
|
e := le.Value.(*entry)
|
|
|
|
delete(c.cache, e.key)
|
2019-12-05 00:17:24 +08:00
|
|
|
if c.onEvict != nil {
|
|
|
|
c.onEvict(e.key, e.value)
|
|
|
|
}
|
2019-07-26 19:09:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type entry struct {
|
|
|
|
key interface{}
|
|
|
|
value interface{}
|
|
|
|
expires int64
|
|
|
|
}
|