mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-26 00:16:18 +00:00
This PR adds the support of Pebble v2, details as below: - Pebble V2 will be used if database is empty - Pebble V1 will be used if database is not empty and the format is old - Upgrade command (geth db pebble-upgrade) is provided to upgrade the format to v2 offline
146 lines
5.7 KiB
Go
146 lines
5.7 KiB
Go
// Copyright 2025 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 pebble
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
|
|
pebblev1 "github.com/cockroachdb/pebble"
|
|
v1bloom "github.com/cockroachdb/pebble/bloom"
|
|
pebblev2 "github.com/cockroachdb/pebble/v2"
|
|
v2vfs "github.com/cockroachdb/pebble/v2/vfs"
|
|
v1vfs "github.com/cockroachdb/pebble/vfs"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
)
|
|
|
|
// formatMinV2 is the minimum FormatMajorVersion supported by pebble v2.
|
|
// Databases with a lower format version must be opened with pebble v1.
|
|
const formatMinV2 = pebblev2.FormatFlushableIngest
|
|
|
|
// PeekFormatVersion reads the format version of an existing pebble database
|
|
// without opening it.
|
|
func PeekFormatVersion(file string) (bool, uint64, error) {
|
|
desc, err := pebblev2.Peek(file, v2vfs.Default)
|
|
if err == nil && desc.Exists {
|
|
return true, uint64(desc.FormatMajorVersion), nil
|
|
}
|
|
// Pebble v2 dropped support for the legacy FormatMostCompatible layout,
|
|
// which relies on the CURRENT file rather than a manifest marker.
|
|
//
|
|
// Databases created by older Geth (which never set FormatMajorVersion
|
|
// and therefore default to FormatMostCompatible) are not recognized by
|
|
// v2's Peek: it reports Exists=false with a nil error instead of failing.
|
|
// It may also fail outright on some old databases. In both cases fall
|
|
// back to v1's Peek, which still understands the CURRENT-file layout.
|
|
desc1, err1 := pebblev1.Peek(file, v1vfs.Default)
|
|
if err1 != nil {
|
|
// Surface the v2 error if there was one, otherwise the v1 error.
|
|
// Such as the folder is not existent, fs.ErrNotExist.
|
|
if err != nil {
|
|
return false, 0, err
|
|
}
|
|
return false, 0, err1
|
|
}
|
|
if !desc1.Exists {
|
|
// Neither version found a database; treat as a new/empty directory.
|
|
return false, 0, nil
|
|
}
|
|
return true, uint64(desc1.FormatMajorVersion), nil
|
|
}
|
|
|
|
// NeedsV1 returns true if the database at the given path requires pebble v1
|
|
// to open (format version too old for pebble v2).
|
|
func NeedsV1(file string) bool {
|
|
exists, ver, err := PeekFormatVersion(file)
|
|
if err != nil || !exists {
|
|
return false // New database or error; use v2
|
|
}
|
|
return pebblev2.FormatMajorVersion(ver) < formatMinV2
|
|
}
|
|
|
|
// Upgrade upgrades an existing pebble v1 database to be compatible with pebble v2.
|
|
// It opens the database with pebble v1 at its current format version, then uses
|
|
// RatchetFormatMajorVersion to migrate to FormatFlushableIngest (the minimum format
|
|
// version that pebble v2 supports).
|
|
//
|
|
// Notably, it's not an irreversible upgrade, the database can still be opened with
|
|
// legacy Geth binary.
|
|
func Upgrade(file string) error {
|
|
exists, ver, err := PeekFormatVersion(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
return fmt.Errorf("pebble database not found at %s", file)
|
|
}
|
|
if pebblev2.FormatMajorVersion(ver) >= formatMinV2 {
|
|
log.Info("Database format already compatible with pebble v2", "version", ver)
|
|
return nil
|
|
}
|
|
// FormatFlushableIngest exists in both pebble v1 and pebble v2 and it serves
|
|
// as the natural bridge point: a v1 database can be ratcheted up to
|
|
// FormatFlushableIngest using pebble v1, and then pebble v2 can open it since
|
|
// that's its minimum supported format.
|
|
v1Target := pebblev1.FormatFlushableIngest
|
|
log.Info("Upgrading pebble database format via v1", "from", ver, "to", v1Target)
|
|
|
|
numCPU := runtime.NumCPU()
|
|
opt := &pebblev1.Options{
|
|
// Open at the current on-disk format version; do not request a
|
|
// higher version here so that the upgrade happens explicitly via
|
|
// RatchetFormatMajorVersion below.
|
|
MaxConcurrentCompactions: func() int { return numCPU },
|
|
Levels: []pebblev1.LevelOptions{
|
|
{TargetFileSize: 2 * 1024 * 1024, FilterPolicy: v1bloom.FilterPolicy(10)},
|
|
{TargetFileSize: 4 * 1024 * 1024, FilterPolicy: v1bloom.FilterPolicy(10)},
|
|
{TargetFileSize: 8 * 1024 * 1024, FilterPolicy: v1bloom.FilterPolicy(10)},
|
|
{TargetFileSize: 16 * 1024 * 1024, FilterPolicy: v1bloom.FilterPolicy(10)},
|
|
{TargetFileSize: 32 * 1024 * 1024, FilterPolicy: v1bloom.FilterPolicy(10)},
|
|
{TargetFileSize: 64 * 1024 * 1024, FilterPolicy: v1bloom.FilterPolicy(10)},
|
|
{TargetFileSize: 128 * 1024 * 1024},
|
|
},
|
|
Logger: panicLogger{},
|
|
}
|
|
db, err := pebblev1.Open(file, opt)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open database with pebble v1 for upgrade: %w", err)
|
|
}
|
|
if err := db.RatchetFormatMajorVersion(v1Target); err != nil {
|
|
db.Close()
|
|
return fmt.Errorf("failed to ratchet format version to %d: %w", v1Target, err)
|
|
}
|
|
if err := db.Close(); err != nil {
|
|
return fmt.Errorf("failed to close database after v1 upgrade: %w", err)
|
|
}
|
|
log.Info("Pebble v1 format upgrade complete, verifying v2 compatibility")
|
|
|
|
// Verify that pebble v2 can open the upgraded database.
|
|
opt2 := &pebblev2.Options{
|
|
Logger: panicLogger{},
|
|
FormatMajorVersion: formatMinV2,
|
|
}
|
|
db2, err := pebblev2.Open(file, opt2)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open database with pebble v2 after upgrade: %w", err)
|
|
}
|
|
if err := db2.Close(); err != nil {
|
|
return fmt.Errorf("failed to close database after v2 verification: %w", err)
|
|
}
|
|
log.Info("Pebble database format upgrade complete")
|
|
return nil
|
|
}
|