Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1505,7 +1505,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
}

// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
SenderCacher.RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
SenderCacher().RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SenderCacher() is now lazily initialized and will allocate the task channel and spawn worker goroutines on first use. insertChain runs with the chain mutex held, so the first call can shift initialization cost into a lock-held critical path. Consider initializing the cacher before acquiring bc.chainmu (or during blockchain startup) to avoid a one-time pause during the first chain insertion.

Copilot uses AI. Check for mistakes.

// A queued approach to delivering events. This is generally
// faster than direct delivery and requires much less mutex
Expand Down
13 changes: 11 additions & 2 deletions core/sender_cacher.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,21 @@ package core

import (
"runtime"
"sync"

"github.com/XinFinOrg/XDPoSChain/core/types"
)

// SenderCacher is a concurrent transaction sender recoverer and cacher.
var SenderCacher = newTxSenderCacher(runtime.NumCPU())
// senderCacherOnce is used to ensure that the SenderCacher is initialized only once.
var senderCacherOnce = sync.OnceValue(func() *txSenderCacher {
return newTxSenderCacher(runtime.NumCPU())
})

// SenderCacher returns the singleton instance of SenderCacher, initializing it if called for the first time.
// This function is thread-safe and ensures that initialization happens only once.
func SenderCacher() *txSenderCacher {
return senderCacherOnce()
}
Comment on lines +26 to +35
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing SenderCacher from an exported variable to an exported function is a breaking API change for any downstream code importing core (call sites must switch from core.SenderCacher.Recover... to core.SenderCacher().Recover...). If preserving public API compatibility matters, consider keeping the exported var SenderCacher and moving the sync.Once-guarded initialization inside its methods (or introducing a new getter name while keeping the old var during a deprecation window).

Copilot uses AI. Check for mistakes.

// txSenderCacherRequest is a request for recovering transaction senders with a
// specific signature scheme and caching it into the transactions themselves.
Expand Down
2 changes: 1 addition & 1 deletion core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1613,7 +1613,7 @@ func (pool *LegacyPool) reset(oldHead, newHead *types.Header) {

// Inject any transactions discarded due to reorgs
log.Debug("Reinjecting stale transactions", "count", len(reinject))
core.SenderCacher.Recover(pool.signer, reinject)
core.SenderCacher().Recover(pool.signer, reinject)
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

core.SenderCacher() is now lazily initialized and may start runtime.NumCPU() goroutines on first use. In this code path reset is executed under pool.mu (see runReorg), so the first call could add noticeable latency while holding the txpool lock. Consider ensuring the cacher is initialized before taking the pool mutex (e.g., warm it up earlier in the reorg loop) so initialization work doesn’t happen in this critical section.

Copilot uses AI. Check for mistakes.
pool.addTxsLocked(reinject, false)
}

Expand Down
Loading