123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- // Copyright 2014 beego Author. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package cache
- import (
- "encoding/json"
- "errors"
- "sync"
- "time"
- )
- var (
- // DefaultEvery means the clock time of recycling the expired cache items in memory.
- DefaultEvery = 60 // 1 minute
- )
- // MemoryItem store memory cache item.
- type MemoryItem struct {
- val interface{}
- createdTime time.Time
- lifespan time.Duration
- }
- func (mi *MemoryItem) isExpire() bool {
- // 0 means forever
- if mi.lifespan == 0 {
- return false
- }
- return time.Now().Sub(mi.createdTime) > mi.lifespan
- }
- // MemoryCache is Memory cache adapter.
- // it contains a RW locker for safe map storage.
- type MemoryCache struct {
- sync.RWMutex
- dur time.Duration
- items map[string]*MemoryItem
- Every int // run an expiration check Every clock time
- }
- // NewMemoryCache returns a new MemoryCache.
- func NewMemoryCache() Cache {
- cache := MemoryCache{items: make(map[string]*MemoryItem)}
- return &cache
- }
- // Get cache from memory.
- // if non-existed or expired, return nil.
- func (bc *MemoryCache) Get(name string) interface{} {
- bc.RLock()
- defer bc.RUnlock()
- if itm, ok := bc.items[name]; ok {
- if itm.isExpire() {
- return nil
- }
- return itm.val
- }
- return nil
- }
- // GetMulti gets caches from memory.
- // if non-existed or expired, return nil.
- func (bc *MemoryCache) GetMulti(names []string) []interface{} {
- var rc []interface{}
- for _, name := range names {
- rc = append(rc, bc.Get(name))
- }
- return rc
- }
- // Put cache to memory.
- // if lifespan is 0, it will be forever till restart.
- func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error {
- bc.Lock()
- defer bc.Unlock()
- bc.items[name] = &MemoryItem{
- val: value,
- createdTime: time.Now(),
- lifespan: lifespan,
- }
- return nil
- }
- // Delete cache in memory.
- func (bc *MemoryCache) Delete(name string) error {
- bc.Lock()
- defer bc.Unlock()
- if _, ok := bc.items[name]; !ok {
- return errors.New("key not exist")
- }
- delete(bc.items, name)
- if _, ok := bc.items[name]; ok {
- return errors.New("delete key error")
- }
- return nil
- }
- // Incr increase cache counter in memory.
- // it supports int,int32,int64,uint,uint32,uint64.
- func (bc *MemoryCache) Incr(key string) error {
- bc.RLock()
- defer bc.RUnlock()
- itm, ok := bc.items[key]
- if !ok {
- return errors.New("key not exist")
- }
- switch itm.val.(type) {
- case int:
- itm.val = itm.val.(int) + 1
- case int32:
- itm.val = itm.val.(int32) + 1
- case int64:
- itm.val = itm.val.(int64) + 1
- case uint:
- itm.val = itm.val.(uint) + 1
- case uint32:
- itm.val = itm.val.(uint32) + 1
- case uint64:
- itm.val = itm.val.(uint64) + 1
- default:
- return errors.New("item val is not (u)int (u)int32 (u)int64")
- }
- return nil
- }
- // Decr decrease counter in memory.
- func (bc *MemoryCache) Decr(key string) error {
- bc.RLock()
- defer bc.RUnlock()
- itm, ok := bc.items[key]
- if !ok {
- return errors.New("key not exist")
- }
- switch itm.val.(type) {
- case int:
- itm.val = itm.val.(int) - 1
- case int64:
- itm.val = itm.val.(int64) - 1
- case int32:
- itm.val = itm.val.(int32) - 1
- case uint:
- if itm.val.(uint) > 0 {
- itm.val = itm.val.(uint) - 1
- } else {
- return errors.New("item val is less than 0")
- }
- case uint32:
- if itm.val.(uint32) > 0 {
- itm.val = itm.val.(uint32) - 1
- } else {
- return errors.New("item val is less than 0")
- }
- case uint64:
- if itm.val.(uint64) > 0 {
- itm.val = itm.val.(uint64) - 1
- } else {
- return errors.New("item val is less than 0")
- }
- default:
- return errors.New("item val is not int int64 int32")
- }
- return nil
- }
- // IsExist check cache exist in memory.
- func (bc *MemoryCache) IsExist(name string) bool {
- bc.RLock()
- defer bc.RUnlock()
- if v, ok := bc.items[name]; ok {
- return !v.isExpire()
- }
- return false
- }
- // ClearAll will delete all cache in memory.
- func (bc *MemoryCache) ClearAll() error {
- bc.Lock()
- defer bc.Unlock()
- bc.items = make(map[string]*MemoryItem)
- return nil
- }
- // StartAndGC start memory cache. it will check expiration in every clock time.
- func (bc *MemoryCache) StartAndGC(config string) error {
- var cf map[string]int
- json.Unmarshal([]byte(config), &cf)
- if _, ok := cf["interval"]; !ok {
- cf = make(map[string]int)
- cf["interval"] = DefaultEvery
- }
- dur := time.Duration(cf["interval"]) * time.Second
- bc.Every = cf["interval"]
- bc.dur = dur
- go bc.vaccuum()
- return nil
- }
- // check expiration.
- func (bc *MemoryCache) vaccuum() {
- if bc.Every < 1 {
- return
- }
- for {
- <-time.After(bc.dur)
- if bc.items == nil {
- return
- }
- for name := range bc.items {
- bc.itemExpired(name)
- }
- }
- }
- // itemExpired returns true if an item is expired.
- func (bc *MemoryCache) itemExpired(name string) bool {
- bc.Lock()
- defer bc.Unlock()
- itm, ok := bc.items[name]
- if !ok {
- return true
- }
- if itm.isExpire() {
- delete(bc.items, name)
- return true
- }
- return false
- }
- func init() {
- Register("memory", NewMemoryCache)
- }
|