github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/cache/lfucache/lfucache.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 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 lfucache 16 17 import ( 18 "runtime/debug" 19 "sync" 20 "sync/atomic" 21 "time" 22 23 "github.com/zuoyebang/bitalosdb/internal/base" 24 "github.com/zuoyebang/bitalosdb/internal/cache" 25 "github.com/zuoyebang/bitalosdb/internal/hash" 26 "github.com/zuoyebang/bitalosdb/internal/options" 27 ) 28 29 const ( 30 compationWorkerNum int = 4 31 runCompactInterval int = 120 32 ) 33 34 var _ cache.ICache = (*LfuCache)(nil) 35 36 func New(opts *options.CacheOptions) cache.ICache { 37 return NewLfuCache(opts) 38 } 39 40 type LfuCache struct { 41 maxSize uint64 42 shardNum uint32 43 launchTime int64 44 closed atomic.Bool 45 shards []*shard 46 logger base.Logger 47 workerClosed chan struct{} 48 workerNum int 49 workerWg sync.WaitGroup 50 workerParams []chan *workerParam 51 } 52 53 func NewLfuCache(opts *options.CacheOptions) *LfuCache { 54 size := uint64(opts.Size) 55 shardNum := uint32(opts.Shards) 56 maxSize := size / uint64(shardNum) 57 memSize := int(maxSize) / 2 58 59 mc := &LfuCache{ 60 logger: opts.Logger, 61 maxSize: size, 62 shardNum: shardNum, 63 launchTime: time.Now().Unix(), 64 shards: make([]*shard, shardNum), 65 workerNum: compationWorkerNum, 66 workerClosed: make(chan struct{}), 67 } 68 69 for i := range mc.shards { 70 mc.shards[i] = newCache(mc, i, memSize, maxSize) 71 } 72 73 mc.runCompactionTask() 74 return mc 75 } 76 77 func (lfc *LfuCache) bucketByHash(khash uint32) *shard { 78 if lfc.shardNum == 1 { 79 return lfc.shards[0] 80 } 81 return lfc.shards[khash%lfc.shardNum] 82 } 83 84 func (lfc *LfuCache) ExistAndDelete(key []byte, khash uint32) error { 85 sd := lfc.bucketByHash(khash) 86 if sd.exist(key) { 87 return sd.delete(key) 88 } 89 90 return nil 91 } 92 93 func (lfc *LfuCache) Set(key, value []byte, khash uint32) error { 94 return lfc.bucketByHash(khash).set(key, value) 95 } 96 97 func (lfc *LfuCache) Get(key []byte, khash uint32) ([]byte, func(), bool) { 98 return lfc.bucketByHash(khash).get(key) 99 } 100 101 func (lfc *LfuCache) GetKeyHash(key []byte) uint32 { 102 return hash.Crc32(key) 103 } 104 105 func (lfc *LfuCache) Delete(key []byte, khash uint32) error { 106 return lfc.bucketByHash(khash).delete(key) 107 } 108 109 func (lfc *LfuCache) Close() { 110 lfc.closed.Store(true) 111 lfc.closeCompactionTask() 112 113 for i := range lfc.shards { 114 lfc.shards[i].close() 115 } 116 117 lfc.logger.Infof("lfucache closed") 118 } 119 120 func (lfc *LfuCache) isClosed() bool { 121 return lfc.closed.Load() 122 } 123 124 func (lfc *LfuCache) runCompactionTask() { 125 lfc.workerParams = make([]chan *workerParam, lfc.workerNum) 126 127 for i := 0; i < lfc.workerNum; i++ { 128 lfc.workerParams[i] = make(chan *workerParam) 129 go lfc.runCompactionWorker(i) 130 } 131 132 lfc.workerWg.Add(1) 133 go func() { 134 defer lfc.workerWg.Done() 135 136 interval := time.Duration(runCompactInterval) 137 jobId := 0 138 ticker := time.NewTicker(interval * time.Second) 139 defer ticker.Stop() 140 141 for { 142 select { 143 case <-lfc.workerClosed: 144 return 145 case <-ticker.C: 146 if lfc.isClosed() { 147 return 148 } 149 150 jobId++ 151 lfc.logger.Infof("lfucache run compact task start scheduleCompact:%d", jobId) 152 lfc.runCompaction(compactTypeMemFlush) 153 lfc.logger.Infof("lfucache run compact task end scheduleCompact:%d", jobId) 154 } 155 } 156 }() 157 } 158 159 func (lfc *LfuCache) closeCompactionTask() { 160 close(lfc.workerClosed) 161 lfc.runCompaction(compactTypeClosed) 162 for i := 0; i < lfc.workerNum; i++ { 163 close(lfc.workerParams[i]) 164 } 165 lfc.workerWg.Wait() 166 } 167 168 type workerParam struct { 169 sid int 170 ctype int 171 wg *sync.WaitGroup 172 } 173 174 func (lfc *LfuCache) runCompactionWorker(workId int) { 175 lfc.workerWg.Add(1) 176 go func(wid int) { 177 defer func() { 178 lfc.workerWg.Done() 179 if r := recover(); r != nil { 180 lfc.logger.Errorf("lfucache compaction worker run panic err:%v panic:%s", r, string(debug.Stack())) 181 lfc.runCompactionWorker(wid) 182 } 183 }() 184 185 for { 186 p, ok := <-lfc.workerParams[wid] 187 if !ok { 188 return 189 } 190 lfc.shards[p.sid].compact(p.ctype) 191 p.wg.Done() 192 } 193 }(workId) 194 } 195 196 func (lfc *LfuCache) runCompaction(ctype int) { 197 wg := &sync.WaitGroup{} 198 199 for i := range lfc.shards { 200 wid := i % lfc.workerNum 201 p := &workerParam{ 202 sid: i, 203 ctype: ctype, 204 wg: wg, 205 } 206 wg.Add(1) 207 lfc.workerParams[wid] <- p 208 } 209 210 wg.Wait() 211 }