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  }