mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
eth/downloader: review concerns + simplify resultcache and queue
This commit is contained in:
parent
757ce48d87
commit
86a371694b
4 changed files with 31 additions and 63 deletions
|
|
@ -367,7 +367,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
|
||||||
d.stateBloom.Close()
|
d.stateBloom.Close()
|
||||||
}
|
}
|
||||||
// Reset the queue, peer set and wake channels to clean any internal leftover state
|
// Reset the queue, peer set and wake channels to clean any internal leftover state
|
||||||
d.queue.Reset()
|
d.queue.Reset(blockCacheItems)
|
||||||
d.peers.Reset()
|
d.peers.Reset()
|
||||||
|
|
||||||
for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} {
|
for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} {
|
||||||
|
|
@ -1644,7 +1644,6 @@ func (d *Downloader) processFastSyncContent(latest *types.Header) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
P, beforeP, afterP := splitAroundPivot(pivot, results)
|
P, beforeP, afterP := splitAroundPivot(pivot, results)
|
||||||
results = nil
|
|
||||||
if err := d.commitFastSyncData(beforeP, sync); err != nil {
|
if err := d.commitFastSyncData(beforeP, sync); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
|
||||||
|
|
||||||
// SetBodyDone flags the body as finished.
|
// SetBodyDone flags the body as finished.
|
||||||
func (f *fetchResult) SetBodyDone() {
|
func (f *fetchResult) SetBodyDone() {
|
||||||
if v := atomic.LoadInt32(&f.pending); v == 1 || v == 3 {
|
if v := atomic.LoadInt32(&f.pending); (v & 1) != 0 {
|
||||||
atomic.AddInt32(&f.pending, -1)
|
atomic.AddInt32(&f.pending, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,19 +95,19 @@ func (f *fetchResult) AllDone() bool {
|
||||||
|
|
||||||
// SetReceiptsDone flags the receipts as finished.
|
// SetReceiptsDone flags the receipts as finished.
|
||||||
func (f *fetchResult) SetReceiptsDone() {
|
func (f *fetchResult) SetReceiptsDone() {
|
||||||
if v := atomic.LoadInt32(&f.pending); v == 2 || v == 3 {
|
if v := atomic.LoadInt32(&f.pending); (v & 2) != 0 {
|
||||||
atomic.AddInt32(&f.pending, -2)
|
atomic.AddInt32(&f.pending, -2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckDone checks if the given type is done already
|
// Done checks if the given type is done already
|
||||||
func (f *fetchResult) Done(typ int) bool {
|
func (f *fetchResult) Done(typ int) bool {
|
||||||
v := atomic.LoadInt32(&f.pending)
|
v := atomic.LoadInt32(&f.pending)
|
||||||
switch typ {
|
switch typ {
|
||||||
case BodyType:
|
case BodyType:
|
||||||
return !(v == 1 || v == 3)
|
return (v & 1) == 0
|
||||||
case ReceiptType:
|
case ReceiptType:
|
||||||
return !(v == 2 || v == 3)
|
return (v & 2) == 0
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -148,23 +148,19 @@ type queue struct {
|
||||||
// newQueue creates a new download queue for scheduling block retrieval.
|
// newQueue creates a new download queue for scheduling block retrieval.
|
||||||
func newQueue(blockCacheLimit int) *queue {
|
func newQueue(blockCacheLimit int) *queue {
|
||||||
lock := new(sync.RWMutex)
|
lock := new(sync.RWMutex)
|
||||||
return &queue{
|
q := &queue{
|
||||||
headerPendPool: make(map[string]*fetchRequest),
|
|
||||||
headerContCh: make(chan bool),
|
headerContCh: make(chan bool),
|
||||||
blockTaskPool: make(map[common.Hash]*types.Header),
|
|
||||||
blockTaskQueue: prque.New(nil),
|
blockTaskQueue: prque.New(nil),
|
||||||
blockPendPool: make(map[string]*fetchRequest),
|
|
||||||
receiptTaskPool: make(map[common.Hash]*types.Header),
|
|
||||||
receiptTaskQueue: prque.New(nil),
|
receiptTaskQueue: prque.New(nil),
|
||||||
receiptPendPool: make(map[string]*fetchRequest),
|
|
||||||
resultCache: newResultStore(blockCacheLimit * 2),
|
|
||||||
active: sync.NewCond(lock),
|
active: sync.NewCond(lock),
|
||||||
lock: lock,
|
lock: lock,
|
||||||
}
|
}
|
||||||
|
q.Reset(blockCacheLimit)
|
||||||
|
return q
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset clears out the queue contents.
|
// Reset clears out the queue contents.
|
||||||
func (q *queue) Reset() {
|
func (q *queue) Reset(blockCacheLimit int) {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -182,7 +178,7 @@ func (q *queue) Reset() {
|
||||||
q.receiptTaskQueue.Reset()
|
q.receiptTaskQueue.Reset()
|
||||||
q.receiptPendPool = make(map[string]*fetchRequest)
|
q.receiptPendPool = make(map[string]*fetchRequest)
|
||||||
|
|
||||||
q.resultCache = newResultStore(blockCacheItems * 2)
|
q.resultCache = newResultStore(blockCacheLimit * 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close marks the end of the sync, unblocking Results.
|
// Close marks the end of the sync, unblocking Results.
|
||||||
|
|
@ -190,8 +186,8 @@ func (q *queue) Reset() {
|
||||||
func (q *queue) Close() {
|
func (q *queue) Close() {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
q.closed = true
|
q.closed = true
|
||||||
|
q.active.Signal()
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
q.active.Broadcast()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingHeaders retrieves the number of header requests pending for retrieval.
|
// PendingHeaders retrieves the number of header requests pending for retrieval.
|
||||||
|
|
@ -341,20 +337,15 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header {
|
||||||
|
|
||||||
// Results retrieves and permanently removes a batch of fetch results from
|
// Results retrieves and permanently removes a batch of fetch results from
|
||||||
// the cache. the result slice will be empty if the queue has been closed.
|
// the cache. the result slice will be empty if the queue has been closed.
|
||||||
// This is 'thread-safe', but assumes that there are not two simultaneous
|
// Results can be called concurrently with Deliver and Schedule,
|
||||||
// callers to Results (both will modify q.resultSize)
|
// but assumes that there are not two simultaneous callers to Results
|
||||||
func (q *queue) Results(block bool) []*fetchResult {
|
func (q *queue) Results(block bool) []*fetchResult {
|
||||||
|
|
||||||
// abort early if there are no items and non-blocking requested
|
// abort early if there are no items and non-blocking requested
|
||||||
if !q.resultCache.HasCompletedItems() && !block {
|
if !block && !q.resultCache.HasCompletedItems() {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
results := q.resultCache.GetCompleted(maxResultsProcess)
|
|
||||||
if len(results) == 0 && !block {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
closed := false
|
closed := false
|
||||||
for !closed && len(results) == 0 {
|
for !closed && !q.resultCache.HasCompletedItems() {
|
||||||
// In order to wait on 'active', we need to obtain the lock.
|
// In order to wait on 'active', we need to obtain the lock.
|
||||||
// That may take a while, if someone is delivering at the same
|
// That may take a while, if someone is delivering at the same
|
||||||
// time, so after obtaining the lock, we check again if there
|
// time, so after obtaining the lock, we check again if there
|
||||||
|
|
@ -363,17 +354,17 @@ func (q *queue) Results(block bool) []*fetchResult {
|
||||||
// someone can have closed the queue. In that case, we should
|
// someone can have closed the queue. In that case, we should
|
||||||
// return the available results and stop blocking
|
// return the available results and stop blocking
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
closed = q.closed
|
if q.resultCache.HasCompletedItems() || q.closed {
|
||||||
results = q.resultCache.GetCompleted(maxResultsProcess)
|
|
||||||
if closed || len(results) > 0 {
|
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
// No items available, and not closed
|
||||||
q.active.Wait()
|
q.active.Wait()
|
||||||
closed = q.closed
|
closed = q.closed
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
results = q.resultCache.GetCompleted(maxResultsProcess)
|
|
||||||
}
|
}
|
||||||
|
// Regardless if closed or not, we can still deliver whatever we have
|
||||||
|
results := q.resultCache.GetCompleted(maxResultsProcess)
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
// Recalculate the result item weights to prevent memory exhaustion
|
// Recalculate the result item weights to prevent memory exhaustion
|
||||||
size := result.Header.Size()
|
size := result.Header.Size()
|
||||||
|
|
@ -397,7 +388,7 @@ func (q *queue) Results(block bool) []*fetchResult {
|
||||||
if time.Now().Second()&0xa == 0 {
|
if time.Now().Second()&0xa == 0 {
|
||||||
info := q.Stats()
|
info := q.Stats()
|
||||||
info = append(info, "throttle", throttleThreshold)
|
info = append(info, "throttle", throttleThreshold)
|
||||||
log.Info("queue stats", info...)
|
log.Info("Downloader queue stats", info...)
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
@ -866,6 +857,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
|
||||||
hashes = append(hashes, header.Hash())
|
hashes = append(hashes, header.Hash())
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
var acceptCount = 0
|
var acceptCount = 0
|
||||||
for _, header := range request.Headers[:i] {
|
for _, header := range request.Headers[:i] {
|
||||||
|
|
@ -889,12 +881,11 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
|
||||||
taskQueue.Push(header, -int64(header.Number.Uint64()))
|
taskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
q.lock.Unlock()
|
|
||||||
|
|
||||||
// Wake up Results
|
// Wake up Results
|
||||||
if acceptCount > 0 {
|
if acceptCount > 0 {
|
||||||
q.active.Broadcast()
|
q.active.Signal()
|
||||||
}
|
}
|
||||||
|
q.lock.Unlock()
|
||||||
// If none of the data was good, it's a stale delivery
|
// If none of the data was good, it's a stale delivery
|
||||||
switch {
|
switch {
|
||||||
case failure == nil || failure == errInvalidChain:
|
case failure == nil || failure == errInvalidChain:
|
||||||
|
|
|
||||||
|
|
@ -242,11 +242,11 @@ func TestEmptyBlocks(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// xTestDelivery does some more extensive testing of events that happen,
|
// XTestDelivery does some more extensive testing of events that happen,
|
||||||
// blocks that become known and peers that make reservations and deliveries.
|
// blocks that become known and peers that make reservations and deliveries.
|
||||||
// disabled since it's not really a unit-test, but can be executed to test
|
// disabled since it's not really a unit-test, but can be executed to test
|
||||||
// some more advanced scenarios
|
// some more advanced scenarios
|
||||||
func xTestDelivery(t *testing.T) {
|
func XTestDelivery(t *testing.T) {
|
||||||
// the outside network, holding blocks
|
// the outside network, holding blocks
|
||||||
blo, rec := makeChain(128, 0, genesis, false)
|
blo, rec := makeChain(128, 0, genesis, false)
|
||||||
world := newNetwork()
|
world := newNetwork()
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type resultStore struct {
|
type resultStore struct {
|
||||||
|
|
@ -73,31 +72,10 @@ func (r *resultStore) SetThrottleThreshold(threshold uint64) {
|
||||||
// prio right now
|
// prio right now
|
||||||
// fetchResult -- the result to store data into
|
// fetchResult -- the result to store data into
|
||||||
// err -- any error that occurred
|
// err -- any error that occurred
|
||||||
func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (bool, bool, *fetchResult, error) {
|
func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (stale, throttled bool, item *fetchResult, err error) {
|
||||||
r.lock.RLock()
|
|
||||||
var index int
|
|
||||||
item, index, stale, throttled, err := r.getFetchResult(header.Number.Uint64())
|
|
||||||
if err != nil || stale || throttled {
|
|
||||||
r.lock.RUnlock()
|
|
||||||
// Index is above the current threshold of 'prioritized' blocks,
|
|
||||||
if throttled {
|
|
||||||
log.Debug("resultcache throttle", "index", index, "threshold", r.throttleThreshold)
|
|
||||||
|
|
||||||
}
|
|
||||||
return stale, throttled, item, err
|
|
||||||
}
|
|
||||||
if item != nil {
|
|
||||||
// All good, item already exists (perhaps a receipt fetch following
|
|
||||||
// a body fetch)
|
|
||||||
r.lock.RUnlock()
|
|
||||||
return stale, throttled, item, err
|
|
||||||
}
|
|
||||||
r.lock.RUnlock()
|
|
||||||
// Need to create a fetchresult, and as we've just release the Rlock,
|
|
||||||
// we need to check again after obtaining the writelock
|
|
||||||
r.lock.Lock()
|
r.lock.Lock()
|
||||||
defer r.lock.Unlock()
|
defer r.lock.Unlock()
|
||||||
// Same checks as above, now with wlock
|
var index int
|
||||||
item, index, stale, throttled, err = r.getFetchResult(header.Number.Uint64())
|
item, index, stale, throttled, err = r.getFetchResult(header.Number.Uint64())
|
||||||
if err != nil || stale || throttled {
|
if err != nil || stale || throttled {
|
||||||
return stale, throttled, item, err
|
return stale, throttled, item, err
|
||||||
|
|
@ -132,13 +110,13 @@ func (r *resultStore) getFetchResult(headerNumber uint64) (item *fetchResult, in
|
||||||
err = fmt.Errorf("index allocation went beyond available resultStore space "+
|
err = fmt.Errorf("index allocation went beyond available resultStore space "+
|
||||||
"(index [%d] = header [%d] - resultOffset [%d], len(resultStore) = %d",
|
"(index [%d] = header [%d] - resultOffset [%d], len(resultStore) = %d",
|
||||||
index, headerNumber, r.resultOffset, len(r.items))
|
index, headerNumber, r.resultOffset, len(r.items))
|
||||||
return
|
return nil, index, stale, throttle, err
|
||||||
}
|
}
|
||||||
if stale {
|
if stale {
|
||||||
return
|
return nil, index, stale, throttle, nil
|
||||||
}
|
}
|
||||||
item = r.items[index]
|
item = r.items[index]
|
||||||
return
|
return item, index, stale, throttle, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasCompletedItems returns true if there are processable items available
|
// hasCompletedItems returns true if there are processable items available
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue