erigon-pulse/migrations/txs_v3.go
Alex Sharov e14664d53b
Store Canonical/NonCanonical/Bad blocks/senders/txs in same tables. Make re-org cheaper (#7648)
- allow store non-canonical blocks/senders
- optimize re-org: don't update/delete most of data
- allow mark chain as `Bad` - will be not visible by eth_getBlockByHash,
but can read if have hash+num
2023-06-05 12:36:24 +07:00

92 lines
2.6 KiB
Go

package migrations
import (
"bytes"
"context"
"encoding/binary"
"runtime"
"time"
common2 "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/datadir"
"github.com/ledgerwatch/erigon-lib/common/dbg"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/log/v3"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/core/rawdb"
)
var TxsV3 = Migration{
Name: "txs_v3",
Up: func(db kv.RwDB, dirs datadir.Dirs, progress []byte, BeforeCommit Callback, logger log.Logger) (err error) {
logEvery := time.NewTicker(10 * time.Second)
defer logEvery.Stop()
// in TxsV3 format canonical and non-canonical blocks are stored in the same table: kv.EthTx
txIDBytes := make([]byte, 8)
// just delete all non-canonical blocks.
if err := db.Update(context.Background(), func(tx kv.RwTx) error {
from := hexutility.EncodeTs(1) // protect genesis
if err := tx.ForEach(kv.BlockBody, from, func(k, _ []byte) error {
blockNum := binary.BigEndian.Uint64(k)
select {
case <-logEvery.C:
var m runtime.MemStats
dbg.ReadMemStats(&m)
logger.Info("[txs_v3] Migration progress", "block_num", blockNum,
"alloc", common2.ByteCount(m.Alloc), "sys", common2.ByteCount(m.Sys))
default:
}
canonicalHash, err := rawdb.ReadCanonicalHash(tx, blockNum)
if err != nil {
return err
}
isCanonical := bytes.Equal(k[8:], canonicalHash[:])
if isCanonical {
return nil // skip
}
b, err := rawdb.ReadBodyForStorageByKey(tx, k)
if err != nil {
return err
}
if b == nil {
log.Debug("PruneBlocks: block body not found", "height", blockNum)
} else {
for txID := b.BaseTxId; txID < b.BaseTxId+uint64(b.TxAmount); txID++ {
binary.BigEndian.PutUint64(txIDBytes, txID)
if err = tx.Delete(kv.NonCanonicalTxs, txIDBytes); err != nil {
return err
}
}
}
// Copying k because otherwise the same memory will be reused
// for the next key and Delete below will end up deleting 1 more record than required
kCopy := common.CopyBytes(k)
if err = tx.Delete(kv.Senders, kCopy); err != nil {
return err
}
if err = tx.Delete(kv.BlockBody, kCopy); err != nil {
return err
}
if err = tx.Delete(kv.Headers, kCopy); err != nil {
return err
}
return nil
}); err != nil {
return err
}
// This migration is no-op, but it forces the migration mechanism to apply it and thus write the DB schema version info
return BeforeCommit(tx, nil, true)
}); err != nil {
return err
}
return nil
},
}