close OnReplaced

This commit is contained in:
Sina Mahmoodi 2025-04-16 15:17:32 +02:00 committed by lightclient
parent d053edfd74
commit bd697bc671
No known key found for this signature in database
GPG key ID: 657913021EF45A6A
3 changed files with 32 additions and 18 deletions

View file

@ -26,6 +26,7 @@ type BasicLRU[K comparable, V any] struct {
items map[K]cacheItem[K, V] items map[K]cacheItem[K, V]
cap int cap int
onEvicted func(key K, value V) onEvicted func(key K, value V)
onReplaced func(key K, value V)
} }
type cacheItem[K any, V any] struct { type cacheItem[K any, V any] struct {
@ -51,6 +52,9 @@ func (c *BasicLRU[K, V]) Add(key K, value V) (evicted bool) {
item, ok := c.items[key] item, ok := c.items[key]
if ok { if ok {
// Already exists in cache. // Already exists in cache.
if c.onReplaced != nil {
c.onReplaced(key, item.value)
}
item.value = value item.value = value
c.items[key] = item c.items[key] = item
c.list.moveToFront(item.elem) c.list.moveToFront(item.elem)
@ -152,6 +156,11 @@ func (c *BasicLRU[K, V]) OnEvicted(fn func(k K, v V)) {
c.onEvicted = fn c.onEvicted = fn
} }
// OnReplaced sets a callback function to be called when an item is replaced in the cache.
func (c *BasicLRU[K, V]) OnReplaced(fn func(k K, v V)) {
c.onReplaced = fn
}
// Keys returns all keys in the cache. // Keys returns all keys in the cache.
func (c *BasicLRU[K, V]) Keys() []K { func (c *BasicLRU[K, V]) Keys() []K {
keys := make([]K, 0, len(c.items)) keys := make([]K, 0, len(c.items))

View file

@ -101,3 +101,11 @@ func (c *Cache[K, V]) OnEvicted(fn func(key K, value V)) {
c.cache.OnEvicted(fn) c.cache.OnEvicted(fn)
} }
// OnReplaced sets a callback function to be called when an item is replaced in the cache.
func (c *Cache[K, V]) OnReplaced(fn func(key K, value V)) {
c.mu.Lock()
defer c.mu.Unlock()
c.cache.OnReplaced(fn)
}

View file

@ -27,12 +27,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
/**
* TODO:
* - FD leak possible on cache eviction.
* - FD leak possible on concurrent access to GetRaw*.
*/
const ( const (
openFileLimit = 64 openFileLimit = 64
) )
@ -61,17 +55,20 @@ func New(datadir string) (*EraDatabase, error) {
return nil, err return nil, err
} }
db := &EraDatabase{datadir: datadir, cache: lru.NewCache[uint64, *era.Era](openFileLimit)} db := &EraDatabase{datadir: datadir, cache: lru.NewCache[uint64, *era.Era](openFileLimit)}
db.cache.OnEvicted(func(key uint64, value *era.Era) { // Take care to close era1 files when they are evicted or replaced
if value == nil { // in the cache to avoid leaking file descriptors.
log.Warn("Era1 cache evicted nil value", "epoch", key) closeEra := func(epoch uint64, e *era.Era) {
if e == nil {
log.Warn("Era1 cache contained nil value", "epoch", epoch)
return return
} }
// Close the era1 file when it is evicted from the cache if err := e.Close(); err != nil {
// to avoid leaks. log.Warn("Error closing era1 file", "epoch", epoch, "err", err)
if err := value.Close(); err != nil {
log.Warn("Error closing era1 file", "epoch", key, "err", err)
} }
})
}
db.cache.OnEvicted(closeEra)
db.cache.OnReplaced(closeEra)
log.Info("Opened erastore", "datadir", datadir) log.Info("Opened erastore", "datadir", datadir)
return db, nil return db, nil
} }