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