core/txpool: add pending, queued slots to txpool's stats

This commit is contained in:
Bui Quang Minh 2023-05-30 18:06:20 +07:00
parent 41ee96fdfe
commit e85fb69328
No known key found for this signature in database
GPG key ID: 9591AA13D0891B99
16 changed files with 114 additions and 78 deletions

View file

@ -1481,8 +1481,8 @@ func (p *BlobPool) Nonce(addr common.Address) uint64 {
} }
// Stats retrieves the current pool stats, namely the number of pending and the // Stats retrieves the current pool stats, namely the number of pending and the
// number of queued (non-executable) transactions. // number of queued (non-executable) transactions, slots.
func (p *BlobPool) Stats() (int, int) { func (p *BlobPool) Stats() (int, int, int, int) {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
@ -1490,7 +1490,7 @@ func (p *BlobPool) Stats() (int, int) {
for _, txs := range p.index { for _, txs := range p.index {
pending += len(txs) pending += len(txs)
} }
return pending, 0 // No non-executable txs in the blob pool return pending, 0, 0, 0 // No non-executable txs in the blob pool
} }
// Content retrieves the data content of the transaction pool, returning all the // Content retrieves the data content of the transaction pool, returning all the

View file

@ -348,12 +348,17 @@ func (pool *LegacyPool) loop() {
// Handle stats reporting ticks // Handle stats reporting ticks
case <-report.C: case <-report.C:
pool.mu.RLock() pool.mu.RLock()
pending, queued := pool.stats() pending, queued, pendingSlots, queuedSlots := pool.stats()
pool.mu.RUnlock() pool.mu.RUnlock()
stales := int(pool.priced.stales.Load()) stales := int(pool.priced.stales.Load())
if pending != prevPending || queued != prevQueued || stales != prevStales { if pending != prevPending || queued != prevQueued || stales != prevStales {
log.Debug("Transaction pool status report", "executable", pending, "queued", queued, "stales", stales) log.Debug(
"Transaction pool status report",
"executable", pending, "executable slots", pendingSlots,
"queued", queued, "queued slots", queuedSlots,
"stales", stales,
)
prevPending, prevQueued, prevStales = pending, queued, stales prevPending, prevQueued, prevStales = pending, queued, stales
} }
@ -448,27 +453,35 @@ func (pool *LegacyPool) Nonce(addr common.Address) uint64 {
return pool.pendingNonces.get(addr) return pool.pendingNonces.get(addr)
} }
// Stats retrieves the current pool stats, namely the number of pending and the // Stats retrieves the current pool stats, namely the number of pending transactions,
// number of queued (non-executable) transactions. // slots, and the number of queued (non-executable) transactions, slots.
func (pool *LegacyPool) Stats() (int, int) { func (pool *LegacyPool) Stats() (int, int, int, int) {
pool.mu.RLock() pool.mu.RLock()
defer pool.mu.RUnlock() defer pool.mu.RUnlock()
return pool.stats() return pool.stats()
} }
// stats retrieves the current pool stats, namely the number of pending and the // stats retrieves the current pool stats, namely the number of pending transactions,
// number of queued (non-executable) transactions. // slots and the number of queued (non-executable) transactions, slots.
func (pool *LegacyPool) stats() (int, int) { func (pool *LegacyPool) stats() (int, int, int, int) {
pending := 0 pending := 0
for _, list := range pool.pending { for _, list := range pool.pending {
pending += list.Len() pending += list.Len()
} }
pendingSlots := 0
for _, list := range pool.pending {
pendingSlots += list.TotalSlots()
}
queued := 0 queued := 0
for _, list := range pool.queue { for _, list := range pool.queue {
queued += list.Len() queued += list.Len()
} }
return pending, queued queuedSlots := 0
for _, list := range pool.queue {
queuedSlots += list.TotalSlots()
}
return pending, queued, pendingSlots, queuedSlots
} }
// Content retrieves the data content of the transaction pool, returning all the // Content retrieves the data content of the transaction pool, returning all the

View file

@ -35,7 +35,7 @@ func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gaspric
func count(t *testing.T, pool *LegacyPool) (pending int, queued int) { func count(t *testing.T, pool *LegacyPool) (pending int, queued int) {
t.Helper() t.Helper()
pending, queued = pool.stats() pending, queued, _, _ = pool.stats()
if err := validatePoolInternals(pool); err != nil { if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err) t.Fatalf("pool internal state corrupted: %v", err)
} }
@ -58,7 +58,7 @@ func fillPool(t testing.TB, pool *LegacyPool) {
// Import the batch and verify that limits have been enforced // Import the batch and verify that limits have been enforced
pool.addRemotesSync(executableTxs) pool.addRemotesSync(executableTxs)
pool.addRemotesSync(nonExecutableTxs) pool.addRemotesSync(nonExecutableTxs)
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
slots := pool.all.Slots() slots := pool.all.Slots()
// sanity-check that the test prerequisites are ok (pending full) // sanity-check that the test prerequisites are ok (pending full)
if have, want := pending, slots; have != want { if have, want := pending, slots; have != want {
@ -87,7 +87,7 @@ func TestTransactionFutureAttack(t *testing.T) {
pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver())
defer pool.Close() defer pool.Close()
fillPool(t, pool) fillPool(t, pool)
pending, _ := pool.Stats() pending, _, _, _ := pool.Stats()
// Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops
{ {
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
@ -102,7 +102,7 @@ func TestTransactionFutureAttack(t *testing.T) {
t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots()) t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots())
} }
} }
newPending, _ := pool.Stats() newPending, _, _, _ := pool.Stats()
// Pending should not have been touched // Pending should not have been touched
if have, want := newPending, pending; have < want { if have, want := newPending, pending; have < want {
t.Errorf("wrong pending-count, have %d, want %d (GlobalSlots: %d)", t.Errorf("wrong pending-count, have %d, want %d (GlobalSlots: %d)",
@ -123,7 +123,7 @@ func TestTransactionFuture1559(t *testing.T) {
// Create a number of test accounts, fund them and make transactions // Create a number of test accounts, fund them and make transactions
fillPool(t, pool) fillPool(t, pool)
pending, _ := pool.Stats() pending, _, _, _ := pool.Stats()
// Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops
{ {
@ -135,7 +135,7 @@ func TestTransactionFuture1559(t *testing.T) {
} }
pool.addRemotesSync(futureTxs) pool.addRemotesSync(futureTxs)
} }
newPending, _ := pool.Stats() newPending, _, _, _ := pool.Stats()
// Pending should not have been touched // Pending should not have been touched
if have, want := newPending, pending; have != want { if have, want := newPending, pending; have != want {
t.Errorf("Wrong pending-count, have %d, want %d (GlobalSlots: %d)", t.Errorf("Wrong pending-count, have %d, want %d (GlobalSlots: %d)",

View file

@ -177,7 +177,7 @@ func validatePoolInternals(pool *LegacyPool) error {
defer pool.mu.RUnlock() defer pool.mu.RUnlock()
// Ensure the total transaction set is consistent with pending + queued // Ensure the total transaction set is consistent with pending + queued
pending, queued := pool.stats() pending, queued, _, _ := pool.stats()
if total := pool.all.Count(); total != pending+queued { if total := pool.all.Count(); total != pending+queued {
return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued) return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued)
} }
@ -828,7 +828,7 @@ func TestGapFilling(t *testing.T) {
transaction(0, 100000, key), transaction(0, 100000, key),
transaction(2, 100000, key), transaction(2, 100000, key),
}) })
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 1 { if pending != 1 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1)
} }
@ -845,7 +845,7 @@ func TestGapFilling(t *testing.T) {
if err := pool.addRemoteSync(transaction(1, 100000, key)); err != nil { if err := pool.addRemoteSync(transaction(1, 100000, key)); err != nil {
t.Fatalf("failed to add gapped transaction: %v", err) t.Fatalf("failed to add gapped transaction: %v", err)
} }
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 3 { if pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
} }
@ -1029,7 +1029,7 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
if err := pool.addRemote(pricedTransaction(1, 100000, big.NewInt(1), remote)); err != nil { if err := pool.addRemote(pricedTransaction(1, 100000, big.NewInt(1), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err) t.Fatalf("failed to add remote transaction: %v", err)
} }
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 0 { if pending != 0 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
} }
@ -1044,7 +1044,7 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
time.Sleep(2 * evictionInterval) time.Sleep(2 * evictionInterval)
// Transactions should not be evicted from the queue yet since lifetime duration has not passed // Transactions should not be evicted from the queue yet since lifetime duration has not passed
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 0 { if pending != 0 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
} }
@ -1058,7 +1058,7 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
// Wait a bit for eviction to run and clean up any leftovers, and ensure only the local remains // Wait a bit for eviction to run and clean up any leftovers, and ensure only the local remains
time.Sleep(2 * config.Lifetime) time.Sleep(2 * config.Lifetime)
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 0 { if pending != 0 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
} }
@ -1081,7 +1081,7 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
<-pool.requestReset(nil, nil) <-pool.requestReset(nil, nil)
// make sure queue, pending are cleared // make sure queue, pending are cleared
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 0 { if pending != 0 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
} }
@ -1111,7 +1111,7 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
time.Sleep(6 * evictionInterval) time.Sleep(6 * evictionInterval)
// All gapped transactions shouldn't be kicked out // All gapped transactions shouldn't be kicked out
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
@ -1124,7 +1124,7 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
// The whole life time pass after last promotion, kick out stale transactions // The whole life time pass after last promotion, kick out stale transactions
time.Sleep(2 * config.Lifetime) time.Sleep(2 * config.Lifetime)
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
@ -1275,7 +1275,7 @@ func TestAllowedTxSize(t *testing.T) {
t.Fatalf("expected rejection on oversize transaction") t.Fatalf("expected rejection on oversize transaction")
} }
// Run some sanity checks on the pool internals // Run some sanity checks on the pool internals
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
@ -1415,7 +1415,7 @@ func TestRepricing(t *testing.T) {
pool.addRemotesSync(txs) pool.addRemotesSync(txs)
pool.addLocal(ltx) pool.addLocal(ltx)
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 7 { if pending != 7 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7)
} }
@ -1431,7 +1431,7 @@ func TestRepricing(t *testing.T) {
// Reprice the pool and check that underpriced transactions get dropped // Reprice the pool and check that underpriced transactions get dropped
pool.SetGasTip(big.NewInt(2)) pool.SetGasTip(big.NewInt(2))
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
@ -1465,7 +1465,7 @@ func TestRepricing(t *testing.T) {
if err := pool.addLocal(tx); err != nil { if err := pool.addLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err) t.Fatalf("failed to add underpriced local transaction: %v", err)
} }
if pending, _ = pool.Stats(); pending != 3 { if pending, _, _, _ = pool.Stats(); pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
} }
if err := validateEvents(events, 1); err != nil { if err := validateEvents(events, 1); err != nil {
@ -1536,7 +1536,7 @@ func TestRepricingDynamicFee(t *testing.T) {
pool.addRemotesSync(txs) pool.addRemotesSync(txs)
pool.addLocal(ltx) pool.addLocal(ltx)
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 7 { if pending != 7 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7)
} }
@ -1552,7 +1552,7 @@ func TestRepricingDynamicFee(t *testing.T) {
// Reprice the pool and check that underpriced transactions get dropped // Reprice the pool and check that underpriced transactions get dropped
pool.SetGasTip(big.NewInt(2)) pool.SetGasTip(big.NewInt(2))
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
@ -1589,7 +1589,7 @@ func TestRepricingDynamicFee(t *testing.T) {
if err := pool.addLocal(tx); err != nil { if err := pool.addLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err) t.Fatalf("failed to add underpriced local transaction: %v", err)
} }
if pending, _ = pool.Stats(); pending != 3 { if pending, _, _, _ = pool.Stats(); pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
} }
if err := validateEvents(events, 1); err != nil { if err := validateEvents(events, 1); err != nil {
@ -1662,10 +1662,10 @@ func TestRepricingKeepsLocals(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
expPending, expQueued := 1000, 1000 expPending, expQueued := 1000, 1000
validate := func() { validate := func() {
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != expPending { if pending != expPending {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, expPending) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, expPending)
} }
@ -1735,7 +1735,7 @@ func TestUnderpricing(t *testing.T) {
pool.addRemotes(txs) pool.addRemotes(txs)
pool.addLocal(ltx) pool.addLocal(ltx)
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 3 { if pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
} }
@ -1770,7 +1770,7 @@ func TestUnderpricing(t *testing.T) {
if err := pool.addRemote(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != txpool.ErrFutureReplacePending { if err := pool.addRemote(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != txpool.ErrFutureReplacePending {
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, txpool.ErrFutureReplacePending) t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, txpool.ErrFutureReplacePending)
} }
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
@ -1792,7 +1792,7 @@ func TestUnderpricing(t *testing.T) {
if err := pool.addLocal(ltx); err != nil { if err := pool.addLocal(ltx); err != nil {
t.Fatalf("failed to add new underpriced local transaction: %v", err) t.Fatalf("failed to add new underpriced local transaction: %v", err)
} }
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 3 { if pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
} }
@ -1843,7 +1843,7 @@ func TestStableUnderpricing(t *testing.T) {
} }
pool.addRemotesSync(txs) pool.addRemotesSync(txs)
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != int(config.GlobalSlots) { if pending != int(config.GlobalSlots) {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots)
} }
@ -1860,7 +1860,7 @@ func TestStableUnderpricing(t *testing.T) {
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil {
t.Fatalf("failed to add well priced transaction: %v", err) t.Fatalf("failed to add well priced transaction: %v", err)
} }
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != int(config.GlobalSlots) { if pending != int(config.GlobalSlots) {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots)
} }
@ -1914,7 +1914,7 @@ func TestUnderpricingDynamicFee(t *testing.T) {
pool.addRemotes(txs) // Pend K0:0, K0:1; Que K1:1 pool.addRemotes(txs) // Pend K0:0, K0:1; Que K1:1
pool.addLocal(ltx) // +K2:0 => Pend K0:0, K0:1, K2:0; Que K1:1 pool.addLocal(ltx) // +K2:0 => Pend K0:0, K0:1, K2:0; Que K1:1
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 3 { if pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
} }
@ -1948,7 +1948,7 @@ func TestUnderpricingDynamicFee(t *testing.T) {
if err := pool.addRemoteSync(tx); err != nil { // +K1:3, -K1:0 => Pend K0:0 K2:0; Que K1:2 K1:3 if err := pool.addRemoteSync(tx); err != nil { // +K1:3, -K1:0 => Pend K0:0 K2:0; Que K1:2 K1:3
t.Fatalf("failed to add well priced transaction: %v", err) t.Fatalf("failed to add well priced transaction: %v", err)
} }
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
@ -1970,7 +1970,7 @@ func TestUnderpricingDynamicFee(t *testing.T) {
if err := pool.addLocal(ltx); err != nil { if err := pool.addLocal(ltx); err != nil {
t.Fatalf("failed to add new underpriced local transaction: %v", err) t.Fatalf("failed to add new underpriced local transaction: %v", err)
} }
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 3 { if pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
} }
@ -2022,7 +2022,7 @@ func TestDualHeapEviction(t *testing.T) {
} }
pool.addRemotesSync([]*types.Transaction{tx}) pool.addRemotesSync([]*types.Transaction{tx})
} }
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending+queued != 20 { if pending+queued != 20 {
t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 10) t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 10)
} }
@ -2076,7 +2076,7 @@ func TestDeduplication(t *testing.T) {
t.Errorf("add %d failed: %v", i, err) t.Errorf("add %d failed: %v", i, err)
} }
} }
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 1 { if pending != 1 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1)
} }
@ -2096,7 +2096,7 @@ func TestDeduplication(t *testing.T) {
t.Errorf("add %d failed: %v", i, err) t.Errorf("add %d failed: %v", i, err)
} }
} }
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != len(txs) { if pending != len(txs) {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, len(txs)) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, len(txs))
} }
@ -2351,7 +2351,7 @@ func testJournaling(t *testing.T, nolocals bool) {
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), remote)); err != nil { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err) t.Fatalf("failed to add remote transaction: %v", err)
} }
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 4 { if pending != 4 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4)
} }
@ -2369,7 +2369,7 @@ func testJournaling(t *testing.T, nolocals bool) {
pool = New(config, blockchain) pool = New(config, blockchain)
pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver())
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if queued != 0 { if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0) t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
} }
@ -2396,7 +2396,7 @@ func testJournaling(t *testing.T, nolocals bool) {
pool = New(config, blockchain) pool = New(config, blockchain)
pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver()) pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver())
pending, queued = pool.Stats() pending, queued, _, _ = pool.Stats()
if pending != 0 { if pending != 0 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
} }
@ -2445,7 +2445,7 @@ func TestStatusCheck(t *testing.T) {
// Import the transaction and ensure they are correctly added // Import the transaction and ensure they are correctly added
pool.addRemotesSync(txs) pool.addRemotesSync(txs)
pending, queued := pool.Stats() pending, queued, _, _ := pool.Stats()
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }

