github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/cache/memo.go (about) 1 package cache 2 3 import ( 4 "encoding/gob" 5 "fmt" 6 "os" 7 "sync" 8 "time" 9 10 "github.com/cloudreve/Cloudreve/v3/pkg/util" 11 ) 12 13 // MemoStore 内存存储驱动 14 type MemoStore struct { 15 Store *sync.Map 16 } 17 18 // item 存储的对象 19 type itemWithTTL struct { 20 Expires int64 21 Value interface{} 22 } 23 24 const DefaultCacheFile = "cache_persist.bin" 25 26 func newItem(value interface{}, expires int) itemWithTTL { 27 expires64 := int64(expires) 28 if expires > 0 { 29 expires64 = time.Now().Unix() + expires64 30 } 31 return itemWithTTL{ 32 Value: value, 33 Expires: expires64, 34 } 35 } 36 37 // getValue 从itemWithTTL中取值 38 func getValue(item interface{}, ok bool) (interface{}, bool) { 39 if !ok { 40 return nil, ok 41 } 42 43 var itemObj itemWithTTL 44 if itemObj, ok = item.(itemWithTTL); !ok { 45 return item, true 46 } 47 48 if itemObj.Expires > 0 && itemObj.Expires < time.Now().Unix() { 49 return nil, false 50 } 51 52 return itemObj.Value, ok 53 54 } 55 56 // GarbageCollect 回收已过期的缓存 57 func (store *MemoStore) GarbageCollect() { 58 store.Store.Range(func(key, value interface{}) bool { 59 if item, ok := value.(itemWithTTL); ok { 60 if item.Expires > 0 && item.Expires < time.Now().Unix() { 61 util.Log().Debug("Cache %q is garbage collected.", key.(string)) 62 store.Store.Delete(key) 63 } 64 } 65 return true 66 }) 67 } 68 69 // NewMemoStore 新建内存存储 70 func NewMemoStore() *MemoStore { 71 return &MemoStore{ 72 Store: &sync.Map{}, 73 } 74 } 75 76 // Set 存储值 77 func (store *MemoStore) Set(key string, value interface{}, ttl int) error { 78 store.Store.Store(key, newItem(value, ttl)) 79 return nil 80 } 81 82 // Get 取值 83 func (store *MemoStore) Get(key string) (interface{}, bool) { 84 return getValue(store.Store.Load(key)) 85 } 86 87 // Gets 批量取值 88 func (store *MemoStore) Gets(keys []string, prefix string) (map[string]interface{}, []string) { 89 var res = make(map[string]interface{}) 90 var notFound = make([]string, 0, len(keys)) 91 92 for _, key := range keys { 93 if value, ok := getValue(store.Store.Load(prefix + key)); ok { 94 res[key] = value 95 } else { 96 notFound = append(notFound, key) 97 } 98 } 99 100 return res, notFound 101 } 102 103 // Sets 批量设置值 104 func (store *MemoStore) Sets(values map[string]interface{}, prefix string) error { 105 for key, value := range values { 106 store.Store.Store(prefix+key, newItem(value, 0)) 107 } 108 return nil 109 } 110 111 // Delete 批量删除值 112 func (store *MemoStore) Delete(keys []string, prefix string) error { 113 for _, key := range keys { 114 store.Store.Delete(prefix + key) 115 } 116 return nil 117 } 118 119 // Persist write memory store into cache 120 func (store *MemoStore) Persist(path string) error { 121 persisted := make(map[string]itemWithTTL) 122 store.Store.Range(func(key, value interface{}) bool { 123 v, ok := store.Store.Load(key) 124 if _, ok := getValue(v, ok); ok { 125 persisted[key.(string)] = v.(itemWithTTL) 126 } 127 128 return true 129 }) 130 131 res, err := serializer(persisted) 132 if err != nil { 133 return fmt.Errorf("failed to serialize cache: %s", err) 134 } 135 136 err = os.WriteFile(path, res, 0644) 137 return err 138 } 139 140 // Restore memory cache from disk file 141 func (store *MemoStore) Restore(path string) error { 142 if !util.Exists(path) { 143 return nil 144 } 145 146 f, err := os.Open(path) 147 if err != nil { 148 return fmt.Errorf("failed to read cache file: %s", err) 149 } 150 151 defer func() { 152 f.Close() 153 os.Remove(path) 154 }() 155 156 persisted := &item{} 157 dec := gob.NewDecoder(f) 158 if err := dec.Decode(&persisted); err != nil { 159 return fmt.Errorf("unknown cache file format: %s", err) 160 } 161 162 items := persisted.Value.(map[string]itemWithTTL) 163 loaded := 0 164 for k, v := range items { 165 if _, ok := getValue(v, true); ok { 166 loaded++ 167 store.Store.Store(k, v) 168 } else { 169 util.Log().Debug("Persisted cache %q is expired.", k) 170 } 171 } 172 173 util.Log().Info("Restored %d items from %q into memory cache.", loaded, path) 174 return nil 175 }