github.com/TeaOSLab/EdgeNode@v1.3.8/internal/ttlcache/cache.go (about) 1 package ttlcache 2 3 import ( 4 "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" 5 memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem" 6 "runtime" 7 ) 8 9 var SharedInt64Cache = NewBigCache[int64]() 10 11 // Cache TTL缓存 12 // 最大的缓存时间为30 * 86400 13 // Piece数据结构: 14 // 15 // Piece1 | Piece2 | Piece3 | ... 16 // [ Item1, Item2, ... ] | ... 17 type Cache[T any] struct { 18 isDestroyed bool 19 pieces []*Piece[T] 20 countPieces uint64 21 maxItems int 22 23 maxPiecesPerGC int 24 gcPieceIndex int 25 } 26 27 func NewBigCache[T any]() *Cache[T] { 28 var delta = memutils.SystemMemoryGB() / 2 29 if delta <= 0 { 30 delta = 1 31 } 32 return NewCache[T](NewMaxItemsOption(delta * 1_000_000)) 33 } 34 35 func NewCache[T any](opt ...OptionInterface) *Cache[T] { 36 var countPieces = 256 37 var maxItems = 1_000_000 38 39 var totalMemory = memutils.SystemMemoryGB() 40 if totalMemory < 2 { 41 // 我们限制内存过小的服务能够使用的数量 42 maxItems = 500_000 43 } else { 44 var delta = totalMemory / 4 45 if delta > 0 { 46 maxItems *= delta 47 } 48 } 49 50 for _, option := range opt { 51 if option == nil { 52 continue 53 } 54 switch o := option.(type) { 55 case *PiecesOption: 56 if o.Count > 0 { 57 countPieces = o.Count 58 } 59 case *MaxItemsOption: 60 if o.Count > 0 { 61 maxItems = o.Count 62 } 63 } 64 } 65 66 var maxPiecesPerGC = 4 67 var numCPU = runtime.NumCPU() / 2 68 if numCPU > maxPiecesPerGC { 69 maxPiecesPerGC = numCPU 70 } 71 72 var cache = &Cache[T]{ 73 countPieces: uint64(countPieces), 74 maxItems: maxItems, 75 maxPiecesPerGC: maxPiecesPerGC, 76 } 77 78 for i := 0; i < countPieces; i++ { 79 cache.pieces = append(cache.pieces, NewPiece[T](maxItems/countPieces)) 80 } 81 82 // Add to manager 83 SharedManager.Add(cache) 84 85 return cache 86 } 87 88 func (this *Cache[T]) Write(key string, value T, expiredAt int64) (ok bool) { 89 if this.isDestroyed { 90 return 91 } 92 93 var currentTimestamp = fasttime.Now().Unix() 94 if expiredAt <= currentTimestamp { 95 return 96 } 97 98 var maxExpiredAt = currentTimestamp + 30*86400 99 if expiredAt > maxExpiredAt { 100 expiredAt = maxExpiredAt 101 } 102 var uint64Key = HashKeyString(key) 103 var pieceIndex = uint64Key % this.countPieces 104 return this.pieces[pieceIndex].Add(uint64Key, &Item[T]{ 105 Value: value, 106 expiredAt: expiredAt, 107 }) 108 } 109 110 func (this *Cache[T]) IncreaseInt64(key string, delta T, expiredAt int64, extend bool) T { 111 if this.isDestroyed { 112 return any(0).(T) 113 } 114 115 var currentTimestamp = fasttime.Now().Unix() 116 if expiredAt <= currentTimestamp { 117 return any(0).(T) 118 } 119 120 var maxExpiredAt = currentTimestamp + 30*86400 121 if expiredAt > maxExpiredAt { 122 expiredAt = maxExpiredAt 123 } 124 var uint64Key = HashKeyString(key) 125 var pieceIndex = uint64Key % this.countPieces 126 return this.pieces[pieceIndex].IncreaseInt64(uint64Key, delta, expiredAt, extend) 127 } 128 129 func (this *Cache[T]) Read(key string) (item *Item[T]) { 130 var uint64Key = HashKeyString(key) 131 return this.pieces[uint64Key%this.countPieces].Read(uint64Key) 132 } 133 134 func (this *Cache[T]) Delete(key string) { 135 var uint64Key = HashKeyString(key) 136 this.pieces[uint64Key%this.countPieces].Delete(uint64Key) 137 } 138 139 func (this *Cache[T]) Count() (count int) { 140 for _, piece := range this.pieces { 141 count += piece.Count() 142 } 143 return 144 } 145 146 func (this *Cache[T]) GC() { 147 var index = this.gcPieceIndex 148 149 for i := index; i < index+this.maxPiecesPerGC; i++ { 150 if i >= int(this.countPieces) { 151 break 152 } 153 this.pieces[i].GC() 154 } 155 156 index += this.maxPiecesPerGC 157 if index >= int(this.countPieces) { 158 index = 0 159 } 160 this.gcPieceIndex = index 161 } 162 163 func (this *Cache[T]) Clean() { 164 for _, piece := range this.pieces { 165 piece.Clean() 166 } 167 } 168 169 func (this *Cache[T]) Destroy() { 170 SharedManager.Remove(this) 171 172 this.isDestroyed = true 173 174 for _, piece := range this.pieces { 175 piece.Destroy() 176 } 177 }