mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
eth/downloader: don't process trie nodes on the queue thread
This commit is contained in:
parent
d18b509e40
commit
dbb742efc4
1 changed files with 34 additions and 19 deletions
|
|
@ -102,6 +102,7 @@ type queue struct {
|
||||||
stateDatabase ethdb.Database // [eth/63] Trie database to populate during state reassembly
|
stateDatabase ethdb.Database // [eth/63] Trie database to populate during state reassembly
|
||||||
stateScheduler *state.StateSync // [eth/63] State trie synchronisation scheduler and integrator
|
stateScheduler *state.StateSync // [eth/63] State trie synchronisation scheduler and integrator
|
||||||
stateWriters int // [eth/63] Number of running state DB writer goroutines
|
stateWriters int // [eth/63] Number of running state DB writer goroutines
|
||||||
|
stateSchedLock sync.RWMutex // [eth/63] Lock serialising access to the state scheduler
|
||||||
|
|
||||||
resultCache []*fetchResult // Downloaded but not yet delivered fetch results
|
resultCache []*fetchResult // Downloaded but not yet delivered fetch results
|
||||||
resultOffset uint64 // Offset of the first cached fetch result in the block chain
|
resultOffset uint64 // Offset of the first cached fetch result in the block chain
|
||||||
|
|
@ -140,6 +141,9 @@ func (q *queue) Reset() {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
|
q.stateSchedLock.Lock()
|
||||||
|
defer q.stateSchedLock.Unlock()
|
||||||
|
|
||||||
q.closed = false
|
q.closed = false
|
||||||
q.mode = FullSync
|
q.mode = FullSync
|
||||||
q.fastSyncPivot = 0
|
q.fastSyncPivot = 0
|
||||||
|
|
@ -203,15 +207,9 @@ func (q *queue) PendingReceipts() int {
|
||||||
|
|
||||||
// PendingNodeData retrieves the number of node data entries pending for retrieval.
|
// PendingNodeData retrieves the number of node data entries pending for retrieval.
|
||||||
func (q *queue) PendingNodeData() int {
|
func (q *queue) PendingNodeData() int {
|
||||||
q.lock.Lock()
|
q.stateSchedLock.RLock()
|
||||||
defer q.lock.Unlock()
|
defer q.stateSchedLock.RUnlock()
|
||||||
|
|
||||||
return q.pendingNodeDataLocked()
|
|
||||||
}
|
|
||||||
|
|
||||||
// pendingNodeDataLocked retrieves the number of node data entries pending for retrieval.
|
|
||||||
// The caller must hold q.lock.
|
|
||||||
func (q *queue) pendingNodeDataLocked() int {
|
|
||||||
var n int
|
var n int
|
||||||
if q.stateScheduler != nil {
|
if q.stateScheduler != nil {
|
||||||
n = q.stateScheduler.Pending()
|
n = q.stateScheduler.Pending()
|
||||||
|
|
@ -269,9 +267,12 @@ func (q *queue) Idle() bool {
|
||||||
pending := len(q.blockPendPool) + len(q.receiptPendPool) + len(q.statePendPool)
|
pending := len(q.blockPendPool) + len(q.receiptPendPool) + len(q.statePendPool)
|
||||||
cached := len(q.blockDonePool) + len(q.receiptDonePool)
|
cached := len(q.blockDonePool) + len(q.receiptDonePool)
|
||||||
|
|
||||||
|
q.stateSchedLock.RLock()
|
||||||
if q.stateScheduler != nil {
|
if q.stateScheduler != nil {
|
||||||
queued += q.stateScheduler.Pending()
|
queued += q.stateScheduler.Pending()
|
||||||
}
|
}
|
||||||
|
q.stateSchedLock.RUnlock()
|
||||||
|
|
||||||
return (queued + pending + cached) == 0
|
return (queued + pending + cached) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,8 +400,9 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header {
|
||||||
for _, req := range q.statePendPool {
|
for _, req := range q.statePendPool {
|
||||||
req.Hashes = make(map[common.Hash]int) // Make sure executing requests fail, but don't disappear
|
req.Hashes = make(map[common.Hash]int) // Make sure executing requests fail, but don't disappear
|
||||||
}
|
}
|
||||||
|
q.stateSchedLock.Lock()
|
||||||
q.stateScheduler = state.NewStateSync(header.Root, q.stateDatabase)
|
q.stateScheduler = state.NewStateSync(header.Root, q.stateDatabase)
|
||||||
|
q.stateSchedLock.Unlock()
|
||||||
}
|
}
|
||||||
inserts = append(inserts, header)
|
inserts = append(inserts, header)
|
||||||
q.headerHead = hash
|
q.headerHead = hash
|
||||||
|
|
@ -459,7 +461,7 @@ func (q *queue) countProcessableItems() int {
|
||||||
// resultCache has space for fsHeaderForceVerify items. Not
|
// resultCache has space for fsHeaderForceVerify items. Not
|
||||||
// doing this could leave us unable to download the required
|
// doing this could leave us unable to download the required
|
||||||
// amount of headers.
|
// amount of headers.
|
||||||
if i > 0 || len(q.stateTaskPool) > 0 || q.pendingNodeDataLocked() > 0 {
|
if i > 0 || len(q.stateTaskPool) > 0 || q.PendingNodeData() > 0 {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
for j := 0; j < fsHeaderForceVerify; j++ {
|
for j := 0; j < fsHeaderForceVerify; j++ {
|
||||||
|
|
@ -525,6 +527,9 @@ func (q *queue) ReserveNodeData(p *peer, count int) *fetchRequest {
|
||||||
// Create a task generator to fetch status-fetch tasks if all schedules ones are done
|
// Create a task generator to fetch status-fetch tasks if all schedules ones are done
|
||||||
generator := func(max int) {
|
generator := func(max int) {
|
||||||
if q.stateScheduler != nil {
|
if q.stateScheduler != nil {
|
||||||
|
q.stateSchedLock.Lock()
|
||||||
|
defer q.stateSchedLock.Unlock()
|
||||||
|
|
||||||
for _, hash := range q.stateScheduler.Missing(max) {
|
for _, hash := range q.stateScheduler.Missing(max) {
|
||||||
q.stateTaskPool[hash] = q.stateTaskIndex
|
q.stateTaskPool[hash] = q.stateTaskIndex
|
||||||
q.stateTaskQueue.Push(hash, -float32(q.stateTaskIndex))
|
q.stateTaskQueue.Push(hash, -float32(q.stateTaskIndex))
|
||||||
|
|
@ -1083,18 +1088,27 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(int, boo
|
||||||
for hash, index := range request.Hashes {
|
for hash, index := range request.Hashes {
|
||||||
q.stateTaskQueue.Push(hash, float32(index))
|
q.stateTaskQueue.Push(hash, float32(index))
|
||||||
}
|
}
|
||||||
if q.stateScheduler == nil {
|
|
||||||
return 0, errNoFetchesPending
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run valid nodes through the trie download scheduler. It writes completed nodes to a
|
// Run valid nodes through the trie download scheduler. It writes completed nodes to a
|
||||||
// batch, which is committed asynchronously. This may lead to over-fetches because the
|
// batch and commits them asynchronously. This may lead to over-fetches because the
|
||||||
// scheduler treats everything as written after Process has returned, but it's
|
// scheduler treats everything as written after Process has returned, but it's
|
||||||
// unlikely to be an issue in practice.
|
// unlikely to be an issue in practice.
|
||||||
batch := q.stateDatabase.NewBatch()
|
q.stateWriters++
|
||||||
progressed, nproc, procerr := q.stateScheduler.Process(process, batch)
|
|
||||||
q.stateWriters += 1
|
|
||||||
go func() {
|
go func() {
|
||||||
|
var (
|
||||||
|
progressed bool
|
||||||
|
nproc int
|
||||||
|
procerr error
|
||||||
|
batch ethdb.Batch
|
||||||
|
)
|
||||||
|
q.stateSchedLock.Lock()
|
||||||
|
if q.stateScheduler == nil {
|
||||||
|
procerr = errNoFetchesPending
|
||||||
|
} else {
|
||||||
|
batch = q.stateDatabase.NewBatch()
|
||||||
|
progressed, nproc, procerr = q.stateScheduler.Process(process, batch)
|
||||||
|
}
|
||||||
|
q.stateSchedLock.Unlock()
|
||||||
|
|
||||||
if procerr == nil {
|
if procerr == nil {
|
||||||
nproc = len(process)
|
nproc = len(process)
|
||||||
procerr = batch.Write()
|
procerr = batch.Write()
|
||||||
|
|
@ -1103,8 +1117,9 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(int, boo
|
||||||
// number of writers is decremented prior to the call so PendingNodeData will
|
// number of writers is decremented prior to the call so PendingNodeData will
|
||||||
// return zero when the callback runs.
|
// return zero when the callback runs.
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
q.stateWriters -= 1
|
q.stateWriters--
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
|
|
||||||
callback(nproc, progressed, procerr)
|
callback(nproc, progressed, procerr)
|
||||||
// Wake up WaitResults after the state has been written because it might be
|
// Wake up WaitResults after the state has been written because it might be
|
||||||
// waiting for completion of the pivot block's state download.
|
// waiting for completion of the pivot block's state download.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue