func (c *cacheImpl) removeElement(e *list.Element) {
	c.evictList.Remove(e)
	kv := e.Value.(*cacheItem)
	delete(c.items, kv.key)
	c.stat.Evicted++
	if c.onEvicted != nil {
		c.onEvicted(kv.key, kv.value)
	}
}
removeElement is used to remove a given list element from the cache. Has to be called with lock!
package cache
import (
	"container/list"
	"fmt"
	"sync"
	"time"
	"github.com/pkg/errors"
)
type Cache interface {
	fmt.Stringer
	Set(key string, value interface{}, ttl time.Duration)
	Get(key string) (interface{}, bool)
	Peek(key string) (interface{}, bool)
	Keys() []string
	Len() int
	Invalidate(key string)
	InvalidateFn(fn func(key string) bool)
	RemoveOldest()
	DeleteExpired()
	Purge()
	Stat() Stats
}
type Stats struct {
	Hits, Misses   int  
	Added, Evicted int  
}
type cacheImpl struct {
	ttl       time.Duration
	maxKeys   int
	isLRU     bool
	onEvicted func(key string, value interface{})
	sync.Mutex
	stat      Stats
	items     map[string]*list.Element
	evictList *list.List
}
const noEvictionTTL = time.Hour * 24 * 365 * 10
func NewCache(options ...Option) (Cache, error) {
	res := cacheImpl{
		items:     map[string]*list.Element{},
		evictList: list.New(),
		ttl:       noEvictionTTL,
		maxKeys:   0,
	}
	for _, opt := range options {
		if err := opt(&res); err != nil {
			return nil, errors.Wrap(err, "failed to set cache option")
		}
	}
	return &res, nil
}
func (c *cacheImpl) Set(key string, value interface{}, ttl time.Duration) {
	c.Lock()
	defer c.Unlock()
	now := time.Now()
	if ttl == 0 {
		ttl = c.ttl
	}
	if ent, ok := c.items[key]; ok {
		c.evictList.MoveToFront(ent)
		ent.Value.(*cacheItem).value = value
		ent.Value.(*cacheItem).expiresAt = now.Add(ttl)
		return
	}
	ent := &cacheItem{key: key, value: value, expiresAt: now.Add(ttl)}
	entry := c.evictList.PushFront(ent)
	c.items[key] = entry
	c.stat.Added++
	if c.ttl != noEvictionTTL || ttl != noEvictionTTL {
		c.removeOldestIfExpired()
	}
	if c.maxKeys > 0 && len(c.items) > c.maxKeys {
		c.removeOldest()
	}
}
func (c *cacheImpl) Get(key string) (interface{}, bool) {
	c.Lock()
	defer c.Unlock()
	if ent, ok := c.items[key]; ok {
		if time.Now().After(ent.Value.(*cacheItem).expiresAt) {
			c.stat.Misses++
			return nil, false
		}
		if c.isLRU {
			c.evictList.MoveToFront(ent)
		}
		c.stat.Hits++
		return ent.Value.(*cacheItem).value, true
	}
	c.stat.Misses++
	return nil, false
}
func (c *cacheImpl) Peek(key string) (interface{}, bool) {
	c.Lock()
	defer c.Unlock()
	if ent, ok := c.items[key]; ok {
		if time.Now().After(ent.Value.(*cacheItem).expiresAt) {
			c.stat.Misses++
			return nil, false
		}
		c.stat.Hits++
		return ent.Value.(*cacheItem).value, true
	}
	c.stat.Misses++
	return nil, false
}
func (c *cacheImpl) Keys() []string {
	c.Lock()
	defer c.Unlock()
	return c.keys()
}
func (c *cacheImpl) Len() int {
	c.Lock()
	defer c.Unlock()
	return c.evictList.Len()
}
func (c *cacheImpl) Invalidate(key string) {
	c.Lock()
	defer c.Unlock()
	if ent, ok := c.items[key]; ok {
		c.removeElement(ent)
	}
}
func (c *cacheImpl) InvalidateFn(fn func(key string) bool) {
	c.Lock()
	defer c.Unlock()
	for key, ent := range c.items {
		if fn(key) {
			c.removeElement(ent)
		}
	}
}
func (c *cacheImpl) RemoveOldest() {
	c.Lock()
	defer c.Unlock()
	c.removeOldest()
}
func (c *cacheImpl) DeleteExpired() {
	c.Lock()
	defer c.Unlock()
	for _, key := range c.keys() {
		if time.Now().After(c.items[key].Value.(*cacheItem).expiresAt) {
			c.removeElement(c.items[key])
		}
	}
}
func (c *cacheImpl) Purge() {
	c.Lock()
	defer c.Unlock()
	for k, v := range c.items {
		delete(c.items, k)
		c.stat.Evicted++
		if c.onEvicted != nil {
			c.onEvicted(k, v.Value.(*cacheItem).value)
		}
	}
	c.evictList.Init()
}
func (c *cacheImpl) Stat() Stats {
	c.Lock()
	defer c.Unlock()
	return c.stat
}
func (c *cacheImpl) String() string {
	stats := c.Stat()
	size := c.Len()
	return fmt.Sprintf("Size: %d, Stats: %+v (%0.1f%%)", size, stats, 100*float64(stats.Hits)/float64(stats.Hits+stats.Misses))
}
func (c *cacheImpl) keys() []string {
	keys := make([]string, 0, len(c.items))
	for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
		keys = append(keys, ent.Value.(*cacheItem).key)
	}
	return keys
}
func (c *cacheImpl) removeOldest() {
	ent := c.evictList.Back()
	if ent != nil {
		c.removeElement(ent)
	}
}
func (c *cacheImpl) removeOldestIfExpired() {
	ent := c.evictList.Back()
	if ent != nil && time.Now().After(ent.Value.(*cacheItem).expiresAt) {
		c.removeElement(ent)
	}
}