github.com/xgzlucario/GigaCache@v0.0.0-20240508025442-54204e9c8a6b/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"slices"
     5  	"time"
     6  )
     7  
     8  const (
     9  	noTTL = 0
    10  	KB    = 1024
    11  
    12  	// maxFailCount indicates that the evict algorithm break
    13  	// when consecutive unexpired key-value pairs are detected.
    14  	maxFailCount = 3
    15  )
    16  
    17  var bpool = NewBufferPool()
    18  
    19  // GigaCache implements a key-value cache.
    20  type GigaCache struct {
    21  	mask    uint32
    22  	hashFn  HashFn
    23  	buckets []*bucket
    24  }
    25  
    26  // New returns new GigaCache instance.
    27  func New(options Options) *GigaCache {
    28  	if err := checkOptions(options); err != nil {
    29  		panic(err)
    30  	}
    31  	cache := &GigaCache{
    32  		mask:    options.ShardCount - 1,
    33  		hashFn:  options.HashFn,
    34  		buckets: make([]*bucket, options.ShardCount),
    35  	}
    36  	for i := range cache.buckets {
    37  		cache.buckets[i] = newBucket(options)
    38  	}
    39  	return cache
    40  }
    41  
    42  // getShard returns the bucket and the real key by hash(kstr).
    43  // sharding and index use different hash function, can reduce the hash conflicts greatly.
    44  func (c *GigaCache) getShard(kstr string) (*bucket, Key) {
    45  	hash := c.hashFn(kstr)
    46  	hash32 := uint32(hash >> 1)
    47  	return c.buckets[hash32&c.mask], Key(hash)
    48  }
    49  
    50  // Get returns value with expiration time by the key.
    51  func (c *GigaCache) Get(kstr string) ([]byte, int64, bool) {
    52  	b, key := c.getShard(kstr)
    53  	b.RLock()
    54  	val, ts, ok := b.get(kstr, key)
    55  	if ok {
    56  		val = slices.Clone(val)
    57  	}
    58  	b.RUnlock()
    59  	return val, ts, ok
    60  }
    61  
    62  // SetTx store key-value pair with deadline.
    63  func (c *GigaCache) SetTx(kstr string, val []byte, ts int64) {
    64  	b, key := c.getShard(kstr)
    65  	b.Lock()
    66  	b.eliminate()
    67  	b.set(key, s2b(&kstr), val, ts)
    68  	b.Unlock()
    69  }
    70  
    71  // Set store key-value pair.
    72  func (c *GigaCache) Set(kstr string, val []byte) {
    73  	c.SetTx(kstr, val, noTTL)
    74  }
    75  
    76  // SetEx store key-value pair with expired duration.
    77  func (c *GigaCache) SetEx(kstr string, val []byte, dur time.Duration) {
    78  	c.SetTx(kstr, val, GetNanoSec()+int64(dur))
    79  }
    80  
    81  // Remove removes the key-value pair by the key.
    82  func (c *GigaCache) Remove(kstr string) bool {
    83  	b, key := c.getShard(kstr)
    84  	b.Lock()
    85  	b.eliminate()
    86  	ok := b.remove(key, kstr)
    87  	b.Unlock()
    88  	return ok
    89  }
    90  
    91  // SetTTL set ttl for key.
    92  func (c *GigaCache) SetTTL(kstr string, ts int64) bool {
    93  	b, key := c.getShard(kstr)
    94  	b.Lock()
    95  	ok := b.setTTL(key, kstr, ts)
    96  	b.eliminate()
    97  	b.Unlock()
    98  	return ok
    99  }
   100  
   101  // Walker is the callback function for iterator.
   102  type Walker func(key, val []byte, ttl int64) (next bool)
   103  
   104  // Scan walk all alive key-value pairs.
   105  // DO NOT EDIT the bytes as they are NO COPY.
   106  func (c *GigaCache) Scan(f Walker) {
   107  	var next bool
   108  	for _, b := range c.buckets {
   109  		b.RLock()
   110  		next = b.scan(f)
   111  		b.RUnlock()
   112  		if !next {
   113  			return
   114  		}
   115  	}
   116  }
   117  
   118  // Migrate move all data to new buckets.
   119  func (c *GigaCache) Migrate() {
   120  	for _, b := range c.buckets {
   121  		b.Lock()
   122  		b.migrate()
   123  		b.Unlock()
   124  	}
   125  }
   126  
   127  // Stat is the runtime statistics of GigaCache.
   128  type Stat struct {
   129  	Len      int
   130  	Conflict int
   131  	Alloc    uint64
   132  	Unused   uint64
   133  	Migrates uint64
   134  	Evict    uint64
   135  	Probe    uint64
   136  }
   137  
   138  // Stat return the runtime statistics of GigaCache.
   139  func (c *GigaCache) Stat() (s Stat) {
   140  	for _, b := range c.buckets {
   141  		b.RLock()
   142  		s.Len += len(b.index) + len(b.cmap)
   143  		s.Conflict += len(b.cmap)
   144  		s.Alloc += uint64(len(b.data))
   145  		s.Unused += b.unused
   146  		s.Migrates += b.migrates
   147  		s.Evict += b.evict
   148  		s.Probe += b.probe
   149  		b.RUnlock()
   150  	}
   151  	return
   152  }
   153  
   154  func (s Stat) UnusedRate() float64 {
   155  	return float64(s.Unused) / float64(s.Alloc) * 100
   156  }
   157  
   158  func (s Stat) EvictRate() float64 {
   159  	return float64(s.Evict) / float64(s.Probe) * 100
   160  }