github.com/status-im/status-go@v1.1.0/services/wallet/balance/ttl_cache.go (about) 1 package balance 2 3 import ( 4 "context" 5 "math/big" 6 "time" 7 8 "github.com/jellydator/ttlcache/v3" 9 10 "github.com/ethereum/go-ethereum/log" 11 ) 12 13 var ( 14 defaultTTLValue = 5 * time.Minute 15 ) 16 17 func NewCacherWithTTL(ttl time.Duration) Cacher { 18 return newCacherImpl(newCacheWithTTL(ttl)) 19 } 20 21 // TTL cache implementation of cacheIface 22 type ttlCache[K comparable, V any] struct { 23 cache *ttlcache.Cache[K, V] 24 } 25 26 //nolint:golint,unused // linter does not detect using it via reflect 27 func (c *ttlCache[K, V]) get(key K) V { 28 item := c.cache.Get(key) 29 if item == nil { 30 var v V 31 return v 32 } 33 return item.Value() 34 } 35 36 //nolint:golint,unused // linter does not detect using it via reflect 37 func (c *ttlCache[K, V]) set(key K, value V) { 38 _ = c.cache.Set(key, value, ttlcache.DefaultTTL) 39 } 40 41 //nolint:golint,unused // linter does not detect using it via reflect 42 func (c *ttlCache[K, V]) len() int { 43 return c.cache.Len() 44 } 45 46 //nolint:golint,unused // linter does not detect using it via reflect 47 func (c *ttlCache[K, V]) keys() []K { 48 return c.cache.Keys() 49 } 50 51 //nolint:golint,unused // linter does not detect using it via reflect 52 func (c *ttlCache[K, V]) init() { 53 c.cache = ttlcache.New[K, V]( 54 ttlcache.WithTTL[K, V](defaultTTLValue), 55 ) 56 c.cache.OnEviction(func(ctx context.Context, reason ttlcache.EvictionReason, item *ttlcache.Item[K, V]) { 57 log.Debug("Evicting item from balance/nonce cache", "reason", reason, "key", item.Key, "value", item.Value) 58 }) 59 go c.cache.Start() // starts automatic expired item deletion 60 } 61 62 //nolint:golint,unused // linter does not detect using it via reflect 63 func (c *ttlCache[K, V]) clear() { 64 c.cache.DeleteAll() 65 } 66 67 // specializes generic cache 68 type cacheWithTTL struct { 69 // TODO: use ttlCache instead of mapCache for nonceRangeCache. For that we need to update sortedRanges on item eviction 70 // For now, nonceRanges cache is not updated on nonces items eviction, but it should not be as big as nonceCache is 71 genericCache[*ttlCache[uint64, *big.Int], *ttlCache[uint64, *int64], *mapCache[int64, nonceRange]] 72 } 73 74 func newCacheWithTTL(ttl time.Duration) *cacheWithTTL { 75 defaultTTLValue = ttl 76 77 return &cacheWithTTL{ 78 genericCache: genericCache[*ttlCache[uint64, *big.Int], *ttlCache[uint64, *int64], *mapCache[int64, nonceRange]]{ 79 balances: make(addressChainMap[*ttlCache[uint64, *big.Int]]), 80 nonces: make(addressChainMap[*ttlCache[uint64, *int64]]), 81 nonceRangeCache: *newNonceRangeCache[*mapCache[int64, nonceRange]](), 82 }, 83 } 84 } 85 86 func (c *cacheWithTTL) Clear() { 87 c.rw.Lock() 88 defer c.rw.Unlock() 89 90 // TTL cache removes expired items automatically 91 // but in case we want to clear it manually we can do it here 92 for _, chainCache := range c.balances { 93 for _, cache := range chainCache { 94 cache.clear() 95 } 96 } 97 98 for _, chainCache := range c.nonces { 99 for _, cache := range chainCache { 100 cache.clear() 101 } 102 } 103 104 c.nonceRangeCache.clear() 105 }