Skip to content

Commit

Permalink
Migration script fixes (#179)
Browse files Browse the repository at this point in the history
* Fixed migration for datadirs without ancients

The script was assuming that ancients would have been migrated and was
considering the numAncients-1 to be the next block to migrate but when
numAncients is zero that's a problem.

Also remved logic for  picking up where db migration left of for the
level db since it was complicating the logic and that process takes a
few seconds, which is nothing compared with the minutes taken to migrate
the ancients.

* Ensure that we set gas limit if migrating at pre-gingerbread point
  • Loading branch information
piersy authored and palango committed Sep 24, 2024
1 parent ba9bf6a commit f30b7ad
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 83 deletions.
27 changes: 2 additions & 25 deletions op-chain-ops/cmd/celo-migrate/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import (

// Constants for the database
const (
DBCache = 1024 // size of the cache in MB
DBHandles = 60 // number of handles
LastMigratedNonAncientBlockKey = "celoLastMigratedNonAncientBlock"
DBCache = 1024 // size of the cache in MB
DBHandles = 60 // number of handles
)

var (
Expand All @@ -35,28 +34,6 @@ func headerKey(number uint64, hash common.Hash) []byte {
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
}

// readLastMigratedNonAncientBlock returns the last migration number. If it doesn't exist, it returns 0.
func readLastMigratedNonAncientBlock(db ethdb.KeyValueReader) uint64 {
data, err := db.Get([]byte(LastMigratedNonAncientBlockKey))
if err != nil {
return 0
}
number := binary.BigEndian.Uint64(data)
return number
}

// writeLastMigratedNonAncientBlock stores the last migration number.
func writeLastMigratedNonAncientBlock(db ethdb.KeyValueWriter, number uint64) error {
enc := make([]byte, 8)
binary.BigEndian.PutUint64(enc, number)
return db.Put([]byte(LastMigratedNonAncientBlockKey), enc)
}

// deleteLastMigratedNonAncientBlock removes the last migration number.
func deleteLastMigratedNonAncientBlock(db ethdb.KeyValueWriter) error {
return db.Delete([]byte(LastMigratedNonAncientBlockKey))
}

// openDB opens the chaindata database at the given path. Note this path is below the datadir
func openDB(chaindataPath string) (ethdb.Database, error) {
if _, err := os.Stat(chaindataPath); errors.Is(err, os.ErrNotExist) {
Expand Down
41 changes: 17 additions & 24 deletions op-chain-ops/cmd/celo-migrate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,6 @@ var (
Name: "clear-all",
Usage: "Use this to start with a fresh new db, deleting all data including ancients. CAUTION: Re-migrating ancients takes time.",
}
keepNonAncientsFlag = &cli.BoolFlag{
Name: "keep-non-ancients",
Usage: "CAUTION: Not recommended for production. Use to keep all data in the new db as is, including any partially migrated non-ancient blocks and state data. If non-ancient blocks are partially migrated, the script will attempt to resume the migration.",
}
onlyAncientsFlag = &cli.BoolFlag{
Name: "only-ancients",
Usage: "Use to only migrate ancient blocks. Ignored when running full migration",
Expand All @@ -101,7 +97,6 @@ var (
bufferSizeFlag,
memoryLimitFlag,
clearAllFlag,
keepNonAncientsFlag,
}
stateMigrationFlags = []cli.Flag{
newDBPathFlag,
Expand All @@ -117,14 +112,13 @@ var (
)

type blockMigrationOptions struct {
oldDBPath string
newDBPath string
batchSize uint64
bufferSize uint64
memoryLimit int64
clearAll bool
keepNonAncients bool
onlyAncients bool
oldDBPath string
newDBPath string
batchSize uint64
bufferSize uint64
memoryLimit int64
clearAll bool
onlyAncients bool
}

type stateMigrationOptions struct {
Expand All @@ -139,14 +133,13 @@ type stateMigrationOptions struct {

func parseBlockMigrationOptions(ctx *cli.Context) blockMigrationOptions {
return blockMigrationOptions{
oldDBPath: ctx.String(oldDBPathFlag.Name),
newDBPath: ctx.String(newDBPathFlag.Name),
batchSize: ctx.Uint64(batchSizeFlag.Name),
bufferSize: ctx.Uint64(bufferSizeFlag.Name),
memoryLimit: ctx.Int64(memoryLimitFlag.Name),
clearAll: ctx.Bool(clearAllFlag.Name),
keepNonAncients: ctx.Bool(keepNonAncientsFlag.Name),
onlyAncients: ctx.Bool(onlyAncientsFlag.Name),
oldDBPath: ctx.String(oldDBPathFlag.Name),
newDBPath: ctx.String(newDBPathFlag.Name),
batchSize: ctx.Uint64(batchSizeFlag.Name),
bufferSize: ctx.Uint64(bufferSizeFlag.Name),
memoryLimit: ctx.Int64(memoryLimitFlag.Name),
clearAll: ctx.Bool(clearAllFlag.Name),
onlyAncients: ctx.Bool(onlyAncientsFlag.Name),
}
}

Expand Down Expand Up @@ -234,7 +227,7 @@ func runBlockMigration(opts blockMigrationOptions) error {

debug.SetMemoryLimit(opts.memoryLimit * 1 << 20) // Set memory limit, converting from MiB to bytes

log.Info("Block Migration Started", "oldDBPath", opts.oldDBPath, "newDBPath", opts.newDBPath, "batchSize", opts.batchSize, "memoryLimit", opts.memoryLimit, "clearAll", opts.clearAll, "keepNonAncients", opts.keepNonAncients, "onlyAncients", opts.onlyAncients)
log.Info("Block Migration Started", "oldDBPath", opts.oldDBPath, "newDBPath", opts.newDBPath, "batchSize", opts.batchSize, "memoryLimit", opts.memoryLimit, "clearAll", opts.clearAll, "onlyAncients", opts.onlyAncients)

var err error

Expand All @@ -246,7 +239,7 @@ func runBlockMigration(opts blockMigrationOptions) error {
if err = os.RemoveAll(opts.newDBPath); err != nil {
return fmt.Errorf("failed to remove new database: %w", err)
}
} else if !opts.keepNonAncients {
} else {
if err = cleanupNonAncientDb(opts.newDBPath); err != nil {
return fmt.Errorf("failed to reset non-ancient database: %w", err)
}
Expand All @@ -260,7 +253,7 @@ func runBlockMigration(opts blockMigrationOptions) error {

var numNonAncients uint64
if !opts.onlyAncients {
if numNonAncients, err = migrateNonAncientsDb(opts.oldDBPath, opts.newDBPath, numAncientsNewAfter-1, opts.batchSize); err != nil {
if numNonAncients, err = migrateNonAncientsDb(opts.oldDBPath, opts.newDBPath, numAncientsNewAfter, opts.batchSize); err != nil {
return fmt.Errorf("failed to migrate non-ancients database: %w", err)
}
} else {
Expand Down
51 changes: 18 additions & 33 deletions op-chain-ops/cmd/celo-migrate/non-ancients.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/ethereum/go-ethereum/log"
)

func migrateNonAncientsDb(oldDbPath, newDbPath string, lastAncientBlock, batchSize uint64) (uint64, error) {
func migrateNonAncientsDb(oldDbPath, newDbPath string, numAncients, batchSize uint64) (uint64, error) {
// First copy files from old database to new database
log.Info("Copy files from old database (excluding ancients)", "process", "non-ancients")

Expand Down Expand Up @@ -51,22 +51,10 @@ func migrateNonAncientsDb(oldDbPath, newDbPath string, lastAncientBlock, batchSi
// get the last block number
hash := rawdb.ReadHeadHeaderHash(newDB)
lastBlock := *rawdb.ReadHeaderNumber(newDB, hash)
lastMigratedNonAncientBlock := readLastMigratedNonAncientBlock(newDB) // returns 0 if not found

// if migration was interrupted, start from the last migrated block
fromBlock := max(lastAncientBlock, lastMigratedNonAncientBlock) + 1
log.Info("Non-Ancient Block Migration Started", "process", "non-ancients", "startBlock", numAncients, "endBlock", lastBlock, "count", lastBlock-numAncients, "lastAncientBlock", numAncients)

if fromBlock >= lastBlock {
log.Info("Non-Ancient Block Migration Skipped", "process", "non-ancients", "lastAncientBlock", lastAncientBlock, "endBlock", lastBlock, "lastMigratedNonAncientBlock", lastMigratedNonAncientBlock)
if lastMigratedNonAncientBlock != lastBlock {
return 0, fmt.Errorf("migration range empty but last migrated block is not the last block in the database")
}
return 0, nil
}

log.Info("Non-Ancient Block Migration Started", "process", "non-ancients", "startBlock", fromBlock, "endBlock", lastBlock, "count", lastBlock-fromBlock, "lastAncientBlock", lastAncientBlock, "lastMigratedNonAncientBlock", lastMigratedNonAncientBlock)

for i := fromBlock; i <= lastBlock; i += batchSize {
for i := numAncients; i <= lastBlock; i += batchSize {
numbersHash := rawdb.ReadAllHashesInRange(newDB, i, i+batchSize-1)

log.Info("Processing Block Range", "process", "non-ancients", "from", i, "to(inclusve)", i+batchSize-1, "count", len(numbersHash))
Expand Down Expand Up @@ -94,30 +82,27 @@ func migrateNonAncientsDb(oldDbPath, newDbPath string, lastAncientBlock, batchSi
batch := newDB.NewBatch()
rawdb.WriteBodyRLP(batch, numberHash.Hash, numberHash.Number, newBody)
_ = batch.Put(headerKey(numberHash.Number, numberHash.Hash), newHeader)
_ = writeLastMigratedNonAncientBlock(batch, numberHash.Number)
if err := batch.Write(); err != nil {
return 0, fmt.Errorf("failed to write header and body: block %d - %x: %w", numberHash.Number, numberHash.Hash, err)
}
}
}

toBeRemoved := rawdb.ReadAllHashesInRange(newDB, 1, lastAncientBlock)
log.Info("Removing frozen blocks", "process", "non-ancients", "count", len(toBeRemoved))
batch := newDB.NewBatch()
for _, numberHash := range toBeRemoved {
rawdb.DeleteBlockWithoutNumber(batch, numberHash.Hash, numberHash.Number)
rawdb.DeleteCanonicalHash(batch, numberHash.Number)
}
if err := batch.Write(); err != nil {
return 0, fmt.Errorf("failed to delete frozen blocks: %w", err)
}

// if migration finished, remove the last migration number
if err := deleteLastMigratedNonAncientBlock(newDB); err != nil {
return 0, fmt.Errorf("failed to delete last migration number: %w", err)
if numAncients > 0 {
toBeRemoved := rawdb.ReadAllHashesInRange(newDB, 1, numAncients)
log.Info("Removing frozen blocks", "process", "non-ancients", "count", len(toBeRemoved))
batch := newDB.NewBatch()
for _, numberHash := range toBeRemoved {
rawdb.DeleteBlockWithoutNumber(batch, numberHash.Hash, numberHash.Number)
rawdb.DeleteCanonicalHash(batch, numberHash.Number)
}
if err := batch.Write(); err != nil {
return 0, fmt.Errorf("failed to delete frozen blocks: %w", err)
}
log.Info("Removed frozen blocks, still in leveldb", "process", "non-ancients", "removedBlocks", len(toBeRemoved))
}
migratedCount := lastBlock - numAncients + 1
log.Info("Non-Ancient Block Migration Ended", "process", "non-ancients", "migratedBlocks", migratedCount)

log.Info("Non-Ancient Block Migration Ended", "process", "non-ancients", "migratedBlocks", lastBlock-fromBlock+1, "removedBlocks", len(toBeRemoved))

return lastBlock - fromBlock + 1, nil
return migratedCount, nil
}
10 changes: 9 additions & 1 deletion op-chain-ops/cmd/celo-migrate/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ func applyStateMigrationChanges(config *genesis.DeployConfig, genesis *core.Gene
migrationBlockTime = uint64(time.Now().Unix())
}

// If gas limit was zero at the transition point use a default of 30M.
// Note that in op-geth we use gasLimit==0 to indicate a pre-gingerbread
// block and adjust encoding appropriately, so we must make sure that
// gasLimit is non-zero, bacause L2 blocks are all post gingerbread.
gasLimit := header.GasLimit
if gasLimit == 0 {
gasLimit = 30e6
}
// Create the header for the Cel2 transition block.
cel2Header := &types.Header{
ParentHash: header.Hash(),
Expand All @@ -136,7 +144,7 @@ func applyStateMigrationChanges(config *genesis.DeployConfig, genesis *core.Gene
Bloom: types.Bloom{},
Difficulty: new(big.Int).Set(common.Big0),
Number: migrationBlock,
GasLimit: header.GasLimit,
GasLimit: gasLimit,
GasUsed: 0,
Time: migrationBlockTime,
Extra: []byte("CeL2 migration"),
Expand Down

0 comments on commit f30b7ad

Please sign in to comment.