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  }