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  }