diff --git a/core/rawdb/freezer_meta.go b/core/rawdb/freezer_meta.go index a77e563f9a39..cf0aadb5b66d 100644 --- a/core/rawdb/freezer_meta.go +++ b/core/rawdb/freezer_meta.go @@ -20,6 +20,7 @@ import ( "bytes" "errors" "fmt" + "io" "os" "github.com/ethereum/go-ethereum/rlp" @@ -175,7 +176,7 @@ func upgradeTableIndex(index *os.File, version uint16) (*os.File, *freezerTableM return nil, nil, err } default: - return nil, nil, errors.New("unknown freezer table index") + return nil, nil, errors.New("unknown freezer table version") } // Reopen the upgraded index file and load the metadata from it index, err := os.Open(index.Name()) @@ -205,7 +206,7 @@ func repairTableIndex(index *os.File) (*os.File, *freezerTableMeta, error) { return nil, nil, err } // Shift file cursor to the end for next write operation - _, err = index.Seek(0, 2) + _, err = index.Seek(0, io.SeekEnd) if err != nil { return nil, nil, err } diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index aaf81a0c757e..8c0671a29f6c 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -406,18 +406,6 @@ func (t *freezerTable) truncateHead(items uint64) error { if existing <= items { return nil } - // We need to truncate, save the old size for metrics tracking - oldSize, err := t.sizeNolock() - if err != nil { - return err - } - // Something's out of sync, truncate the table's offset index - log := t.logger.Debug - if existing > items+1 { - log = t.logger.Warn // Only loud warn if we delete multiple items - } - log("Truncating freezer table", "items", existing, "limit", items) - // Calculate the relative offset between the new head and tail, use // it to access the corresponding index entry. If the requested target // is even below the freezer tail, reject it. @@ -429,17 +417,28 @@ func (t *freezerTable) truncateHead(items uint64) error { if items < tail { return errors.New("truncation below tail") } - offset := items - itemOffset + // We need to truncate, save the old size for metrics tracking + oldSize, err := t.sizeNolock() + if err != nil { + return err + } + // Something's out of sync, truncate the table's offset index + log := t.logger.Debug + if existing > items+1 { + log = t.logger.Warn // Only loud warn if we delete multiple items + } + log("Truncating freezer table", "items", existing, "limit", items) - if err := truncateFreezerFile(t.index, int64(offset)*indexEntrySize+metaLength); err != nil { + length := items - itemOffset + if err := truncateFreezerFile(t.index, int64(length)*indexEntrySize+metaLength); err != nil { return err } // Calculate the new expected size of the data file and truncate it var expected *indexEntry - if offset == 0 { + if length == 0 { expected = &indexEntry{filenum: t.tailId, offset: 0} } else { - expected, err = t.getIndex(int64(offset-1), 0) + expected, err = t.getIndex(int64(length-1), 0) if err != nil { return err } @@ -498,7 +497,7 @@ func (t *freezerTable) truncateIndexFile(originDeleted, deleted, hidden uint64, return nil } -// truncateHead discards any recent data before the provided threshold number. +// truncateTail discards any recent data before the provided threshold number. func (t *freezerTable) truncateTail(items uint64) error { t.lock.Lock() defer t.lock.Unlock() @@ -515,18 +514,31 @@ func (t *freezerTable) truncateTail(items uint64) error { if head < items { return errors.New("truncation above head") } - // Load the index of new tail item after the deletion. - newTail, err := t.getIndex(int64(items-deleted), 0) + // Load the file number of new tail item after the deletion. + count, err := t.indexLen() if err != nil { return err } + var ( + tailId uint32 + delLen = items - deleted + ) + if uint64(count) == delLen { + tailId = t.headId + } else { + newTail, err := t.getIndex(int64(delLen), 0) + if err != nil { + return err + } + tailId = newTail.filenum + } // Freezer only supports deletion by file, just mark the entries as hidden - if t.tailId == newTail.filenum { - atomic.StoreUint64(&t.itemHidden, items-deleted) - return storeMetadata(t.index, newMetadata(t.tailId, deleted, items-deleted)) + if t.tailId == tailId { + atomic.StoreUint64(&t.itemHidden, delLen) + return storeMetadata(t.index, newMetadata(t.tailId, deleted, delLen)) } - if t.tailId > newTail.filenum { - return fmt.Errorf("invalid index, tail-file %d, item-file %d", t.tailId, newTail.filenum) + if t.tailId > tailId { + return fmt.Errorf("invalid index, tail-file %d, item-file %d", t.tailId, tailId) } // We need to truncate, save the old size for metrics tracking oldSize, err := t.sizeNolock() @@ -540,16 +552,16 @@ func (t *freezerTable) truncateTail(items uint64) error { if err != nil { return err } - if cur.filenum != newTail.filenum { + if cur.filenum != tailId { break } newDeleted = current } - if err := t.truncateIndexFile(deleted, newDeleted, items-newDeleted, newTail.filenum); err != nil { + if err := t.truncateIndexFile(deleted, newDeleted, items-newDeleted, tailId); err != nil { return err } // Release any files before the current tail - t.tailId = newTail.filenum + t.tailId = tailId atomic.StoreUint64(&t.itemOffset, newDeleted) atomic.StoreUint64(&t.itemHidden, items-newDeleted) t.releaseFilesBefore(t.tailId, true) diff --git a/core/rawdb/freezer_table_test.go b/core/rawdb/freezer_table_test.go index dbbbabac1940..c2101f45a695 100644 --- a/core/rawdb/freezer_table_test.go +++ b/core/rawdb/freezer_table_test.go @@ -760,7 +760,7 @@ func TestTruncateTail(t *testing.T) { }) // truncate all, the entire freezer should be deleted - f.truncateTail(6) + f.truncateTail(7) checkRetrieveError(t, f, map[uint64]error{ 0: errOutOfBounds, 1: errOutOfBounds, @@ -768,13 +768,11 @@ func TestTruncateTail(t *testing.T) { 3: errOutOfBounds, 4: errOutOfBounds, 5: errOutOfBounds, - }) - checkRetrieve(t, f, map[uint64][]byte{ - 6: getChunk(20, 0x11), + 6: errOutOfBounds, }) } -func TestTruncateHeadBelowTail(t *testing.T) { +func TestTruncateHead(t *testing.T) { t.Parallel() rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge() fname := fmt.Sprintf("truncate-head-blow-tail-%d", rand.Uint64()) @@ -822,12 +820,6 @@ func TestTruncateHeadBelowTail(t *testing.T) { 5: getChunk(20, 0xaa), 6: getChunk(20, 0x11), }) - - f.truncateTail(5) // Lazy deleted the item-4, it's hidden - f.truncateHead(5) // New head is reset to item-4 - checkRetrieveError(t, f, map[uint64]error{ - 4: errOutOfBounds, // Hidden item - }) } func TestUpgradeLegacyFreezerTable(t *testing.T) {