trie/triedb/pathdb, core/rawdb: enhance error message in freezer (#28198)
This PR adds more error message for debugging purpose.
This commit is contained in:
parent
062598bb40
commit
3853f50082
|
@ -22,6 +22,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const tmpSuffix = ".tmp"
|
const tmpSuffix = ".tmp"
|
||||||
|
@ -224,6 +225,7 @@ func cleanup(path string) error {
|
||||||
}
|
}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if name == filepath.Base(path)+tmpSuffix {
|
if name == filepath.Base(path)+tmpSuffix {
|
||||||
|
log.Info("Removed leftover freezer directory", "name", name)
|
||||||
return os.RemoveAll(filepath.Join(parent, name))
|
return os.RemoveAll(filepath.Join(parent, name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,6 +257,12 @@ func (t *freezerTable) repair() error {
|
||||||
t.index.ReadAt(buffer, offsetsSize-indexEntrySize)
|
t.index.ReadAt(buffer, offsetsSize-indexEntrySize)
|
||||||
lastIndex.unmarshalBinary(buffer)
|
lastIndex.unmarshalBinary(buffer)
|
||||||
}
|
}
|
||||||
|
// Print an error log if the index is corrupted due to an incorrect
|
||||||
|
// last index item. While it is theoretically possible to have a zero offset
|
||||||
|
// by storing all zero-size items, it is highly unlikely to occur in practice.
|
||||||
|
if lastIndex.offset == 0 && offsetsSize%indexEntrySize > 1 {
|
||||||
|
log.Error("Corrupted index file detected", "lastOffset", lastIndex.offset, "items", offsetsSize%indexEntrySize-1)
|
||||||
|
}
|
||||||
if t.readonly {
|
if t.readonly {
|
||||||
t.head, err = t.openFile(lastIndex.filenum, openFreezerFileForReadOnly)
|
t.head, err = t.openFile(lastIndex.filenum, openFreezerFileForReadOnly)
|
||||||
} else {
|
} else {
|
||||||
|
@ -349,7 +355,7 @@ func (t *freezerTable) repair() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if verbose {
|
if verbose {
|
||||||
t.logger.Info("Chain freezer table opened", "items", t.items.Load(), "size", t.headBytes)
|
t.logger.Info("Chain freezer table opened", "items", t.items.Load(), "deleted", t.itemOffset.Load(), "hidden", t.itemHidden.Load(), "tailId", t.tailId, "headId", t.headId, "size", t.headBytes)
|
||||||
} else {
|
} else {
|
||||||
t.logger.Debug("Chain freezer table opened", "items", t.items.Load(), "size", common.StorageSize(t.headBytes))
|
t.logger.Debug("Chain freezer table opened", "items", t.items.Load(), "size", common.StorageSize(t.headBytes))
|
||||||
}
|
}
|
||||||
|
@ -522,6 +528,10 @@ func (t *freezerTable) truncateTail(items uint64) error {
|
||||||
if err := t.meta.Sync(); err != nil {
|
if err := t.meta.Sync(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Close the index file before shorten it.
|
||||||
|
if err := t.index.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Truncate the deleted index entries from the index file.
|
// Truncate the deleted index entries from the index file.
|
||||||
err = copyFrom(t.index.Name(), t.index.Name(), indexEntrySize*(newDeleted-deleted+1), func(f *os.File) error {
|
err = copyFrom(t.index.Name(), t.index.Name(), indexEntrySize*(newDeleted-deleted+1), func(f *os.File) error {
|
||||||
tailIndex := indexEntry{
|
tailIndex := indexEntry{
|
||||||
|
@ -535,13 +545,14 @@ func (t *freezerTable) truncateTail(items uint64) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Reopen the modified index file to load the changes
|
// Reopen the modified index file to load the changes
|
||||||
if err := t.index.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
t.index, err = openFreezerFileForAppend(t.index.Name())
|
t.index, err = openFreezerFileForAppend(t.index.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Sync the file to ensure changes are flushed to disk
|
||||||
|
if err := t.index.Sync(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Release any files before the current tail
|
// Release any files before the current tail
|
||||||
t.tailId = newTailId
|
t.tailId = newTailId
|
||||||
t.itemOffset.Store(newDeleted)
|
t.itemOffset.Store(newDeleted)
|
||||||
|
@ -774,7 +785,7 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i
|
||||||
return fmt.Errorf("missing data file %d", fileId)
|
return fmt.Errorf("missing data file %d", fileId)
|
||||||
}
|
}
|
||||||
if _, err := dataFile.ReadAt(output[len(output)-length:], int64(start)); err != nil {
|
if _, err := dataFile.ReadAt(output[len(output)-length:], int64(start)); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w, fileid: %d, start: %d, length: %d", err, fileId, start, length)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -581,7 +581,16 @@ func truncateFromHead(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, nhead
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if ohead <= nhead {
|
otail, err := freezer.Tail()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Ensure that the truncation target falls within the specified range.
|
||||||
|
if ohead < nhead || nhead < otail {
|
||||||
|
return 0, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", otail, ohead, nhead)
|
||||||
|
}
|
||||||
|
// Short circuit if nothing to truncate.
|
||||||
|
if ohead == nhead {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
// Load the meta objects in range [nhead+1, ohead]
|
// Load the meta objects in range [nhead+1, ohead]
|
||||||
|
@ -610,11 +619,20 @@ func truncateFromHead(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, nhead
|
||||||
// truncateFromTail removes the extra state histories from the tail with the given
|
// truncateFromTail removes the extra state histories from the tail with the given
|
||||||
// parameters. It returns the number of items removed from the tail.
|
// parameters. It returns the number of items removed from the tail.
|
||||||
func truncateFromTail(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, ntail uint64) (int, error) {
|
func truncateFromTail(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, ntail uint64) (int, error) {
|
||||||
|
ohead, err := freezer.Ancients()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
otail, err := freezer.Tail()
|
otail, err := freezer.Tail()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if otail >= ntail {
|
// Ensure that the truncation target falls within the specified range.
|
||||||
|
if otail > ntail || ntail > ohead {
|
||||||
|
return 0, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", otail, ohead, ntail)
|
||||||
|
}
|
||||||
|
// Short circuit if nothing to truncate.
|
||||||
|
if otail == ntail {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
// Load the meta objects in range [otail+1, ntail]
|
// Load the meta objects in range [otail+1, ntail]
|
||||||
|
|
|
@ -224,6 +224,50 @@ func TestTruncateTailHistories(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTruncateOutOfRange(t *testing.T) {
|
||||||
|
var (
|
||||||
|
hs = makeHistories(10)
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
freezer, _ = openFreezer(t.TempDir(), false)
|
||||||
|
)
|
||||||
|
defer freezer.Close()
|
||||||
|
|
||||||
|
for i := 0; i < len(hs); i++ {
|
||||||
|
accountData, storageData, accountIndex, storageIndex := hs[i].encode()
|
||||||
|
rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
|
||||||
|
rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
|
||||||
|
}
|
||||||
|
truncateFromTail(db, freezer, uint64(len(hs)/2))
|
||||||
|
|
||||||
|
// Ensure of-out-range truncations are rejected correctly.
|
||||||
|
head, _ := freezer.Ancients()
|
||||||
|
tail, _ := freezer.Tail()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
mode int
|
||||||
|
target uint64
|
||||||
|
expErr error
|
||||||
|
}{
|
||||||
|
{0, head, nil}, // nothing to delete
|
||||||
|
{0, head + 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, head+1)},
|
||||||
|
{0, tail - 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, tail-1)},
|
||||||
|
{1, tail, nil}, // nothing to delete
|
||||||
|
{1, head + 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, head+1)},
|
||||||
|
{1, tail - 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, tail-1)},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
var gotErr error
|
||||||
|
if c.mode == 0 {
|
||||||
|
_, gotErr = truncateFromHead(db, freezer, c.target)
|
||||||
|
} else {
|
||||||
|
_, gotErr = truncateFromTail(db, freezer, c.target)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotErr, c.expErr) {
|
||||||
|
t.Errorf("Unexpected error, want: %v, got: %v", c.expErr, gotErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// openFreezer initializes the freezer instance for storing state histories.
|
// openFreezer initializes the freezer instance for storing state histories.
|
||||||
func openFreezer(datadir string, readOnly bool) (*rawdb.ResettableFreezer, error) {
|
func openFreezer(datadir string, readOnly bool) (*rawdb.ResettableFreezer, error) {
|
||||||
return rawdb.NewStateFreezer(datadir, readOnly)
|
return rawdb.NewStateFreezer(datadir, readOnly)
|
||||||
|
|
Loading…
Reference in New Issue