use glob instead of scanning

This commit is contained in:
Sina Mahmoodi 2025-04-03 17:39:57 +02:00 committed by lightclient
parent 228e410d4c
commit 583837bfdd
No known key found for this signature in database
GPG key ID: 657913021EF45A6A
3 changed files with 37 additions and 61 deletions

View file

@ -20,9 +20,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"path" "path/filepath"
"sync"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/era" "github.com/ethereum/go-ethereum/internal/era"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -30,12 +30,12 @@ import (
type EraDatabase struct { type EraDatabase struct {
datadir string datadir string
table map[uint64]*era.Era network string
mu sync.RWMutex cache *lru.Cache[uint64, *era.Era]
} }
// New creates a new EraDatabase instance. // New creates a new EraDatabase instance.
func New(datadir string) (*EraDatabase, error) { func New(datadir, network string) (*EraDatabase, error) {
// Ensure the datadir is not a symbolic link if it exists. // Ensure the datadir is not a symbolic link if it exists.
if info, err := os.Lstat(datadir); !os.IsNotExist(err) { if info, err := os.Lstat(datadir); !os.IsNotExist(err) {
if info == nil { if info == nil {
@ -50,28 +50,9 @@ func New(datadir string) (*EraDatabase, error) {
if err := os.MkdirAll(datadir, 0755); err != nil { if err := os.MkdirAll(datadir, 0755); err != nil {
return nil, err return nil, err
} }
db := &EraDatabase{datadir: datadir, network: network, cache: lru.NewCache[uint64, *era.Era](50)}
log.Info("Opened erastore", "datadir", datadir) log.Info("Opened erastore", "datadir", datadir)
return &EraDatabase{datadir: datadir, table: make(map[uint64]*era.Era)}, nil return db, nil
}
// scan returns a list of all era1 files in the datadir.
func (db *EraDatabase) scan() ([]string, error) {
entries, err := os.ReadDir(db.datadir)
if err != nil {
return nil, err
}
var files []string
for _, entry := range entries {
if entry.IsDir() || entry.Type()&os.ModeSymlink != 0 {
continue
}
// Skip files that are not era1 files.
if path.Ext(entry.Name()) != ".era1" {
continue
}
files = append(files, entry.Name())
}
return files, nil
} }
func (db *EraDatabase) openEra(name string) (*era.Era, error) { func (db *EraDatabase) openEra(name string) (*era.Era, error) {
@ -86,46 +67,46 @@ func (db *EraDatabase) openEra(name string) (*era.Era, error) {
if e.Start()%uint64(era.MaxEra1Size) != 0 { if e.Start()%uint64(era.MaxEra1Size) != 0 {
return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxEra1Size) return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxEra1Size)
} }
epoch := e.Start() / uint64(era.MaxEra1Size)
db.mu.Lock()
db.table[epoch] = e
db.mu.Unlock()
return e, nil return e, nil
} }
func (db *EraDatabase) Close() { func (db *EraDatabase) Close() {
db.mu.Lock() // Close all open era1 files in the cache.
defer db.mu.Unlock() keys := db.cache.Keys()
for _, e := range db.table { for _, key := range keys {
if err := e.Close(); err != nil { if e, ok := db.cache.Get(key); ok {
log.Warn("Failed to close era", "error", err) e.Close()
} }
} }
db.table = nil
} }
func (db *EraDatabase) GetBlockByNumber(number uint64) (*types.Block, error) { func (db *EraDatabase) GetBlockByNumber(number uint64) (*types.Block, error) {
files, err := db.scan() // Lookup the table by epoch.
epoch := number / uint64(era.MaxEra1Size)
// Check the cache first.
if e, ok := db.cache.Get(epoch); ok {
fmt.Printf("Cache hit for epoch %d\n", epoch)
return e.GetBlockByNumber(number)
}
// file name scheme is <network>-<epoch>-<root>.
glob := fmt.Sprintf("%s-%05d-*.era1", db.network, epoch)
matches, err := filepath.Glob(filepath.Join(db.datadir, glob))
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, file := range files { if len(matches) > 1 {
_, err := db.openEra(path.Join(db.datadir, file)) return nil, fmt.Errorf("multiple era1 files found for epoch %d", epoch)
if err != nil {
return nil, err
}
} }
if len(matches) == 0 {
// Lookup the table by epoch. return nil, nil
epoch := number / uint64(era.MaxEra1Size)
db.mu.RLock()
defer db.mu.RUnlock()
if e, ok := db.table[epoch]; ok {
block, err := e.GetBlockByNumber(number)
if err == nil {
return block, nil
}
} }
filename := matches[0]
e, err := db.openEra(filename)
if err != nil {
return nil, err
}
// Add the era to the cache.
db.cache.Add(epoch, e)
return nil, errors.New("block not found") return e.GetBlockByNumber(number)
} }

View file

@ -24,16 +24,12 @@ import (
func TestEraDatabase_Scan(t *testing.T) { func TestEraDatabase_Scan(t *testing.T) {
// Create the database // Create the database
db, err := New("testdata") db, err := New("testdata", "sepolia")
require.NoError(t, err) require.NoError(t, err)
// Scan for era files
files, err := db.scan()
require.NoError(t, err)
t.Log(files)
block, err := db.GetBlockByNumber(15000) block, err := db.GetBlockByNumber(15000)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, block, "block not found")
defer db.Close() defer db.Close()
require.Equal(t, uint64(15000), block.NumberU64()) require.Equal(t, uint64(15000), block.NumberU64())
} }

View file

@ -18,7 +18,6 @@ package era
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
@ -151,7 +150,7 @@ func (e *Era) GetHeaderByNumber(num uint64) (*types.Header, error) {
// GetBlockByNumber returns the block for the given block number. // GetBlockByNumber returns the block for the given block number.
func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) { func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) {
if e.m.start > num || e.m.start+e.m.count <= num { if e.m.start > num || e.m.start+e.m.count <= num {
return nil, errors.New("out-of-bounds") return nil, fmt.Errorf("out-of-bounds: %d not in [%d, %d)", num, e.m.start, e.m.start+e.m.count)
} }
off, err := e.readOffset(num) off, err := e.readOffset(num)
if err != nil { if err != nil {