mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
feat(miner): account fetch limit (#939)
* feat(worker): try to limit the number of txns miner has to deal with (#745) to reduce the effect of having a huge backlog on performance * fix(worker): set default account fetch limit (#756) * fix * update miner/worker.go * fix --------- Co-authored-by: Ömer Faruk Irmak <omerfirmak@gmail.com> Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
This commit is contained in:
parent
c86a215b04
commit
ac044caa1b
12 changed files with 102 additions and 9 deletions
|
|
@ -124,6 +124,7 @@ var (
|
||||||
utils.MinerRecommitIntervalFlag,
|
utils.MinerRecommitIntervalFlag,
|
||||||
utils.MinerNewPayloadTimeout,
|
utils.MinerNewPayloadTimeout,
|
||||||
utils.MinerStoreSkippedTxTracesFlag,
|
utils.MinerStoreSkippedTxTracesFlag,
|
||||||
|
utils.MinerMaxAccountsNumFlag,
|
||||||
utils.NATFlag,
|
utils.NATFlag,
|
||||||
utils.NoDiscoverFlag,
|
utils.NoDiscoverFlag,
|
||||||
utils.DiscoveryV4Flag,
|
utils.DiscoveryV4Flag,
|
||||||
|
|
|
||||||
|
|
@ -526,6 +526,13 @@ var (
|
||||||
MinerStoreSkippedTxTracesFlag = &cli.BoolFlag{
|
MinerStoreSkippedTxTracesFlag = &cli.BoolFlag{
|
||||||
Name: "miner.storeskippedtxtraces",
|
Name: "miner.storeskippedtxtraces",
|
||||||
Usage: "Store the wrapped traces when storing a skipped tx",
|
Usage: "Store the wrapped traces when storing a skipped tx",
|
||||||
|
Category: flags.MinerCategory,
|
||||||
|
}
|
||||||
|
MinerMaxAccountsNumFlag = &cli.IntFlag{
|
||||||
|
Name: "miner.maxaccountsnum",
|
||||||
|
Usage: "Maximum number of accounts that miner will fetch the pending transactions of when building a new block",
|
||||||
|
Value: math.MaxInt,
|
||||||
|
Category: flags.MinerCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account settings
|
// Account settings
|
||||||
|
|
@ -1697,6 +1704,9 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
||||||
if ctx.IsSet(MinerStoreSkippedTxTracesFlag.Name) {
|
if ctx.IsSet(MinerStoreSkippedTxTracesFlag.Name) {
|
||||||
cfg.StoreSkippedTxTraces = ctx.Bool(MinerStoreSkippedTxTracesFlag.Name)
|
cfg.StoreSkippedTxTraces = ctx.Bool(MinerStoreSkippedTxTracesFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.IsSet(MinerMaxAccountsNumFlag.Name) {
|
||||||
|
cfg.MaxAccountsNum = ctx.Int(MinerMaxAccountsNumFlag.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/holiman/billy"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus/misc/eip1559"
|
"github.com/scroll-tech/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus/misc/eip4844"
|
"github.com/scroll-tech/go-ethereum/consensus/misc/eip4844"
|
||||||
|
|
@ -42,8 +44,6 @@ import (
|
||||||
"github.com/scroll-tech/go-ethereum/params"
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
"github.com/scroll-tech/go-ethereum/rlp"
|
"github.com/scroll-tech/go-ethereum/rlp"
|
||||||
"github.com/scroll-tech/go-ethereum/rollup/fees"
|
"github.com/scroll-tech/go-ethereum/rollup/fees"
|
||||||
"github.com/holiman/billy"
|
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -1381,6 +1381,16 @@ func (p *BlobPool) drop() {
|
||||||
// Pending retrieves all currently processable transactions, grouped by origin
|
// Pending retrieves all currently processable transactions, grouped by origin
|
||||||
// account and sorted by nonce.
|
// account and sorted by nonce.
|
||||||
func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction {
|
func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction {
|
||||||
|
return p.pendingWithMax(enforceTips, math.MaxInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingWithMax works similar to Pending but allows setting an upper limit on how many
|
||||||
|
// accounts to return
|
||||||
|
func (p *BlobPool) PendingWithMax(enforceTips bool, maxAccountsNum int) map[common.Address][]*txpool.LazyTransaction {
|
||||||
|
return p.pendingWithMax(enforceTips, maxAccountsNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BlobPool) pendingWithMax(enforceTips bool, maxAccountsNum int) map[common.Address][]*txpool.LazyTransaction {
|
||||||
// Track the amount of time waiting to retrieve the list of pending blob txs
|
// Track the amount of time waiting to retrieve the list of pending blob txs
|
||||||
// from the pool and the amount of time actually spent on assembling the data.
|
// from the pool and the amount of time actually spent on assembling the data.
|
||||||
// The latter will be pretty much moot, but we've kept it to have symmetric
|
// The latter will be pretty much moot, but we've kept it to have symmetric
|
||||||
|
|
@ -1411,6 +1421,9 @@ func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTr
|
||||||
if len(lazies) > 0 {
|
if len(lazies) > 0 {
|
||||||
pending[addr] = lazies
|
pending[addr] = lazies
|
||||||
}
|
}
|
||||||
|
if len(pending) >= maxAccountsNum {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return pending
|
return pending
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -523,6 +523,16 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction,
|
||||||
// transactions and only return those whose **effective** tip is large enough in
|
// transactions and only return those whose **effective** tip is large enough in
|
||||||
// the next pending execution environment.
|
// the next pending execution environment.
|
||||||
func (pool *LegacyPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction {
|
func (pool *LegacyPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction {
|
||||||
|
return pool.pendingWithMax(enforceTips, math.MaxInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingWithMax works similar to Pending but allows setting an upper limit on how many
|
||||||
|
// accounts to return
|
||||||
|
func (pool *LegacyPool) PendingWithMax(enforceTips bool, maxAccountsNum int) map[common.Address][]*txpool.LazyTransaction {
|
||||||
|
return pool.pendingWithMax(enforceTips, maxAccountsNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pool *LegacyPool) pendingWithMax(enforceTips bool, maxAccountsNum int) map[common.Address][]*txpool.LazyTransaction {
|
||||||
pool.mu.Lock()
|
pool.mu.Lock()
|
||||||
defer pool.mu.Unlock()
|
defer pool.mu.Unlock()
|
||||||
|
|
||||||
|
|
@ -554,6 +564,9 @@ func (pool *LegacyPool) Pending(enforceTips bool) map[common.Address][]*txpool.L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pending[addr] = lazies
|
pending[addr] = lazies
|
||||||
|
if len(pending) >= maxAccountsNum {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pending
|
return pending
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/core"
|
"github.com/scroll-tech/go-ethereum/core"
|
||||||
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
||||||
|
|
@ -2628,3 +2630,27 @@ func BenchmarkMultiAccountBatchInsert(b *testing.B) {
|
||||||
pool.addRemotesSync([]*types.Transaction{tx})
|
pool.addRemotesSync([]*types.Transaction{tx})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPoolPending(t *testing.T) {
|
||||||
|
// Generate a batch of transactions to enqueue into the pool
|
||||||
|
pool, _ := setupPool()
|
||||||
|
defer pool.Close()
|
||||||
|
numTxns := 100
|
||||||
|
batches := make(types.Transactions, numTxns)
|
||||||
|
for i := 0; i < numTxns; i++ {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
account := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||||
|
tx := transaction(uint64(0), 100000, key)
|
||||||
|
batches[i] = tx
|
||||||
|
}
|
||||||
|
// Benchmark importing the transactions into the queue
|
||||||
|
for _, tx := range batches {
|
||||||
|
pool.addRemotesSync([]*types.Transaction{tx})
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Len(t, pool.Pending(false), numTxns)
|
||||||
|
|
||||||
|
maxAccounts := 10
|
||||||
|
assert.Len(t, pool.PendingWithMax(false, maxAccounts), maxAccounts)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,8 @@ type SubPool interface {
|
||||||
// account and sorted by nonce.
|
// account and sorted by nonce.
|
||||||
Pending(enforceTips bool) map[common.Address][]*LazyTransaction
|
Pending(enforceTips bool) map[common.Address][]*LazyTransaction
|
||||||
|
|
||||||
|
PendingWithMax(enforceTips bool, maxAccountsNum int) map[common.Address][]*LazyTransaction
|
||||||
|
|
||||||
// SubscribeTransactions subscribes to new transaction events. The subscriber
|
// SubscribeTransactions subscribes to new transaction events. The subscriber
|
||||||
// can decide whether to receive notifications only for newly seen transactions
|
// can decide whether to receive notifications only for newly seen transactions
|
||||||
// or also for reorged out ones.
|
// or also for reorged out ones.
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,16 @@ func (p *TxPool) Pending(enforceTips bool) map[common.Address][]*LazyTransaction
|
||||||
return txs
|
return txs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *TxPool) PendingWithMax(enforceTips bool, maxAccountsNum int) map[common.Address][]*LazyTransaction {
|
||||||
|
txs := make(map[common.Address][]*LazyTransaction)
|
||||||
|
for _, subpool := range p.subpools {
|
||||||
|
for addr, set := range subpool.PendingWithMax(enforceTips, maxAccountsNum) {
|
||||||
|
txs[addr] = set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return txs
|
||||||
|
}
|
||||||
|
|
||||||
// SubscribeTransactions registers a subscription for new transaction events,
|
// SubscribeTransactions registers a subscription for new transaction events,
|
||||||
// supporting feeding only newly seen or also resurrected transactions.
|
// supporting feeding only newly seen or also resurrected transactions.
|
||||||
func (p *TxPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool) event.Subscription {
|
func (p *TxPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool) event.Subscription {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ type Config struct {
|
||||||
NewPayloadTimeout time.Duration // The maximum time allowance for creating a new payload
|
NewPayloadTimeout time.Duration // The maximum time allowance for creating a new payload
|
||||||
|
|
||||||
StoreSkippedTxTraces bool // Whether store the wrapped traces when storing a skipped tx
|
StoreSkippedTxTraces bool // Whether store the wrapped traces when storing a skipped tx
|
||||||
|
MaxAccountsNum int // Maximum number of accounts that miner will fetch the pending transactions of when building a new block
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfig contains default settings for miner.
|
// DefaultConfig contains default settings for miner.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -302,6 +303,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) {
|
||||||
// Create Ethash config
|
// Create Ethash config
|
||||||
config := Config{
|
config := Config{
|
||||||
Etherbase: common.HexToAddress("123456789"),
|
Etherbase: common.HexToAddress("123456789"),
|
||||||
|
MaxAccountsNum: math.MaxInt,
|
||||||
}
|
}
|
||||||
// Create chainConfig
|
// Create chainConfig
|
||||||
chainDB := rawdb.NewMemoryDatabase()
|
chainDB := rawdb.NewMemoryDatabase()
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package miner
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -162,6 +163,12 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
|
||||||
// Subscribe events for blockchain
|
// Subscribe events for blockchain
|
||||||
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
|
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
|
||||||
|
|
||||||
|
// Sanitize account fetch limit.
|
||||||
|
if worker.config.MaxAccountsNum == 0 {
|
||||||
|
log.Warn("Sanitizing miner account fetch limit", "provided", worker.config.MaxAccountsNum, "updated", math.MaxInt)
|
||||||
|
worker.config.MaxAccountsNum = math.MaxInt
|
||||||
|
}
|
||||||
|
|
||||||
worker.wg.Add(1)
|
worker.wg.Add(1)
|
||||||
go worker.mainLoop()
|
go worker.mainLoop()
|
||||||
|
|
||||||
|
|
@ -382,7 +389,7 @@ func (w *worker) startNewPipeline(timestamp int64) {
|
||||||
|
|
||||||
tidyPendingStart := time.Now()
|
tidyPendingStart := time.Now()
|
||||||
// Fill the block with all available pending transactions.
|
// Fill the block with all available pending transactions.
|
||||||
pending := w.eth.TxPool().Pending(false)
|
pending := w.eth.TxPool().PendingWithMax(false, w.config.MaxAccountsNum)
|
||||||
// Split the pending transactions into locals and remotes
|
// Split the pending transactions into locals and remotes
|
||||||
localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending
|
localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending
|
||||||
for _, account := range w.eth.TxPool().Locals() {
|
for _, account := range w.eth.TxPool().Locals() {
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,12 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
|
||||||
}
|
}
|
||||||
worker.newpayloadTimeout = newpayloadTimeout
|
worker.newpayloadTimeout = newpayloadTimeout
|
||||||
|
|
||||||
|
// Sanitize account fetch limit.
|
||||||
|
if worker.config.MaxAccountsNum == 0 {
|
||||||
|
log.Warn("Sanitizing miner account fetch limit", "provided", worker.config.MaxAccountsNum, "updated", math.MaxInt)
|
||||||
|
worker.config.MaxAccountsNum = math.MaxInt
|
||||||
|
}
|
||||||
|
|
||||||
worker.wg.Add(4)
|
worker.wg.Add(4)
|
||||||
go worker.mainLoop()
|
go worker.mainLoop()
|
||||||
go worker.newWorkLoop(recommit)
|
go worker.newWorkLoop(recommit)
|
||||||
|
|
@ -1420,7 +1426,7 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err
|
||||||
}
|
}
|
||||||
|
|
||||||
tidyPendingStart := time.Now()
|
tidyPendingStart := time.Now()
|
||||||
pending := w.eth.TxPool().Pending(true)
|
pending := w.eth.TxPool().PendingWithMax(true, w.config.MaxAccountsNum)
|
||||||
|
|
||||||
// Split the pending transactions into locals and remotes.
|
// Split the pending transactions into locals and remotes.
|
||||||
localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending
|
localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package miner
|
package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -69,6 +70,7 @@ var (
|
||||||
testConfig = &Config{
|
testConfig = &Config{
|
||||||
Recommit: time.Second,
|
Recommit: time.Second,
|
||||||
GasCeil: params.GenesisGasLimit,
|
GasCeil: params.GenesisGasLimit,
|
||||||
|
MaxAccountsNum: math.MaxInt,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue