code.gitea.io/gitea@v1.19.3/modules/cache/cache_twoqueue.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package cache 5 6 import ( 7 "strconv" 8 "sync" 9 "time" 10 11 "code.gitea.io/gitea/modules/json" 12 13 mc "gitea.com/go-chi/cache" 14 lru "github.com/hashicorp/golang-lru" 15 ) 16 17 // TwoQueueCache represents a LRU 2Q cache adapter implementation 18 type TwoQueueCache struct { 19 lock sync.Mutex 20 cache *lru.TwoQueueCache 21 interval int 22 } 23 24 // TwoQueueCacheConfig describes the configuration for TwoQueueCache 25 type TwoQueueCacheConfig struct { 26 Size int `ini:"SIZE" json:"size"` 27 RecentRatio float64 `ini:"RECENT_RATIO" json:"recent_ratio"` 28 GhostRatio float64 `ini:"GHOST_RATIO" json:"ghost_ratio"` 29 } 30 31 // MemoryItem represents a memory cache item. 32 type MemoryItem struct { 33 Val interface{} 34 Created int64 35 Timeout int64 36 } 37 38 func (item *MemoryItem) hasExpired() bool { 39 return item.Timeout > 0 && 40 (time.Now().Unix()-item.Created) >= item.Timeout 41 } 42 43 var _ mc.Cache = &TwoQueueCache{} 44 45 // Put puts value into cache with key and expire time. 46 func (c *TwoQueueCache) Put(key string, val interface{}, timeout int64) error { 47 item := &MemoryItem{ 48 Val: val, 49 Created: time.Now().Unix(), 50 Timeout: timeout, 51 } 52 c.lock.Lock() 53 defer c.lock.Unlock() 54 c.cache.Add(key, item) 55 return nil 56 } 57 58 // Get gets cached value by given key. 59 func (c *TwoQueueCache) Get(key string) interface{} { 60 c.lock.Lock() 61 defer c.lock.Unlock() 62 cached, ok := c.cache.Get(key) 63 if !ok { 64 return nil 65 } 66 item, ok := cached.(*MemoryItem) 67 68 if !ok || item.hasExpired() { 69 c.cache.Remove(key) 70 return nil 71 } 72 73 return item.Val 74 } 75 76 // Delete deletes cached value by given key. 77 func (c *TwoQueueCache) Delete(key string) error { 78 c.lock.Lock() 79 defer c.lock.Unlock() 80 c.cache.Remove(key) 81 return nil 82 } 83 84 // Incr increases cached int-type value by given key as a counter. 85 func (c *TwoQueueCache) Incr(key string) error { 86 c.lock.Lock() 87 defer c.lock.Unlock() 88 cached, ok := c.cache.Get(key) 89 if !ok { 90 return nil 91 } 92 item, ok := cached.(*MemoryItem) 93 94 if !ok || item.hasExpired() { 95 c.cache.Remove(key) 96 return nil 97 } 98 99 var err error 100 item.Val, err = mc.Incr(item.Val) 101 return err 102 } 103 104 // Decr decreases cached int-type value by given key as a counter. 105 func (c *TwoQueueCache) Decr(key string) error { 106 c.lock.Lock() 107 defer c.lock.Unlock() 108 cached, ok := c.cache.Get(key) 109 if !ok { 110 return nil 111 } 112 item, ok := cached.(*MemoryItem) 113 114 if !ok || item.hasExpired() { 115 c.cache.Remove(key) 116 return nil 117 } 118 119 var err error 120 item.Val, err = mc.Decr(item.Val) 121 return err 122 } 123 124 // IsExist returns true if cached value exists. 125 func (c *TwoQueueCache) IsExist(key string) bool { 126 c.lock.Lock() 127 defer c.lock.Unlock() 128 cached, ok := c.cache.Peek(key) 129 if !ok { 130 return false 131 } 132 item, ok := cached.(*MemoryItem) 133 if !ok || item.hasExpired() { 134 c.cache.Remove(key) 135 return false 136 } 137 138 return true 139 } 140 141 // Flush deletes all cached data. 142 func (c *TwoQueueCache) Flush() error { 143 c.lock.Lock() 144 defer c.lock.Unlock() 145 c.cache.Purge() 146 return nil 147 } 148 149 func (c *TwoQueueCache) checkAndInvalidate(key interface{}) { 150 c.lock.Lock() 151 defer c.lock.Unlock() 152 cached, ok := c.cache.Peek(key) 153 if !ok { 154 return 155 } 156 item, ok := cached.(*MemoryItem) 157 if !ok || item.hasExpired() { 158 c.cache.Remove(item) 159 } 160 } 161 162 func (c *TwoQueueCache) startGC() { 163 if c.interval < 0 { 164 return 165 } 166 for _, key := range c.cache.Keys() { 167 c.checkAndInvalidate(key) 168 } 169 time.AfterFunc(time.Duration(c.interval)*time.Second, c.startGC) 170 } 171 172 // StartAndGC starts GC routine based on config string settings. 173 func (c *TwoQueueCache) StartAndGC(opts mc.Options) error { 174 var err error 175 size := 50000 176 if opts.AdapterConfig != "" { 177 size, err = strconv.Atoi(opts.AdapterConfig) 178 } 179 if err != nil { 180 if !json.Valid([]byte(opts.AdapterConfig)) { 181 return err 182 } 183 184 cfg := &TwoQueueCacheConfig{ 185 Size: 50000, 186 RecentRatio: lru.Default2QRecentRatio, 187 GhostRatio: lru.Default2QGhostEntries, 188 } 189 _ = json.Unmarshal([]byte(opts.AdapterConfig), cfg) 190 c.cache, err = lru.New2QParams(cfg.Size, cfg.RecentRatio, cfg.GhostRatio) 191 } else { 192 c.cache, err = lru.New2Q(size) 193 } 194 c.interval = opts.Interval 195 if c.interval > 0 { 196 go c.startGC() 197 } 198 return err 199 } 200 201 // Ping tests if the cache is alive. 202 func (c *TwoQueueCache) Ping() error { 203 return mc.GenericPing(c) 204 } 205 206 func init() { 207 mc.Register("twoqueue", &TwoQueueCache{}) 208 }