-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improve cache #1866
improve cache #1866
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,12 +16,13 @@ const ( | |
) | ||
|
||
type ristrettoCache struct { | ||
cache *ristretto.Cache | ||
quit chan struct{} | ||
cache *ristretto.Cache | ||
quit chan struct{} | ||
lastEviction time.Time | ||
} | ||
|
||
// NewRistrettoCache returns a new ristrettoCache. | ||
func NewRistrettoCache(logger log.Logger) (Cache, error) { | ||
// NewRistrettoCacheWithEviction returns a new ristrettoCache. | ||
func NewRistrettoCacheWithEviction(logger log.Logger) (Cache, error) { | ||
cache, err := ristretto.NewCache(&ristretto.Config{ | ||
NumCounters: numCounters, | ||
MaxCost: maxCost, | ||
|
@@ -33,8 +34,9 @@ func NewRistrettoCache(logger log.Logger) (Cache, error) { | |
} | ||
|
||
c := &ristrettoCache{ | ||
cache: cache, | ||
quit: make(chan struct{}), | ||
cache: cache, | ||
quit: make(chan struct{}), | ||
lastEviction: time.Now(), | ||
} | ||
|
||
// Start the metrics logging | ||
|
@@ -43,6 +45,21 @@ func NewRistrettoCache(logger log.Logger) (Cache, error) { | |
return c, nil | ||
} | ||
|
||
func (c *ristrettoCache) EvictShortLiving() { | ||
c.lastEviction = time.Now() | ||
} | ||
|
||
func (c *ristrettoCache) IsEvicted(key any) bool { | ||
remainingTTL, notExpired := c.cache.GetTTL(key) | ||
if !notExpired { | ||
return true | ||
} | ||
cachedTime := time.Now().Add(-remainingTTL) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like the maths is a bit off here because TTL remaining is not time since cached. Not sure there's enough information to work out the cached time as this is at the moment. Concrete example:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you're right. I got confused. |
||
// ... LE...Cached...Now - Valid | ||
// ... Cached...LE...Now - Evicted | ||
return c.lastEviction.After(cachedTime) | ||
} | ||
|
||
// Set adds the key and value to the cache. | ||
func (c *ristrettoCache) Set(key []byte, value any, ttl time.Duration) bool { | ||
return c.cache.SetWithTTL(key, value, defaultCost, ttl) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ const ( | |
notAuthorised = "not authorised" | ||
|
||
longCacheTTL = 5 * time.Hour | ||
shortCacheTTL = 100 * time.Millisecond | ||
shortCacheTTL = 1 * time.Minute | ||
) | ||
|
||
var rpcNotImplemented = fmt.Errorf("rpc endpoint not implemented") | ||
|
@@ -44,12 +44,17 @@ type ExecCfg struct { | |
cacheCfg *CacheCfg | ||
} | ||
|
||
type CacheStrategy uint8 | ||
|
||
const ( | ||
NoCache CacheStrategy = iota | ||
LatestBatch CacheStrategy = iota | ||
LongLiving CacheStrategy = iota | ||
) | ||
|
||
type CacheCfg struct { | ||
// ResetWhenNewBlock bool todo | ||
TTL time.Duration | ||
// logic based on block | ||
// todo - handle block in the future | ||
TTLCallback func() time.Duration | ||
CacheType CacheStrategy | ||
CacheTypeDynamic func() CacheStrategy | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personal preference but I'd just have the func property instead of supporting either, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to keep things straight-forward and compact where you know exactly |
||
} | ||
|
||
func UnauthenticatedTenRPCCall[R any](ctx context.Context, w *Services, cfg *CacheCfg, method string, args ...any) (*R, error) { | ||
|
@@ -193,28 +198,34 @@ func withCache[R any](cache cache.Cache, cfg *CacheCfg, cacheKey []byte, onCache | |
return onCacheMiss() | ||
} | ||
|
||
cacheTTL := cfg.TTL | ||
if cfg.TTLCallback != nil { | ||
cacheTTL = cfg.TTLCallback() | ||
cacheType := cfg.CacheType | ||
if cfg.CacheTypeDynamic != nil { | ||
cacheType = cfg.CacheTypeDynamic() | ||
} | ||
isCacheable := cacheTTL > 0 | ||
|
||
if isCacheable { | ||
if cachedValue, ok := cache.Get(cacheKey); ok { | ||
// cloning? | ||
returnValue, ok := cachedValue.(*R) | ||
if !ok { | ||
return nil, fmt.Errorf("unexpected error. Invalid format cached. %v", cachedValue) | ||
} | ||
return returnValue, nil | ||
|
||
if cacheType == NoCache { | ||
return onCacheMiss() | ||
} | ||
|
||
ttl := longCacheTTL | ||
if cacheType == LatestBatch { | ||
ttl = shortCacheTTL | ||
} | ||
|
||
cachedValue, foundInCache := cache.Get(cacheKey) | ||
if foundInCache && !cache.IsEvicted(cacheKey) { | ||
returnValue, ok := cachedValue.(*R) | ||
if !ok { | ||
return nil, fmt.Errorf("unexpected error. Invalid format cached. %v", cachedValue) | ||
} | ||
return returnValue, nil | ||
} | ||
|
||
result, err := onCacheMiss() | ||
|
||
// cache only non-nil values | ||
if isCacheable && err == nil && result != nil { | ||
cache.Set(cacheKey, result, cacheTTL) | ||
if err == nil && result != nil { | ||
cache.Set(cacheKey, result, ttl) | ||
} | ||
|
||
return result, err | ||
|
@@ -226,18 +237,18 @@ func audit(services *Services, msg string, params ...any) { | |
} | ||
} | ||
|
||
func cacheTTLBlockNumberOrHash(blockNrOrHash rpc.BlockNumberOrHash) time.Duration { | ||
func cacheTTLBlockNumberOrHash(blockNrOrHash rpc.BlockNumberOrHash) CacheStrategy { | ||
if blockNrOrHash.BlockNumber != nil && blockNrOrHash.BlockNumber.Int64() <= 0 { | ||
return shortCacheTTL | ||
return LatestBatch | ||
} | ||
return longCacheTTL | ||
return LongLiving | ||
} | ||
|
||
func cacheTTLBlockNumber(lastBlock rpc.BlockNumber) time.Duration { | ||
func cacheTTLBlockNumber(lastBlock rpc.BlockNumber) CacheStrategy { | ||
if lastBlock > 0 { | ||
return longCacheTTL | ||
return LongLiving | ||
} | ||
return shortCacheTTL | ||
return LatestBatch | ||
} | ||
|
||
func connectWS(account *GWAccount, logger gethlog.Logger) (*tenrpc.EncRPCClient, error) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,7 +118,7 @@ func NewServices(hostAddrHTTP string, hostAddrWS string, storage storage.Storage | |
|
||
services.backendNewHeadsSubscription = clientSubscription | ||
services.NewHeadsService = subscriptioncommon.NewNewHeadsService(ch, true, logger, func(newHead *common2.BatchHeader) error { | ||
// todo - in a followup PR, invalidate cache entries marked as "latest" | ||
services.Cache.EvictShortLiving() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missed this on a previous review but could you alias that |
||
return nil | ||
}) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This here is the fiddly bit.
The rest is relatively straight forward