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  }