Files
cgrates/cache2go/cache.go
2014-09-03 19:17:54 +03:00

207 lines
4.0 KiB
Go

//Simple caching library with expiration capabilities
package cache2go
import (
"errors"
"strings"
"sync"
"time"
)
const (
PREFIX_LEN = 4
KIND_ADD = "ADD"
KIND_REM = "REM"
KIND_PRF = "PRF"
)
type timestampedValue struct {
timestamp time.Time
value interface{}
}
type transactionItem struct {
key string
value interface{}
kind string
}
var (
cache = make(map[string]timestampedValue)
mux sync.RWMutex
counters = make(map[string]int64)
// transaction stuff
transactionBuffer []transactionItem
transactionMux sync.Mutex
transactionON = false
transactionLock = false
)
func BeginTransaction() {
transactionMux.Lock()
transactionLock = true
transactionON = true
}
func RollbackTransaction() {
transactionBuffer = nil
transactionLock = false
transactionON = false
transactionMux.Unlock()
}
func CommitTransaction() {
transactionON = false
// apply all transactioned items
mux.Lock()
for _, item := range transactionBuffer {
switch item.kind {
case KIND_REM:
RemKey(item.key)
case KIND_PRF:
RemPrefixKey(item.key)
case KIND_ADD:
Cache(item.key, item.value)
}
}
mux.Unlock()
transactionBuffer = nil
transactionLock = false
transactionMux.Unlock()
}
// The function to be used to cache a key/value pair when expiration is not needed
func Cache(key string, value interface{}) {
if !transactionLock {
mux.Lock()
defer mux.Unlock()
}
if !transactionON {
if _, ok := cache[key]; !ok {
// only count if the key is not already there
count(key)
}
cache[key] = timestampedValue{time.Now(), value}
} else {
transactionBuffer = append(transactionBuffer, transactionItem{key: key, value: value, kind: KIND_ADD})
}
}
// The function to extract a value for a key that never expire
func GetCached(key string) (v interface{}, err error) {
mux.RLock()
defer mux.RUnlock()
if r, ok := cache[key]; ok {
return r.value, nil
}
return nil, errors.New("not found")
}
func GetKeyAge(key string) (time.Duration, error) {
mux.RLock()
defer mux.RUnlock()
if r, ok := cache[key]; ok {
return time.Since(r.timestamp), nil
}
return 0, errors.New("not found")
}
func RemKey(key string) {
if !transactionLock {
mux.Lock()
defer mux.Unlock()
}
if !transactionON {
if _, ok := cache[key]; ok {
delete(cache, key)
descount(key)
}
} else {
transactionBuffer = append(transactionBuffer, transactionItem{key: key, kind: KIND_REM})
}
}
func RemPrefixKey(prefix string) {
if !transactionLock {
mux.Lock()
defer mux.Unlock()
}
for key, _ := range cache {
if !transactionON {
if strings.HasPrefix(key, prefix) {
delete(cache, key)
descount(key)
}
}
}
if transactionON {
transactionBuffer = append(transactionBuffer, transactionItem{key: prefix, kind: KIND_PRF})
}
}
func GetAllEntries(prefix string) map[string]interface{} {
mux.RLock()
defer mux.RUnlock()
result := make(map[string]interface{})
for key, timestampedValue := range cache {
if strings.HasPrefix(key, prefix) {
result[key] = timestampedValue.value
}
}
return result
}
// Delete all keys from cache
func Flush() {
mux.Lock()
defer mux.Unlock()
cache = make(map[string]timestampedValue)
counters = make(map[string]int64)
}
func CountEntries(prefix string) (result int64) {
mux.RLock()
defer mux.RUnlock()
if _, ok := counters[prefix]; ok {
return counters[prefix]
}
return 0
}
// increments the counter for the specified key prefix
func count(key string) {
if len(key) < PREFIX_LEN {
return
}
prefix := key[:PREFIX_LEN]
if _, ok := counters[prefix]; ok {
// increase the value
counters[prefix] += 1
} else {
counters[prefix] = 1
}
}
// decrements the counter for the specified key prefix
func descount(key string) {
if len(key) < PREFIX_LEN {
return
}
prefix := key[:PREFIX_LEN]
if value, ok := counters[prefix]; ok && value > 0 {
counters[prefix] -= 1
}
}
func GetEntriesKeys(prefix string) (keys []string) {
mux.RLock()
defer mux.RUnlock()
for key, _ := range cache {
if strings.HasPrefix(key, prefix) {
keys = append(keys, key)
}
}
return
}