Package cache contains caching functions.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

263 lines
5.4 KiB

package cache
import (
"container/list"
"os"
"path/filepath"
"sync"
"time"
)
type mKey struct {
Path string
Key interface{}
Modified time.Time
}
// MemoryLRU implements a concurrent safe in-memory LRU key-value store.
type MemoryLRU struct {
size int
evict *list.List
items map[interface{}]*list.Element
}
func NewMemoryLRU(size uint) *MemoryLRU {
return &MemoryLRU{
size: int(size),
evict: list.New(),
items: make(map[interface{}]*list.Element),
}
}
// Len returns the number of items in the cache.
func (m *MemoryLRU) Len() int {
return m.evict.Len()
}
// Contains checks if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func (m *MemoryLRU) Contains(key interface{}) (ok bool, err error) {
_, ok = m.items[key]
return
}
func (m *MemoryLRU) Delete(key interface{}) (bool, error) {
if entry, ok := m.items[key]; ok {
m.evict.Remove(entry)
delete(m.items, key)
return true, nil
}
return false, nil
}
// Get an item from the cache.
func (m *MemoryLRU) Get(key interface{}) (interface{}, bool, error) {
if entry, ok := m.items[key]; ok {
m.evict.MoveToFront(entry)
return entry.Value.(*kv).Value, true, nil
}
return nil, false, nil
}
// Iter iterates over all key-value pairs, without updating the recent-ness or
// deleting items for being stale.
func (m *MemoryLRU) Iter(fn func(key, value interface{})) error {
for key, entry := range m.items {
fn(key, entry.Value.(*kv).Value)
}
return nil
}
// Set an item in the cache.
func (m *MemoryLRU) Set(key, value interface{}) error {
r, err := m.Replace(key, value)
if r || err != nil {
return err
}
// Add new item
item := &kv{key, value}
entry := m.evict.PushFront(item)
m.items[key] = entry
if m.evict.Len() > m.size {
m.removeOldest()
}
return nil
}
// Replace an item in the cache.
func (m *MemoryLRU) Replace(key, value interface{}) (bool, error) {
// Check for existing item
if entry, ok := m.items[key]; ok {
m.evict.MoveToFront(entry)
entry.Value.(*kv).Value = value
return true, nil
}
return false, nil
}
// removeOldest removes the oldest item from the cache.
func (m *MemoryLRU) removeOldest() {
ent := m.evict.Back()
if ent != nil {
m.removeElement(ent)
}
}
// removeElement is used to remove a given list element from the cache
func (m *MemoryLRU) removeElement(e *list.Element) {
m.evict.Remove(e)
entry := e.Value.(*kv)
delete(m.items, entry.Key)
}
type DiskLRU struct {
kv *DiskKV
size int
evict *list.List
items map[string]*list.Element
mutex sync.RWMutex
}
func NewDiskLRU(path string, perm os.FileMode, size uint) (d *DiskLRU, err error) {
defer func() {
if reason := recover(); reason != nil {
if perr, ok := reason.(error); ok {
err = perr
} else {
panic(reason)
}
}
}()
d = &DiskLRU{
size: int(size),
evict: list.New(),
items: make(map[string]*list.Element),
}
if d.kv, err = NewDiskKV(path, perm); err != nil {
return nil, err
}
if err = filepath.Walk(d.kv.path, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
value := new(kv)
if err = d.kv.get(path, value); err != nil {
return err
}
d.insert(&mKey{path, value.Key, info.ModTime()})
return nil
}); err != nil {
return nil, err
}
return d, nil
}
func (d *DiskLRU) Len() int {
return d.evict.Len()
}
// Contains checks if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func (d *DiskLRU) Contains(key interface{}) (ok bool, err error) {
p := d.kv.pathFor(key)
if ok, err = d.kv.contains(p); err != nil {
return
}
if ok {
_, ok = d.items[p]
}
return
}
func (d *DiskLRU) Delete(key interface{}) (bool, error) {
p := d.kv.pathFor(key)
if entry, ok := d.items[p]; ok {
d.evict.Remove(entry)
delete(d.items, p)
return d.kv.delete(p)
}
return false, nil
}
func (d *DiskLRU) Get(key interface{}) (interface{}, bool, error) {
p := d.kv.pathFor(key)
if entry, ok := d.items[p]; ok {
value := new(kv)
if err := d.kv.get(p, value); err != nil {
return nil, false, err
}
d.evict.MoveToFront(entry)
return value.Value, true, nil
}
return nil, false, nil
}
func (d *DiskLRU) Iter(fn func(key, value interface{})) error {
return d.kv.Iter(fn)
}
func (d *DiskLRU) Replace(key, value interface{}) (bool, error) {
return d.kv.Replace(key, value)
}
// Set an item in the cache.
func (d *DiskLRU) Set(key, value interface{}) error {
r, err := d.Replace(key, value)
if r || err != nil {
return err
}
// Add new item
p := d.kv.pathFor(key)
entry := d.evict.PushFront(&mKey{p, key, time.Now()})
d.items[p] = entry
if d.evict.Len() > d.size {
d.removeOldest()
}
return d.kv.set(p, key, value)
}
// insert adds a new item to the cache
func (d *DiskLRU) insert(key *mKey) {
var ent *list.Element
first := d.evict.Front()
if first == nil {
ent = d.evict.PushFront(key)
} else if first.Value.(*mKey).Modified.After(key.Modified) {
ent = d.evict.PushFront(key)
d.evict.MoveAfter(ent, first)
} else {
ent = d.evict.PushFront(key)
}
d.items[key.Path] = ent
if d.evict.Len() > d.size {
d.removeOldest()
}
}
// removeOldest removes the oldest item from the cache.
func (d *DiskLRU) removeOldest() {
ent := d.evict.Back()
if ent != nil {
d.removeElement(ent)
}
}
// removeElement is used to remove a given list element from the cache
func (d *DiskLRU) removeElement(e *list.Element) {
d.evict.Remove(e)
entry := e.Value.(*mKey)
d.kv.delete(entry.Path)
}