mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
with: #1000275887
Signed-off-by: clinx <chenlin1@oppo.com>
(cherry picked from commit 25b958ca13)
182 lines
4.5 KiB
Go
182 lines
4.5 KiB
Go
package cachengine
|
|
|
|
import (
|
|
"container/list"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/cubefs/cubefs/proto"
|
|
"github.com/cubefs/cubefs/util/log"
|
|
)
|
|
|
|
const (
|
|
// MinMissCacheEvictNum is used in the foreground eviction.
|
|
// When clearing the miss keys from the cache, it stops as soon as 10 inodes have been evicted.
|
|
MinMissCacheEvictNum = 10
|
|
// MaxMissCacheEvictNum is used in the background. We can evict 200000 inodes at max.
|
|
MaxMissCacheEvictNum = 100000
|
|
|
|
BgEvictionInterval = 1 * time.Minute
|
|
)
|
|
|
|
// MissCache defines the structure of the missed key cache.
|
|
type MissCache struct {
|
|
sync.RWMutex
|
|
cache map[string]*list.Element
|
|
lruList *list.List
|
|
expiration time.Duration
|
|
maxElements int
|
|
}
|
|
|
|
func NewMissCache(exp time.Duration, maxElements int) *MissCache {
|
|
mc := &MissCache{
|
|
cache: make(map[string]*list.Element),
|
|
lruList: list.New(),
|
|
expiration: exp,
|
|
maxElements: maxElements,
|
|
}
|
|
go mc.backgroundEviction()
|
|
return mc
|
|
}
|
|
|
|
func (mc *MissCache) Increment(key string) int32 {
|
|
mc.Lock()
|
|
defer mc.Unlock()
|
|
e, ok := mc.cache[key]
|
|
if !ok {
|
|
cme := &proto.CacheMissEntry{
|
|
UniKey: key,
|
|
MissCount: 1,
|
|
}
|
|
if mc.lruList.Len() >= mc.maxElements {
|
|
mc.evict(true)
|
|
}
|
|
proto.SetMissEntryExpiration(cme, mc.expiration)
|
|
element := mc.lruList.PushFront(cme)
|
|
mc.cache[cme.UniKey] = element
|
|
if log.EnableDebug() {
|
|
log.LogDebugf("MissCache put miss cache: unikey(%v) expire(%v)",
|
|
cme.UniKey, proto.GetMissEntryExpiration(cme))
|
|
}
|
|
return 1
|
|
}
|
|
|
|
info := e.Value.(*proto.CacheMissEntry)
|
|
if proto.CacheMissExpired(info) {
|
|
mc.lruList.Remove(e)
|
|
delete(mc.cache, info.UniKey)
|
|
cme := &proto.CacheMissEntry{
|
|
UniKey: key,
|
|
MissCount: 1,
|
|
}
|
|
if mc.lruList.Len() >= mc.maxElements {
|
|
mc.evict(true)
|
|
}
|
|
proto.SetMissEntryExpiration(cme, mc.expiration)
|
|
|
|
element := mc.lruList.PushFront(cme)
|
|
mc.cache[cme.UniKey] = element
|
|
return 1
|
|
}
|
|
return atomic.AddInt32(&info.MissCount, 1)
|
|
}
|
|
|
|
func (mc *MissCache) Get(uniKey string) *proto.CacheMissEntry {
|
|
mc.RLock()
|
|
element, ok := mc.cache[uniKey]
|
|
if !ok {
|
|
mc.RUnlock()
|
|
log.LogDebugf("Miss Cache not found %v", uniKey)
|
|
return nil
|
|
}
|
|
|
|
info := element.Value.(*proto.CacheMissEntry)
|
|
if proto.CacheMissExpired(info) {
|
|
mc.RUnlock()
|
|
log.LogDebugf("Miss Cache %v expired", info)
|
|
return nil
|
|
}
|
|
mc.RUnlock()
|
|
return info
|
|
}
|
|
|
|
func (mc *MissCache) backgroundEviction() {
|
|
t := time.NewTicker(BgEvictionInterval)
|
|
defer t.Stop()
|
|
|
|
for range t.C {
|
|
log.LogInfof("MissCache: start BG evict")
|
|
start := time.Now()
|
|
mc.Lock()
|
|
mc.evict(false)
|
|
mc.Unlock()
|
|
elapsed := time.Since(start)
|
|
log.LogInfof("MissCache: total miss cache(%d), cost(%d)ns", mc.lruList.Len(), elapsed.Nanoseconds())
|
|
}
|
|
}
|
|
|
|
func (mc *MissCache) Delete(uniKey string) {
|
|
if log.EnableDebug() {
|
|
log.LogDebugf("MissCache Delete: key(%v)", uniKey)
|
|
}
|
|
mc.Lock()
|
|
element, ok := mc.cache[uniKey]
|
|
if ok {
|
|
mc.lruList.Remove(element)
|
|
delete(mc.cache, uniKey)
|
|
}
|
|
mc.Unlock()
|
|
}
|
|
|
|
// Foreground eviction cares more about the speed.
|
|
// Background eviction evicts all expired items from the cache.
|
|
// The caller should grab the WRITE lock of the inode cache.
|
|
func (mc *MissCache) evict(foreground bool) {
|
|
var count int
|
|
|
|
for i := 0; i < MinMissCacheEvictNum; i++ {
|
|
element := mc.lruList.Back()
|
|
if element == nil {
|
|
return
|
|
}
|
|
|
|
// For background eviction, if all expired items have been evicted, just return
|
|
// But for foreground eviction, we need to evict at least MinInodeCacheEvictNum inodes.
|
|
// The foreground eviction, does not need to care if the inode has expired or not.
|
|
info := element.Value.(*proto.CacheMissEntry)
|
|
log.LogDebugf("MissCache check miss hot key(%v)", info.UniKey)
|
|
if !foreground && !proto.CacheMissExpired(info) {
|
|
log.LogDebugf("MissCache check hot key(%v) expired(%v)",
|
|
info.UniKey, proto.CacheMissExpired(info))
|
|
return
|
|
}
|
|
|
|
log.LogDebugf("MissCache remove hot key(%v)", info.UniKey)
|
|
mc.lruList.Remove(element)
|
|
delete(mc.cache, info.UniKey)
|
|
count++
|
|
}
|
|
// For background eviction, we need to continue evict all expired items from the cache
|
|
if foreground {
|
|
return
|
|
}
|
|
|
|
for i := 0; i < MaxMissCacheEvictNum; i++ {
|
|
element := mc.lruList.Back()
|
|
if element == nil {
|
|
break
|
|
}
|
|
info := element.Value.(*proto.CacheMissEntry)
|
|
if !proto.CacheMissExpired(info) {
|
|
log.LogDebugf("MissCache check hot key(%v) expired(%v)",
|
|
info.UniKey, proto.CacheMissExpired(info))
|
|
break
|
|
}
|
|
log.LogDebugf("MissCache remove hot key(%v)", info.UniKey)
|
|
mc.lruList.Remove(element)
|
|
delete(mc.cache, info.UniKey)
|
|
count++
|
|
}
|
|
}
|