110 lines
3.3 KiB
Go
110 lines
3.3 KiB
Go
// Copyright 2022 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package rawdb
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
)
|
|
|
|
const freezerVersion = 1 // The initial version tag of freezer table metadata
|
|
|
|
// freezerTableMeta wraps all the metadata of the freezer table.
|
|
type freezerTableMeta struct {
|
|
// Version is the versioning descriptor of the freezer table.
|
|
Version uint16
|
|
|
|
// VirtualTail indicates how many items have been marked as deleted.
|
|
// Its value is equal to the number of items removed from the table
|
|
// plus the number of items hidden in the table, so it should never
|
|
// be lower than the "actual tail".
|
|
VirtualTail uint64
|
|
}
|
|
|
|
// newMetadata initializes the metadata object with the given virtual tail.
|
|
func newMetadata(tail uint64) *freezerTableMeta {
|
|
return &freezerTableMeta{
|
|
Version: freezerVersion,
|
|
VirtualTail: tail,
|
|
}
|
|
}
|
|
|
|
// readMetadata reads the metadata of the freezer table from the
|
|
// given metadata file.
|
|
func readMetadata(file *os.File) (*freezerTableMeta, error) {
|
|
_, err := file.Seek(0, io.SeekStart)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var meta freezerTableMeta
|
|
if err := rlp.Decode(file, &meta); err != nil {
|
|
return nil, err
|
|
}
|
|
return &meta, nil
|
|
}
|
|
|
|
// writeMetadata writes the metadata of the freezer table into the
|
|
// given metadata file.
|
|
func writeMetadata(file *os.File, meta *freezerTableMeta) error {
|
|
_, err := file.Seek(0, io.SeekStart)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rlp.Encode(file, meta)
|
|
}
|
|
|
|
// loadMetadata loads the metadata from the given metadata file.
|
|
// Initializes the metadata file with the given "actual tail" if
|
|
// it's empty.
|
|
func loadMetadata(file *os.File, tail uint64) (*freezerTableMeta, error) {
|
|
stat, err := file.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Write the metadata with the given actual tail into metadata file
|
|
// if it's non-existent. There are two possible scenarios here:
|
|
// - the freezer table is empty
|
|
// - the freezer table is legacy
|
|
// In both cases, write the meta into the file with the actual tail
|
|
// as the virtual tail.
|
|
if stat.Size() == 0 {
|
|
m := newMetadata(tail)
|
|
if err := writeMetadata(file, m); err != nil {
|
|
return nil, err
|
|
}
|
|
return m, nil
|
|
}
|
|
m, err := readMetadata(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Update the virtual tail with the given actual tail if it's even
|
|
// lower than it. Theoretically it shouldn't happen at all, print
|
|
// a warning here.
|
|
if m.VirtualTail < tail {
|
|
log.Warn("Updated virtual tail", "have", m.VirtualTail, "now", tail)
|
|
m.VirtualTail = tail
|
|
if err := writeMetadata(file, m); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return m, nil
|
|
}
|