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 }