Skip to content

Commit 88dfc34

Browse files
authored
Merge branch 'develop' into omerfirmak/mpt
2 parents ecb5fb8 + 8ecaeec commit 88dfc34

File tree

12 files changed

+297
-7
lines changed

12 files changed

+297
-7
lines changed

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ var (
9090
utils.TxPoolGlobalSlotsFlag,
9191
utils.TxPoolAccountQueueFlag,
9292
utils.TxPoolGlobalQueueFlag,
93+
utils.TxPoolAccountPendingLimitFlag,
9394
utils.TxPoolLifetimeFlag,
9495
utils.SyncModeFlag,
9596
utils.ExitWhenSyncedFlag,

cmd/geth/usage.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
109109
utils.TxPoolGlobalSlotsFlag,
110110
utils.TxPoolAccountQueueFlag,
111111
utils.TxPoolGlobalQueueFlag,
112+
utils.TxPoolAccountPendingLimitFlag,
112113
utils.TxPoolLifetimeFlag,
113114
},
114115
},

cmd/utils/flags.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,11 @@ var (
406406
Usage: "Maximum number of non-executable transaction slots for all accounts",
407407
Value: ethconfig.Defaults.TxPool.GlobalQueue,
408408
}
409+
TxPoolAccountPendingLimitFlag = cli.Uint64Flag{
410+
Name: "txpool.accountpendinglimit",
411+
Usage: "Maximum number of executable transactions allowed per account",
412+
Value: ethconfig.Defaults.TxPool.AccountPendingLimit,
413+
}
409414
TxPoolLifetimeFlag = cli.DurationFlag{
410415
Name: "txpool.lifetime",
411416
Usage: "Maximum amount of time non-executable transaction are queued",
@@ -1523,6 +1528,9 @@ func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) {
15231528
if ctx.GlobalIsSet(TxPoolGlobalQueueFlag.Name) {
15241529
cfg.GlobalQueue = ctx.GlobalUint64(TxPoolGlobalQueueFlag.Name)
15251530
}
1531+
if ctx.GlobalIsSet(TxPoolAccountPendingLimitFlag.Name) {
1532+
cfg.AccountPendingLimit = ctx.GlobalUint64(TxPoolAccountPendingLimitFlag.Name)
1533+
}
15261534
if ctx.GlobalIsSet(TxPoolLifetimeFlag.Name) {
15271535
cfg.Lifetime = ctx.GlobalDuration(TxPoolLifetimeFlag.Name)
15281536
}

core/tx_list.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,11 @@ func (l *txList) Ready(start uint64) types.Transactions {
439439

440440
// Len returns the length of the transaction list.
441441
func (l *txList) Len() int {
442-
return l.txs.Len()
442+
if l == nil {
443+
return 0
444+
} else {
445+
return l.txs.Len()
446+
}
443447
}
444448

445449
// Empty returns whether the list of transactions is empty or not.

core/tx_pool.go

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ var (
104104
pendingReplaceMeter = metrics.NewRegisteredMeter("txpool/pending/replace", nil)
105105
pendingRateLimitMeter = metrics.NewRegisteredMeter("txpool/pending/ratelimit", nil) // Dropped due to rate limiting
106106
pendingNofundsMeter = metrics.NewRegisteredMeter("txpool/pending/nofunds", nil) // Dropped due to out-of-funds
107+
pendingEvictionMeter = metrics.NewRegisteredMeter("txpool/pending/eviction", nil) // Dropped due to lifetime
107108

108109
// Metrics for the queued pool
109110
queuedDiscardMeter = metrics.NewRegisteredMeter("txpool/queued/discard", nil)
@@ -178,6 +179,8 @@ type TxPoolConfig struct {
178179
AccountQueue uint64 // Maximum number of non-executable transaction slots permitted per account
179180
GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts
180181

182+
AccountPendingLimit uint64 // Number of executable transactions allowed per account
183+
181184
Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
182185
}
183186

@@ -195,6 +198,8 @@ var DefaultTxPoolConfig = TxPoolConfig{
195198
AccountQueue: 64,
196199
GlobalQueue: 1024,
197200

201+
AccountPendingLimit: 1024,
202+
198203
Lifetime: 3 * time.Hour,
199204
}
200205

@@ -230,6 +235,10 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
230235
log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue)
231236
conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue
232237
}
238+
if conf.AccountPendingLimit < 1 {
239+
log.Warn("Sanitizing invalid txpool account pending limit", "provided", conf.AccountPendingLimit, "updated", DefaultTxPoolConfig.AccountPendingLimit)
240+
conf.AccountPendingLimit = DefaultTxPoolConfig.AccountPendingLimit
241+
}
233242
if conf.Lifetime < 1 {
234243
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime)
235244
conf.Lifetime = DefaultTxPoolConfig.Lifetime
@@ -422,6 +431,7 @@ func (pool *TxPool) loop() {
422431
// Handle inactive account transaction eviction
423432
case <-evict.C:
424433
pool.mu.Lock()
434+
// Evict queued transactions
425435
for addr := range pool.queue {
426436
// Skip local transactions from the eviction mechanism
427437
if pool.locals.contains(addr) {
@@ -431,12 +441,28 @@ func (pool *TxPool) loop() {
431441
if time.Since(pool.beats[addr]) > pool.config.Lifetime {
432442
list := pool.queue[addr].Flatten()
433443
for _, tx := range list {
434-
log.Trace("Evicting transaction due to timeout", "account", addr.Hex(), "hash", tx.Hash().Hex(), "lifetime sec", time.Since(pool.beats[addr]).Seconds(), "lifetime limit sec", pool.config.Lifetime.Seconds())
444+
log.Trace("Evicting queued transaction due to timeout", "account", addr.Hex(), "hash", tx.Hash().Hex(), "lifetime sec", time.Since(pool.beats[addr]).Seconds(), "lifetime limit sec", pool.config.Lifetime.Seconds())
435445
pool.removeTx(tx.Hash(), true)
436446
}
437447
queuedEvictionMeter.Mark(int64(len(list)))
438448
}
439449
}
450+
// Evict pending transactions
451+
for addr := range pool.pending {
452+
// Skip local transactions from the eviction mechanism
453+
if pool.locals.contains(addr) {
454+
continue
455+
}
456+
// Any non-locals old enough should be removed
457+
if time.Since(pool.beats[addr]) > pool.config.Lifetime {
458+
list := pool.pending[addr].Flatten()
459+
for _, tx := range list {
460+
log.Trace("Evicting pending transaction due to timeout", "account", addr.Hex(), "hash", tx.Hash().Hex(), "lifetime sec", time.Since(pool.beats[addr]).Seconds(), "lifetime limit sec", pool.config.Lifetime.Seconds())
461+
pool.removeTx(tx.Hash(), true)
462+
}
463+
pendingEvictionMeter.Mark(int64(len(list)))
464+
}
465+
}
440466
pool.mu.Unlock()
441467

442468
// Handle local transaction journal rotation
@@ -957,6 +983,15 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
957983
}
958984
list := pool.pending[addr]
959985

986+
// Account pending list is full
987+
if uint64(list.Len()) >= pool.config.AccountPendingLimit {
988+
pool.all.Remove(hash)
989+
pool.calculateTxsLifecycle(types.Transactions{tx}, time.Now())
990+
pool.priced.Removed(1)
991+
pendingDiscardMeter.Mark(1)
992+
return false
993+
}
994+
960995
inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead)
961996
if !inserted {
962997
// An older transaction was better, discard this
@@ -1153,6 +1188,12 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
11531188

11541189
addr, _ := types.Sender(pool.signer, tx) // already validated during insertion
11551190

1191+
defer func(addr common.Address) {
1192+
if pool.queue[addr].Empty() && pool.pending[addr].Empty() {
1193+
delete(pool.beats, addr)
1194+
}
1195+
}(addr)
1196+
11561197
// Remove it from the list of known transactions
11571198
pool.all.Remove(hash)
11581199
pool.calculateTxsLifecycle(types.Transactions{tx}, time.Now())
@@ -1189,7 +1230,6 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
11891230
}
11901231
if future.Empty() {
11911232
delete(pool.queue, addr)
1192-
delete(pool.beats, addr)
11931233
}
11941234
}
11951235
}
@@ -1544,6 +1584,8 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
15441584
// Delete the entire queue entry if it became empty.
15451585
if list.Empty() {
15461586
delete(pool.queue, addr)
1587+
}
1588+
if pool.queue[addr].Empty() && pool.pending[addr].Empty() {
15471589
delete(pool.beats, addr)
15481590
}
15491591
}
@@ -1574,6 +1616,29 @@ func (pool *TxPool) executableTxFilter(costLimit *big.Int) func(tx *types.Transa
15741616
// pending limit. The algorithm tries to reduce transaction counts by an approximately
15751617
// equal number for all for accounts with many pending transactions.
15761618
func (pool *TxPool) truncatePending() {
1619+
// Truncate pending lists to max length
1620+
for addr, list := range pool.pending {
1621+
if list.Len() > int(pool.config.AccountPendingLimit) {
1622+
caps := list.Cap(int(pool.config.AccountPendingLimit))
1623+
for _, tx := range caps {
1624+
// Drop the transaction from the global pools too
1625+
hash := tx.Hash()
1626+
pool.all.Remove(hash)
1627+
pool.calculateTxsLifecycle(types.Transactions{tx}, time.Now())
1628+
1629+
// Update the account nonce to the dropped transaction
1630+
// note: this will set pending nonce to the min nonce from the discarded txs
1631+
pool.pendingNonces.setIfLower(addr, tx.Nonce())
1632+
log.Trace("Removed pending transaction to comply with hard limit", "hash", hash.Hex())
1633+
}
1634+
pool.priced.Removed(len(caps))
1635+
pendingGauge.Dec(int64(len(caps)))
1636+
if pool.locals.contains(addr) {
1637+
localGauge.Dec(int64(len(caps)))
1638+
}
1639+
}
1640+
}
1641+
15771642
pending := uint64(0)
15781643
for _, list := range pool.pending {
15791644
pending += uint64(list.Len())

0 commit comments

Comments
 (0)