View file

@ -57,6 +57,7 @@ type sortedMap struct {
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode) index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
cache types.Transactions // Cache of the transactions already sorted cache types.Transactions // Cache of the transactions already sorted
cacheMu sync.Mutex // Mutex covering the cache cacheMu sync.Mutex // Mutex covering the cache
totalslots int // Total number of slots of all transactions in the list
} }
// newSortedMap creates a new nonce-sorted transaction map. // newSortedMap creates a new nonce-sorted transaction map.
@ -78,7 +79,11 @@ func (m *sortedMap) Put(tx *types.Transaction) {
nonce := tx.Nonce() nonce := tx.Nonce()
if m.items[nonce] == nil { if m.items[nonce] == nil {
heap.Push(m.index, nonce) heap.Push(m.index, nonce)
} else {
m.totalslots -= numSlots(m.items[nonce])
} }
m.totalslots += numSlots(tx)
m.cacheMu.Lock() m.cacheMu.Lock()
m.items[nonce], m.cache = tx, nil m.items[nonce], m.cache = tx, nil
m.cacheMu.Unlock() m.cacheMu.Unlock()
@ -94,6 +99,7 @@ func (m *sortedMap) Forward(threshold uint64) types.Transactions {
for m.index.Len() > 0 && (*m.index)[0] < threshold { for m.index.Len() > 0 && (*m.index)[0] < threshold {
nonce := heap.Pop(m.index).(uint64) nonce := heap.Pop(m.index).(uint64)
removed = append(removed, m.items[nonce]) removed = append(removed, m.items[nonce])
m.totalslots -= numSlots(m.items[nonce])
delete(m.items, nonce) delete(m.items, nonce)
} }
// If we had a cached order, shift the front // If we had a cached order, shift the front
@ -139,6 +145,7 @@ func (m *sortedMap) filter(filter func(*types.Transaction) bool) types.Transacti
for nonce, tx := range m.items { for nonce, tx := range m.items {
if filter(tx) { if filter(tx) {
removed = append(removed, tx) removed = append(removed, tx)
m.totalslots -= numSlots(m.items[nonce])
delete(m.items, nonce) delete(m.items, nonce)
} }
} }
@ -192,6 +199,7 @@ func (m *sortedMap) Remove(nonce uint64) bool {
break break
} }
} }
m.totalslots -= numSlots(m.items[nonce])
delete(m.items, nonce) delete(m.items, nonce)
m.cacheMu.Lock() m.cacheMu.Lock()
m.cache = nil m.cache = nil
@ -216,6 +224,7 @@ func (m *sortedMap) Ready(start uint64) types.Transactions {
var ready types.Transactions var ready types.Transactions
for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ {
ready = append(ready, m.items[next]) ready = append(ready, m.items[next])
m.totalslots -= numSlots(m.items[next])
delete(m.items, next) delete(m.items, next)
heap.Pop(m.index) heap.Pop(m.index)
} }
@ -452,6 +461,11 @@ func (l *list) LastElement() *types.Transaction {
return l.txs.LastElement() return l.txs.LastElement()
} }
// TotalSlots returns total slots of transaction list.
func (l *list) TotalSlots() int {
return l.txs.totalslots
}
// subTotalCost subtracts the cost of the given transactions from the // subTotalCost subtracts the cost of the given transactions from the
// total cost of all transactions. // total cost of all transactions.
func (l *list) subTotalCost(txs []*types.Transaction) { func (l *list) subTotalCost(txs []*types.Transaction) {

View file

@ -107,8 +107,8 @@ type SubPool interface {
Nonce(addr common.Address) uint64 Nonce(addr common.Address) uint64
// Stats retrieves the current pool stats, namely the number of pending and the // Stats retrieves the current pool stats, namely the number of pending and the
// number of queued (non-executable) transactions. // number of queued (non-executable) transactions, slots.
Stats() (int, int) Stats() (pending, queued, pendingSlots, queuedSlots int)
// Content retrieves the data content of the transaction pool, returning all the // Content retrieves the data content of the transaction pool, returning all the
// pending as well as queued transactions, grouped by account and sorted by nonce. // pending as well as queued transactions, grouped by account and sorted by nonce.

View file

@ -342,16 +342,18 @@ func (p *TxPool) Nonce(addr common.Address) uint64 {
} }
// Stats retrieves the current pool stats, namely the number of pending and the // Stats retrieves the current pool stats, namely the number of pending and the
// number of queued (non-executable) transactions. // number of queued (non-executable) transactions, slots.
func (p *TxPool) Stats() (int, int) { func (p *TxPool) Stats() (int, int, int, int) {
var runnable, blocked int var runnable, blocked, runnableSlots, blockedSlots int
for _, subpool := range p.subpools { for _, subpool := range p.subpools {
run, block := subpool.Stats() run, block, runSlots, blockSlots := subpool.Stats()
runnable += run runnable += run
blocked += block blocked += block
runnableSlots += runSlots
blockedSlots += blockSlots
} }
return runnable, blocked return runnable, blocked, runnableSlots, blockedSlots
} }
// Content retrieves the data content of the transaction pool, returning all the // Content retrieves the data content of the transaction pool, returning all the

View file

@ -311,7 +311,7 @@ func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (
return b.eth.txPool.Nonce(addr), nil return b.eth.txPool.Nonce(addr), nil
} }
func (b *EthAPIBackend) Stats() (runnable int, blocked int) { func (b *EthAPIBackend) Stats() (pending, queued, pendingSlots, queuedSlots int) {
return b.eth.txPool.Stats() return b.eth.txPool.Stats()
} }

View file

@ -68,7 +68,7 @@ type backend interface {
CurrentHeader() *types.Header CurrentHeader() *types.Header
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
GetTd(ctx context.Context, hash common.Hash) *big.Int GetTd(ctx context.Context, hash common.Hash) *big.Int
Stats() (pending int, queued int) Stats() (pending, queued, pendingSlots, pendingQueued int)
SyncProgress() ethereum.SyncProgress SyncProgress() ethereum.SyncProgress
} }
@ -741,7 +741,7 @@ type pendStats struct {
// it to the stats server. // it to the stats server.
func (s *Service) reportPending(conn *connWrapper) error { func (s *Service) reportPending(conn *connWrapper) error {
// Retrieve the pending count from the local blockchain // Retrieve the pending count from the local blockchain
pending, _ := s.backend.Stats() pending, _, _, _ := s.backend.Stats()
// Assemble the transaction stats and send it to the server // Assemble the transaction stats and send it to the server
log.Trace("Sending pending transactions to ethstats", "count", pending) log.Trace("Sending pending transactions to ethstats", "count", pending)

View file

@ -211,10 +211,12 @@ func (s *TxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCT
// Status returns the number of pending and queued transaction in the pool. // Status returns the number of pending and queued transaction in the pool.
func (s *TxPoolAPI) Status() map[string]hexutil.Uint { func (s *TxPoolAPI) Status() map[string]hexutil.Uint {
pending, queue := s.b.Stats() pending, queue, pendingSlots, queuedSlots := s.b.Stats()
return map[string]hexutil.Uint{ return map[string]hexutil.Uint{
"pending": hexutil.Uint(pending), "pending": hexutil.Uint(pending),
"queued": hexutil.Uint(queue), "queued": hexutil.Uint(queue),
"pending slots": hexutil.Uint(pendingSlots),
"queued slots": hexutil.Uint(queuedSlots),
} }
} }

View file

@ -569,7 +569,7 @@ func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction {
func (b testBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { func (b testBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
panic("implement me") panic("implement me")
} }
func (b testBackend) Stats() (pending int, queued int) { panic("implement me") } func (b testBackend) Stats() (pending, queued, pendingSlots, queuedSlots int) { panic("implement me") }
func (b testBackend) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) { func (b testBackend) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {
panic("implement me") panic("implement me")
} }

View file

@ -79,7 +79,7 @@ type Backend interface {
GetPoolTransactions() (types.Transactions, error) GetPoolTransactions() (types.Transactions, error)
GetPoolTransaction(txHash common.Hash) *types.Transaction GetPoolTransaction(txHash common.Hash) *types.Transaction
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
Stats() (pending int, queued int) Stats() (pending, queued, pendingSlots, queuedSlots int)
TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction)
TxPoolContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) TxPoolContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction)
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription

View file

@ -324,7 +324,7 @@ func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction
func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
return 0, nil return 0, nil
} }
func (b *backendMock) Stats() (pending int, queued int) { return 0, 0 } func (b *backendMock) Stats() (pending, queued, pendingSlots, queuedSlots int) { return 0, 0, 0, 0 }
func (b *backendMock) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) { func (b *backendMock) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {
return nil, nil return nil, nil
} }

