xorm/cache.go

178 lines
3.6 KiB
Go

package xorm
import (
"container/list"
//"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"sync"
)
type CacheStore interface {
Put(key, value interface{}) error
Get(key interface{}) (interface{}, error)
Del(key interface{}) error
}
type MemoryStore struct {
store map[interface{}]interface{}
mutex sync.Mutex
}
func NewMemoryStore() *MemoryStore {
return &MemoryStore{store: make(map[interface{}]interface{})}
}
func (s *MemoryStore) Put(key, value interface{}) error {
s.mutex.Lock()
defer s.mutex.Unlock()
s.store[key] = value
//fmt.Println("after put store:", s.store)
return nil
}
func (s *MemoryStore) Get(key interface{}) (interface{}, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
//fmt.Println("before get store:", s.store)
if v, ok := s.store[key]; ok {
return v, nil
}
return nil, ErrNotExist
}
func (s *MemoryStore) Del(key interface{}) error {
s.mutex.Lock()
defer s.mutex.Unlock()
//fmt.Println("before del store:", s.store)
delete(s.store, key)
//fmt.Println("after del store:", s.store)
return nil
}
type Cacher interface {
Get(id interface{}) interface{}
Put(id, obj interface{})
Del(id interface{})
}
// LRUCacher implements Cacher according to LRU algorithm
type LRUCacher struct {
name string
list *list.List
index map[interface{}]*list.Element
store CacheStore
Max int
mutex sync.RWMutex
}
func NewLRUCacher(store CacheStore, max int) *LRUCacher {
return &LRUCacher{store: store, list: list.New(),
index: make(map[interface{}]*list.Element), Max: max}
}
func (m *LRUCacher) Get(id interface{}) interface{} {
m.mutex.RLock()
defer m.mutex.RUnlock()
if v, err := m.store.Get(id); err == nil {
el := m.index[id]
m.list.MoveToBack(el)
return v
}
return nil
}
func (m *LRUCacher) Put(id interface{}, obj interface{}) {
m.mutex.Lock()
defer m.mutex.Unlock()
el := m.list.PushBack(id)
m.index[id] = el
m.store.Put(id, obj)
if m.list.Len() > m.Max {
e := m.list.Front()
m.store.Del(e.Value)
delete(m.index, e.Value)
m.list.Remove(e)
}
}
func (m *LRUCacher) Del(id interface{}) {
m.mutex.Lock()
defer m.mutex.Unlock()
if el, ok := m.index[id]; ok {
m.store.Del(id)
delete(m.index, el.Value)
m.list.Remove(el)
}
}
func encodeIds(ids []int64) (s string) {
s = "["
for _, id := range ids {
s += fmt.Sprintf("%v,", id)
}
s = s[:len(s)-1] + "]"
return
}
func decodeIds(s string) []int64 {
res := make([]int64, 0)
if len(s) >= 2 {
ss := strings.Split(s[1:len(s)-1], ",")
for _, s := range ss {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return res
}
res = append(res, i)
}
}
return res
}
func getCacheSql(m Cacher, sql string, args ...interface{}) ([]int64, error) {
bytes := m.Get(genSqlKey(sql, args))
if bytes == nil {
return nil, errors.New("Not Exist")
}
objs := decodeIds(bytes.(string))
return objs, nil
}
func putCacheSql(m Cacher, ids []int64, sql string, args ...interface{}) error {
bytes := encodeIds(ids)
m.Put(genSqlKey(sql, args), bytes)
return nil
}
func delCacheSql(m Cacher, sql string, args ...interface{}) error {
m.Del(genSqlKey(sql, args))
return nil
}
func genSqlKey(sql string, args ...interface{}) string {
return fmt.Sprintf("%v-%v", sql, args)
}
func genId(prefix string, id int64) string {
return fmt.Sprintf("%v-%v", prefix, id)
}
func getCacheId(m Cacher, prefix string, id int64) interface{} {
return m.Get(genId(prefix, id))
}
func putCacheId(m Cacher, prefix string, id int64, bean interface{}) error {
m.Put(genId(prefix, id), bean)
return nil
}
func delCacheId(m Cacher, prefix string, id int64) error {
m.Del(genId(prefix, id))
//TODO: should delete id from select
return nil
}