github.com/astaxie/beego@v1.12.3/cache/memory.go (about)

     1  // Copyright 2014 beego Author. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cache
    16  
    17  import (
    18  	"encoding/json"
    19  	"errors"
    20  	"sync"
    21  	"time"
    22  )
    23  
    24  var (
    25  	// DefaultEvery means the clock time of recycling the expired cache items in memory.
    26  	DefaultEvery = 60 // 1 minute
    27  )
    28  
    29  // MemoryItem store memory cache item.
    30  type MemoryItem struct {
    31  	val         interface{}
    32  	createdTime time.Time
    33  	lifespan    time.Duration
    34  }
    35  
    36  func (mi *MemoryItem) isExpire() bool {
    37  	// 0 means forever
    38  	if mi.lifespan == 0 {
    39  		return false
    40  	}
    41  	return time.Now().Sub(mi.createdTime) > mi.lifespan
    42  }
    43  
    44  // MemoryCache is Memory cache adapter.
    45  // it contains a RW locker for safe map storage.
    46  type MemoryCache struct {
    47  	sync.RWMutex
    48  	dur   time.Duration
    49  	items map[string]*MemoryItem
    50  	Every int // run an expiration check Every clock time
    51  }
    52  
    53  // NewMemoryCache returns a new MemoryCache.
    54  func NewMemoryCache() Cache {
    55  	cache := MemoryCache{items: make(map[string]*MemoryItem)}
    56  	return &cache
    57  }
    58  
    59  // Get cache from memory.
    60  // if non-existed or expired, return nil.
    61  func (bc *MemoryCache) Get(name string) interface{} {
    62  	bc.RLock()
    63  	defer bc.RUnlock()
    64  	if itm, ok := bc.items[name]; ok {
    65  		if itm.isExpire() {
    66  			return nil
    67  		}
    68  		return itm.val
    69  	}
    70  	return nil
    71  }
    72  
    73  // GetMulti gets caches from memory.
    74  // if non-existed or expired, return nil.
    75  func (bc *MemoryCache) GetMulti(names []string) []interface{} {
    76  	var rc []interface{}
    77  	for _, name := range names {
    78  		rc = append(rc, bc.Get(name))
    79  	}
    80  	return rc
    81  }
    82  
    83  // Put cache to memory.
    84  // if lifespan is 0, it will be forever till restart.
    85  func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error {
    86  	bc.Lock()
    87  	defer bc.Unlock()
    88  	bc.items[name] = &MemoryItem{
    89  		val:         value,
    90  		createdTime: time.Now(),
    91  		lifespan:    lifespan,
    92  	}
    93  	return nil
    94  }
    95  
    96  // Delete cache in memory.
    97  func (bc *MemoryCache) Delete(name string) error {
    98  	bc.Lock()
    99  	defer bc.Unlock()
   100  	if _, ok := bc.items[name]; !ok {
   101  		return errors.New("key not exist")
   102  	}
   103  	delete(bc.items, name)
   104  	if _, ok := bc.items[name]; ok {
   105  		return errors.New("delete key error")
   106  	}
   107  	return nil
   108  }
   109  
   110  // Incr increase cache counter in memory.
   111  // it supports int,int32,int64,uint,uint32,uint64.
   112  func (bc *MemoryCache) Incr(key string) error {
   113  	bc.Lock()
   114  	defer bc.Unlock()
   115  	itm, ok := bc.items[key]
   116  	if !ok {
   117  		return errors.New("key not exist")
   118  	}
   119  	switch val := itm.val.(type) {
   120  	case int:
   121  		itm.val = val + 1
   122  	case int32:
   123  		itm.val = val + 1
   124  	case int64:
   125  		itm.val = val + 1
   126  	case uint:
   127  		itm.val = val + 1
   128  	case uint32:
   129  		itm.val = val + 1
   130  	case uint64:
   131  		itm.val = val + 1
   132  	default:
   133  		return errors.New("item val is not (u)int (u)int32 (u)int64")
   134  	}
   135  	return nil
   136  }
   137  
   138  // Decr decrease counter in memory.
   139  func (bc *MemoryCache) Decr(key string) error {
   140  	bc.Lock()
   141  	defer bc.Unlock()
   142  	itm, ok := bc.items[key]
   143  	if !ok {
   144  		return errors.New("key not exist")
   145  	}
   146  	switch val := itm.val.(type) {
   147  	case int:
   148  		itm.val = val - 1
   149  	case int64:
   150  		itm.val = val - 1
   151  	case int32:
   152  		itm.val = val - 1
   153  	case uint:
   154  		if val > 0 {
   155  			itm.val = val - 1
   156  		} else {
   157  			return errors.New("item val is less than 0")
   158  		}
   159  	case uint32:
   160  		if val > 0 {
   161  			itm.val = val - 1
   162  		} else {
   163  			return errors.New("item val is less than 0")
   164  		}
   165  	case uint64:
   166  		if val > 0 {
   167  			itm.val = val - 1
   168  		} else {
   169  			return errors.New("item val is less than 0")
   170  		}
   171  	default:
   172  		return errors.New("item val is not int int64 int32")
   173  	}
   174  	return nil
   175  }
   176  
   177  // IsExist check cache exist in memory.
   178  func (bc *MemoryCache) IsExist(name string) bool {
   179  	bc.RLock()
   180  	defer bc.RUnlock()
   181  	if v, ok := bc.items[name]; ok {
   182  		return !v.isExpire()
   183  	}
   184  	return false
   185  }
   186  
   187  // ClearAll will delete all cache in memory.
   188  func (bc *MemoryCache) ClearAll() error {
   189  	bc.Lock()
   190  	defer bc.Unlock()
   191  	bc.items = make(map[string]*MemoryItem)
   192  	return nil
   193  }
   194  
   195  // StartAndGC start memory cache. it will check expiration in every clock time.
   196  func (bc *MemoryCache) StartAndGC(config string) error {
   197  	var cf map[string]int
   198  	json.Unmarshal([]byte(config), &cf)
   199  	if _, ok := cf["interval"]; !ok {
   200  		cf = make(map[string]int)
   201  		cf["interval"] = DefaultEvery
   202  	}
   203  	dur := time.Duration(cf["interval"]) * time.Second
   204  	bc.Every = cf["interval"]
   205  	bc.dur = dur
   206  	go bc.vacuum()
   207  	return nil
   208  }
   209  
   210  // check expiration.
   211  func (bc *MemoryCache) vacuum() {
   212  	bc.RLock()
   213  	every := bc.Every
   214  	bc.RUnlock()
   215  
   216  	if every < 1 {
   217  		return
   218  	}
   219  	for {
   220  		<-time.After(bc.dur)
   221  		bc.RLock()
   222  		if bc.items == nil {
   223  			bc.RUnlock()
   224  			return
   225  		}
   226  		bc.RUnlock()
   227  		if keys := bc.expiredKeys(); len(keys) != 0 {
   228  			bc.clearItems(keys)
   229  		}
   230  	}
   231  }
   232  
   233  // expiredKeys returns key list which are expired.
   234  func (bc *MemoryCache) expiredKeys() (keys []string) {
   235  	bc.RLock()
   236  	defer bc.RUnlock()
   237  	for key, itm := range bc.items {
   238  		if itm.isExpire() {
   239  			keys = append(keys, key)
   240  		}
   241  	}
   242  	return
   243  }
   244  
   245  // clearItems removes all the items which key in keys.
   246  func (bc *MemoryCache) clearItems(keys []string) {
   247  	bc.Lock()
   248  	defer bc.Unlock()
   249  	for _, key := range keys {
   250  		delete(bc.items, key)
   251  	}
   252  }
   253  
   254  func init() {
   255  	Register("memory", NewMemoryCache)
   256  }