View file

@ -219,8 +219,13 @@ func (b *LesApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (
return b.eth.txPool.GetNonce(ctx, addr) return b.eth.txPool.GetNonce(ctx, addr)
} }
func (b *LesApiBackend) Stats() (pending int, queued int) { func (b *LesApiBackend) Stats() (pending, queued, pendingSlots, queuedSlots int) {
return b.eth.txPool.Stats(), 0 pending = b.eth.txPool.Stats()
// For simplicity, just return number of pending transactions in pending slots
// without calculating the exact number of slots here
pendingSlots = pending
return pending, 0, pendingSlots, 0
} }
func (b *LesApiBackend) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) { func (b *LesApiBackend) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {

View file

@ -652,12 +652,12 @@ func testTransactionStatus(t *testing.T, protocol int) {
} }
// wait until TxPool processes the inserted block // wait until TxPool processes the inserted block
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
if pending, _ := server.handler.txpool.Stats(); pending == 1 { if pending, _, _, _ := server.handler.txpool.Stats(); pending == 1 {
break break
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
} }
if pending, _ := server.handler.txpool.Stats(); pending != 1 { if pending, _, _, _ := server.handler.txpool.Stats(); pending != 1 {
t.Fatalf("pending count mismatch: have %d, want 1", pending) t.Fatalf("pending count mismatch: have %d, want 1", pending)
} }
// Discard new block announcement // Discard new block announcement
@ -677,12 +677,12 @@ func testTransactionStatus(t *testing.T, protocol int) {
} }
// wait until TxPool processes the reorg // wait until TxPool processes the reorg
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
if pending, _ := server.handler.txpool.Stats(); pending == 3 { if pending, _, _, _ := server.handler.txpool.Stats(); pending == 3 {
break break
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
} }
if pending, _ := server.handler.txpool.Stats(); pending != 3 { if pending, _, _, _ := server.handler.txpool.Stats(); pending != 3 {
t.Fatalf("pending count mismatch: have %d, want 3", pending) t.Fatalf("pending count mismatch: have %d, want 3", pending)
} }
// Discard new block announcement // Discard new block announcement

View file

@ -138,7 +138,7 @@ func main() {
nonces[index]++ nonces[index]++
// Wait if we're too saturated // Wait if we're too saturated
if pend, _ := backend.TxPool().Stats(); pend > 2048 { if pend, _, _, _ := backend.TxPool().Stats(); pend > 2048 {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
} }
} }