mirror of
https://github.com/status-im/proxy-common.git
synced 2026-08-30 20:11:07 +00:00
171 lines
4.1 KiB
Go
171 lines
4.1 KiB
Go
package multi
|
|
|
|
import (
|
|
"github.com/status-im/proxy-common/cache"
|
|
"github.com/status-im/proxy-common/models"
|
|
)
|
|
|
|
// Ensure MultiCache implements cache.Cache and cache.LevelAwareCache
|
|
var _ cache.Cache = (*MultiCache)(nil)
|
|
var _ cache.LevelAwareCache = (*MultiCache)(nil)
|
|
|
|
// MultiCache implements a composite cache that tries multiple cache implementations
|
|
// It attempts to get/set values through an array of cache interfaces in order
|
|
type MultiCache struct {
|
|
caches []cache.Cache
|
|
logger cache.Logger
|
|
enablePropagation bool
|
|
}
|
|
|
|
// Option is a functional option for configuring MultiCache
|
|
type Option func(*MultiCache)
|
|
|
|
// WithLogger sets the logger for MultiCache
|
|
func WithLogger(logger cache.Logger) Option {
|
|
return func(mc *MultiCache) {
|
|
mc.logger = logger
|
|
}
|
|
}
|
|
|
|
// NewMultiCache creates a new MultiCache instance with provided cache implementations
|
|
func NewMultiCache(caches []cache.Cache, enablePropagation bool, opts ...Option) cache.LevelAwareCache {
|
|
mc := &MultiCache{
|
|
caches: caches,
|
|
logger: cache.NoopLogger{},
|
|
enablePropagation: enablePropagation,
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
opt(mc)
|
|
}
|
|
|
|
return mc
|
|
}
|
|
|
|
// Get retrieves value from the first available cache that has the key
|
|
func (mc *MultiCache) Get(key string) (*models.CacheEntry, bool) {
|
|
result := mc.GetWithLevel(key)
|
|
return result.Entry, result.Found
|
|
}
|
|
|
|
// GetStale retrieves stale value from the first available cache that has the key
|
|
func (mc *MultiCache) GetStale(key string) (*models.CacheEntry, bool) {
|
|
result := mc.GetStaleWithLevel(key)
|
|
return result.Entry, result.Found
|
|
}
|
|
|
|
// Set stores value in all available caches
|
|
func (mc *MultiCache) Set(key string, val []byte, ttl models.TTL) {
|
|
if len(mc.caches) == 0 {
|
|
mc.logger.Warn("No caches available for set operation", "key", key)
|
|
return
|
|
}
|
|
|
|
for _, c := range mc.caches {
|
|
c.Set(key, val, ttl)
|
|
}
|
|
}
|
|
|
|
// Delete removes entry from all available caches
|
|
func (mc *MultiCache) Delete(key string) {
|
|
if len(mc.caches) == 0 {
|
|
mc.logger.Warn("No caches available for delete operation", "key", key)
|
|
return
|
|
}
|
|
|
|
for _, c := range mc.caches {
|
|
c.Delete(key)
|
|
}
|
|
}
|
|
|
|
// GetCacheCount returns the number of caches in the multi-cache
|
|
func (mc *MultiCache) GetCacheCount() int {
|
|
return len(mc.caches)
|
|
}
|
|
|
|
// GetWithLevel retrieves value from cache with level information
|
|
func (mc *MultiCache) GetWithLevel(key string) *models.CacheResult {
|
|
if len(mc.caches) == 0 {
|
|
mc.logger.Warn("No caches available for get operation", "key", key)
|
|
return &models.CacheResult{
|
|
Entry: nil,
|
|
Found: false,
|
|
Level: models.CacheLevelMiss,
|
|
}
|
|
}
|
|
|
|
for i, c := range mc.caches {
|
|
entry, found := c.Get(key)
|
|
if found {
|
|
if i > 0 && mc.enablePropagation {
|
|
mc.propagateToEarlierCaches(key, entry, i)
|
|
}
|
|
|
|
level := models.CacheLevelFromIndex(i)
|
|
|
|
return &models.CacheResult{
|
|
Entry: entry,
|
|
Found: true,
|
|
Level: level,
|
|
}
|
|
}
|
|
}
|
|
|
|
return &models.CacheResult{
|
|
Entry: nil,
|
|
Found: false,
|
|
Level: models.CacheLevelMiss,
|
|
}
|
|
}
|
|
|
|
// GetStaleWithLevel retrieves stale value from cache with level information
|
|
func (mc *MultiCache) GetStaleWithLevel(key string) *models.CacheResult {
|
|
if len(mc.caches) == 0 {
|
|
return &models.CacheResult{
|
|
Entry: nil,
|
|
Found: false,
|
|
Level: models.CacheLevelMiss,
|
|
}
|
|
}
|
|
|
|
for i, c := range mc.caches {
|
|
entry, found := c.GetStale(key)
|
|
if found {
|
|
if i > 0 && mc.enablePropagation {
|
|
mc.propagateToEarlierCaches(key, entry, i)
|
|
}
|
|
|
|
level := models.CacheLevelFromIndex(i)
|
|
|
|
return &models.CacheResult{
|
|
Entry: entry,
|
|
Found: true,
|
|
Level: level,
|
|
}
|
|
}
|
|
}
|
|
|
|
return &models.CacheResult{
|
|
Entry: nil,
|
|
Found: false,
|
|
Level: models.CacheLevelMiss,
|
|
}
|
|
}
|
|
|
|
// propagateToEarlierCaches propagates a cache entry to earlier caches with adjusted TTL
|
|
func (mc *MultiCache) propagateToEarlierCaches(key string, entry *models.CacheEntry, foundAtIndex int) {
|
|
if entry == nil || entry.IsExpired() {
|
|
return
|
|
}
|
|
remainingTTL := entry.RemainingTTL()
|
|
|
|
// Only propagate if there's meaningful time left
|
|
if remainingTTL.Fresh <= 0 && remainingTTL.Stale <= 0 {
|
|
return
|
|
}
|
|
|
|
for i := 0; i < foundAtIndex; i++ {
|
|
mc.caches[i].Set(key, entry.Data, remainingTTL)
|
|
}
|
|
}
|