dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/servicediscovery/store/cache_manager.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package store 19 20 import ( 21 "encoding/gob" 22 "os" 23 "sync" 24 "time" 25 ) 26 27 import ( 28 "github.com/dubbogo/gost/log/logger" 29 30 "github.com/hashicorp/golang-lru" 31 ) 32 33 type CacheManager struct { 34 name string // The name of the cache manager 35 cacheFile string // The file path where the cache is stored 36 dumpInterval time.Duration // The duration after which the cache dump 37 stop chan struct{} // Channel used to stop the cache expiration routine 38 cache *lru.Cache // The LRU cache implementation 39 lock sync.Mutex 40 enableDump bool 41 } 42 43 type Item struct { 44 Key string 45 Value interface{} 46 } 47 48 // NewCacheManager creates a new CacheManager instance. 49 // It initializes the cache manager with the provided parameters and starts a routine for cache dumping. 50 func NewCacheManager(name, cacheFile string, dumpInterval time.Duration, maxCacheSize int, enableDump bool) (*CacheManager, error) { 51 cm := &CacheManager{ 52 name: name, 53 cacheFile: cacheFile, 54 dumpInterval: dumpInterval, 55 stop: make(chan struct{}), 56 enableDump: enableDump, 57 } 58 cache, err := lru.New(maxCacheSize) 59 if err != nil { 60 return nil, err 61 } 62 cm.cache = cache 63 64 // Check if the cache file exists and load the cache if it does 65 if _, err := os.Stat(cacheFile); err == nil { 66 if err = cm.loadCache(); err != nil { 67 logger.Warnf("Failed to load the cache file:[%s].The err is %v", cm.cacheFile, err) 68 } 69 } 70 71 if enableDump { 72 cm.runDumpTask() 73 } 74 75 return cm, nil 76 } 77 78 // Get retrieves the value associated with the given key from the cache. 79 func (cm *CacheManager) Get(key string) (interface{}, bool) { 80 return cm.cache.Get(key) 81 } 82 83 // Set sets the value associated with the given key in the cache. 84 func (cm *CacheManager) Set(key string, value interface{}) { 85 cm.cache.Add(key, value) 86 } 87 88 // Delete removes the value associated with the given key from the cache. 89 func (cm *CacheManager) Delete(key string) { 90 cm.cache.Remove(key) 91 } 92 93 // GetAll returns all the key-value pairs in the cache. 94 func (cm *CacheManager) GetAll() map[string]interface{} { 95 keys := cm.cache.Keys() 96 97 result := make(map[string]interface{}) 98 for _, k := range keys { 99 result[k.(string)], _ = cm.cache.Get(k) 100 } 101 102 return result 103 } 104 105 // loadCache loads the cache from the cache file. 106 func (cm *CacheManager) loadCache() error { 107 cf, err := os.Open(cm.cacheFile) 108 if err != nil { 109 return err 110 } 111 112 decoder := gob.NewDecoder(cf) 113 for { 114 var it Item 115 err = decoder.Decode(&it) 116 if err != nil { 117 if err.Error() == "EOF" { 118 break // Reached end of file 119 } 120 return err 121 } 122 // Add the loaded keys to the front of the LRU list 123 cm.cache.Add(it.Key, it.Value) 124 } 125 126 return cf.Close() 127 } 128 129 // dumpCache dumps the cache to the cache file. 130 func (cm *CacheManager) dumpCache() error { 131 132 cm.lock.Lock() 133 defer cm.lock.Unlock() 134 135 items := cm.GetAll() 136 137 file, err := os.Create(cm.cacheFile) 138 if err != nil { 139 return err 140 } 141 defer file.Close() 142 143 encoder := gob.NewEncoder(file) 144 for k, v := range items { 145 gob.Register(v) 146 err = encoder.Encode(&Item{ 147 Key: k, 148 Value: v, 149 }) 150 if err != nil { 151 return err 152 } 153 } 154 return nil 155 } 156 157 func (cm *CacheManager) runDumpTask() { 158 go func() { 159 ticker := time.NewTicker(cm.dumpInterval) 160 for { 161 select { 162 case <-ticker.C: 163 // Dump the cache to the file 164 if err := cm.dumpCache(); err != nil { 165 // Handle error 166 logger.Warnf("Failed to dump cache,the err is %v", err) 167 } else { 168 logger.Infof("Dumping [%s] caches, latest entries %d", cm.name, cm.cache.Len()) 169 } 170 case <-cm.stop: 171 ticker.Stop() 172 return 173 } 174 } 175 }() 176 } 177 178 func (cm *CacheManager) StopDump() { 179 cm.lock.Lock() 180 defer cm.lock.Unlock() 181 if cm.enableDump { 182 cm.stop <- struct{}{} // Stop the cache dump routine 183 cm.enableDump = false 184 } 185 } 186 187 // destroy stops the cache dump routine, clears the cache and removes the cache file. 188 func (cm *CacheManager) destroy() { 189 cm.StopDump() // Stop the cache dump routine 190 cm.cache.Purge() // Clear the cache 191 192 // Delete the cache file if it exists 193 if _, err := os.Stat(cm.cacheFile); err == nil { 194 if err := os.Remove(cm.cacheFile); err == nil { 195 logger.Infof("The cacheFile [%s] was cleared", cm.cacheFile) 196 } 197 } 198 }