github.com/aergoio/aergo@v1.3.1/syncer/blockfetcher.go (about)

     1  package syncer
     2  
     3  import (
     4  	"bytes"
     5  	"container/list"
     6  	"errors"
     7  	"fmt"
     8  	"github.com/aergoio/aergo/p2p/p2putil"
     9  	"sync"
    10  	"sync/atomic"
    11  	"time"
    12  
    13  	"github.com/aergoio/aergo/internal/enc"
    14  	"github.com/aergoio/aergo/message"
    15  	"github.com/aergoio/aergo/pkg/component"
    16  	"github.com/aergoio/aergo/types"
    17  )
    18  
    19  type BlockFetcher struct {
    20  	compRequester component.IComponentRequester //for communicate with other service
    21  
    22  	ctx *types.SyncContext
    23  
    24  	quitCh chan interface{}
    25  
    26  	hfCh chan *HashSet
    27  
    28  	curHashSet *HashSet
    29  
    30  	runningQueue TaskQueue
    31  	pendingQueue TaskQueue
    32  	retryQueue   SortedTaskQueue
    33  
    34  	responseCh chan interface{} //BlockResponse, AddBlockResponse message
    35  	peers      *PeerSet
    36  
    37  	blockProcessor *BlockProcessor
    38  
    39  	name string
    40  
    41  	maxFetchSize   int
    42  	maxFetchTasks  int
    43  	maxPendingConn int
    44  
    45  	debug bool
    46  
    47  	stat BlockFetcherStat
    48  
    49  	waitGroup *sync.WaitGroup
    50  	isRunning bool
    51  
    52  	cfg *SyncerConfig
    53  }
    54  
    55  type BlockFetcherStat struct {
    56  	maxRspBlock  atomic.Value
    57  	lastAddBlock atomic.Value
    58  }
    59  
    60  type SyncPeer struct {
    61  	No      int
    62  	ID      types.PeerID
    63  	FailCnt int
    64  	IsErr   bool
    65  }
    66  
    67  type TaskQueue struct {
    68  	list.List
    69  }
    70  
    71  type SortedTaskQueue struct {
    72  	TaskQueue
    73  }
    74  
    75  func (squeue *SortedTaskQueue) Push(task *FetchTask) {
    76  	var bigElem *list.Element
    77  
    78  	for e := squeue.Front(); e != nil; e = e.Next() {
    79  		// do something with e.Value
    80  		curTask := e.Value.(*FetchTask)
    81  		if curTask.startNo > task.startNo {
    82  			bigElem = e
    83  			break
    84  		}
    85  	}
    86  
    87  	if bigElem != nil {
    88  		squeue.InsertBefore(task, bigElem)
    89  	} else {
    90  		squeue.PushBack(task)
    91  	}
    92  
    93  }
    94  
    95  type FetchTask struct {
    96  	count   int
    97  	hashes  []message.BlockHash
    98  	startNo types.BlockNo
    99  
   100  	syncPeer *SyncPeer
   101  
   102  	started time.Time
   103  	retry   int
   104  }
   105  
   106  type PeerSet struct {
   107  	total int
   108  	free  int
   109  	bad   int
   110  
   111  	freePeers *list.List
   112  	badPeers  *list.List
   113  }
   114  
   115  var (
   116  	schedTick            = time.Millisecond * 100
   117  	DfltFetchTimeOut     = time.Second * 30
   118  	DfltBlockFetchSize   = 100
   119  	MaxPeerFailCount     = 3
   120  	DfltBlockFetchTasks  = 5
   121  	MaxBlockPendingTasks = 10
   122  )
   123  
   124  var (
   125  	ErrAllPeerBad       = errors.New("BlockFetcher: error no avaliable peers")
   126  	ErrQuitBlockFetcher = errors.New("BlockFetcher quit")
   127  )
   128  
   129  func newBlockFetcher(ctx *types.SyncContext, compRequester component.IComponentRequester, cfg *SyncerConfig) *BlockFetcher {
   130  	bf := &BlockFetcher{ctx: ctx, compRequester: compRequester, name: NameBlockFetcher, cfg: cfg}
   131  
   132  	bf.quitCh = make(chan interface{})
   133  	bf.hfCh = make(chan *HashSet)
   134  	bf.responseCh = make(chan interface{}, cfg.maxBlockReqTasks*2) //for safety. In normal situdation, it should use only one
   135  
   136  	bf.peers = newPeerSet()
   137  	bf.maxFetchSize = cfg.maxBlockReqSize
   138  	bf.maxFetchTasks = cfg.maxBlockReqTasks
   139  	bf.maxPendingConn = cfg.maxPendingConn
   140  
   141  	bf.blockProcessor = NewBlockProcessor(compRequester, bf, ctx.CommonAncestor, ctx.TargetNo)
   142  
   143  	bf.blockProcessor.connQueue = make([]*ConnectTask, 0, 16)
   144  
   145  	bf.runningQueue.Init()
   146  	bf.pendingQueue.Init()
   147  	bf.retryQueue.Init()
   148  
   149  	return bf
   150  }
   151  
   152  func (bf *BlockFetcher) Start() {
   153  	bf.waitGroup = &sync.WaitGroup{}
   154  	bf.waitGroup.Add(1)
   155  
   156  	schedTicker := time.NewTicker(schedTick)
   157  
   158  	bf.isRunning = true
   159  
   160  	if bf.cfg.debugContext != nil && bf.cfg.debugContext.debugHashFetcher {
   161  		testRun := func() {
   162  			logger.Debug().Msg("BlockFetcher dummy mode started")
   163  
   164  			defer bf.waitGroup.Done()
   165  
   166  			for {
   167  				if bf.cfg.debugContext.BfWaitTime > 0 {
   168  					logger.Debug().Msg("BlockFetcher sleep")
   169  					time.Sleep(bf.cfg.debugContext.BfWaitTime)
   170  					logger.Debug().Msg("BlockFetcher wakeup")
   171  				}
   172  				select {
   173  				case <-bf.hfCh:
   174  				case <-bf.quitCh:
   175  					logger.Debug().Msg("BlockFetcher dummy mode exited")
   176  					return
   177  				}
   178  			}
   179  		}
   180  
   181  		go testRun()
   182  		return
   183  	}
   184  
   185  	run := func() {
   186  		defer RecoverSyncer(NameBlockFetcher, bf.GetSeq(), bf.compRequester, func() { bf.waitGroup.Done() })
   187  
   188  		logger.Debug().Msg("start block fetcher")
   189  
   190  		if err := bf.init(); err != nil {
   191  			stopSyncer(bf.compRequester, bf.GetSeq(), bf.name, err)
   192  			return
   193  		}
   194  
   195  		logger.Debug().Msg("block fetcher loop start")
   196  
   197  		for {
   198  			select {
   199  			case <-schedTicker.C:
   200  				if err := bf.checkTaskTimeout(); err != nil {
   201  					logger.Error().Err(err).Msg("failed checkTaskTimeout")
   202  					stopSyncer(bf.compRequester, bf.GetSeq(), bf.name, err)
   203  					return
   204  				}
   205  
   206  			case msg, ok := <-bf.responseCh:
   207  				if !ok {
   208  					logger.Info().Msg("BlockFetcher responseCh is closed. Syncer is maybe stopping.")
   209  					return
   210  				}
   211  
   212  				err := bf.blockProcessor.run(msg)
   213  				if err != nil {
   214  					logger.Error().Err(err).Msg("invalid block response message")
   215  					stopSyncer(bf.compRequester, bf.GetSeq(), bf.name, err)
   216  					return
   217  				}
   218  
   219  			case <-bf.quitCh:
   220  				logger.Info().Msg("BlockFetcher quit#1")
   221  				return
   222  			}
   223  
   224  			//TODO scheduler stop if all tasks have done
   225  			if err := bf.schedule(); err != nil {
   226  				if err == ErrQuitBlockFetcher {
   227  					logger.Info().Msg("BlockFetcher exited while schedule")
   228  					return
   229  				}
   230  
   231  				logger.Error().Err(err).Msg("BlockFetcher schedule failed & finished")
   232  				stopSyncer(bf.compRequester, bf.GetSeq(), bf.name, err)
   233  				return
   234  			}
   235  		}
   236  	}
   237  
   238  	go run()
   239  }
   240  
   241  func (bf *BlockFetcher) init() error {
   242  	setPeers := func() error {
   243  		result, err := bf.compRequester.RequestToFutureResult(message.P2PSvc, &message.GetPeers{}, dfltTimeout, "BlockFetcher init")
   244  		if err != nil {
   245  			logger.Error().Err(err).Msg("failed to get peers information")
   246  			return err
   247  		}
   248  
   249  		msg := result.(*message.GetPeersRsp)
   250  
   251  		for _, peerElem := range msg.Peers {
   252  			state := peerElem.State
   253  			if state.Get() == types.RUNNING {
   254  				bf.peers.addNew(types.PeerID(peerElem.Addr.PeerID))
   255  			}
   256  		}
   257  
   258  		if bf.peers.freePeers.Len() != bf.peers.free {
   259  			panic(fmt.Sprintf("free peer len mismatch %d,%d", bf.peers.freePeers.Len(), bf.peers.free))
   260  		}
   261  
   262  		return nil
   263  	}
   264  
   265  	logger.Debug().Msg("block fetcher init")
   266  
   267  	if err := setPeers(); err != nil {
   268  		logger.Error().Err(err).Msg("failed to set peers")
   269  		return err
   270  	}
   271  
   272  	return nil
   273  }
   274  
   275  func (bf *BlockFetcher) GetSeq() uint64 {
   276  	return bf.ctx.Seq
   277  }
   278  
   279  func (bf *BlockFetcher) schedule() error {
   280  	for bf.peers.free > 0 {
   281  		//check max concurrent runing task count
   282  		curRunning := bf.runningQueue.Len()
   283  		if curRunning >= bf.maxFetchTasks {
   284  			//logger.Debug().Int("runnig", curRunning).Int("pending", bf.pendingQueue.Len()).Msg("max running")
   285  			return nil
   286  		}
   287  
   288  		//check no task
   289  		candTask, err := bf.searchCandidateTask()
   290  		if err != nil {
   291  			logger.Error().Err(err).Msg("failed to search candidate task")
   292  			return err
   293  		}
   294  		if candTask == nil {
   295  			return nil
   296  		}
   297  
   298  		//check max pending connect
   299  		//	if next task is retry task, must run. it can be next block to connect
   300  		curPendingConn := len(bf.blockProcessor.connQueue)
   301  		if curPendingConn >= bf.maxPendingConn && candTask.retry <= 0 {
   302  			return nil
   303  		}
   304  
   305  		freePeer, err := bf.popFreePeer()
   306  		if err != nil {
   307  			logger.Error().Err(err).Msg("error to get free peer")
   308  			return err
   309  		}
   310  		if freePeer == nil {
   311  			panic("free peer can't be nil")
   312  		}
   313  
   314  		bf.popNextTask(candTask)
   315  		if candTask == nil {
   316  			panic("task can't be nil")
   317  		}
   318  
   319  		logger.Debug().Int("pendingConn", curPendingConn).Int("running", curRunning).Msg("schedule")
   320  		bf.runTask(candTask, freePeer)
   321  	}
   322  
   323  	return nil
   324  }
   325  
   326  func (bf *BlockFetcher) checkTaskTimeout() error {
   327  	now := time.Now()
   328  	var next *list.Element
   329  
   330  	for e := bf.runningQueue.Front(); e != nil; e = next {
   331  		// do something with e.Value
   332  		task := e.Value.(*FetchTask)
   333  		next = e.Next()
   334  
   335  		if !task.isTimeOut(now, bf.cfg.fetchTimeOut) {
   336  			continue
   337  		}
   338  
   339  		bf.runningQueue.Remove(e)
   340  
   341  		if err := bf.processFailedTask(task, false); err != nil {
   342  			return err
   343  		}
   344  
   345  		logger.Error().Uint64("StartNo", task.startNo).Str("start", enc.ToString(task.hashes[0])).Int("cout", task.count).Int("runqueue", bf.runningQueue.Len()).Int("pendingqueue", bf.pendingQueue.Len()).
   346  			Msg("timeouted task pushed to pending queue")
   347  
   348  		//time.Sleep(10000*time.Second)
   349  	}
   350  
   351  	return nil
   352  }
   353  
   354  func (bf *BlockFetcher) processFailedTask(task *FetchTask, isErr bool) error {
   355  	logBadPeer := func(peer *SyncPeer, peers *PeerSet, cfg *SyncerConfig) {
   356  		if cfg != nil && cfg.debugContext != nil {
   357  			cfg.debugContext.logBadPeers[peer.No] = true
   358  		}
   359  	}
   360  
   361  	logger.Error().Int("peerno", task.syncPeer.No).Uint64("StartNo", task.startNo).Str("start", enc.ToString(task.hashes[0])).Msg("task fail, move to retry queue")
   362  
   363  	failPeer := task.syncPeer
   364  
   365  	logBadPeer(failPeer, bf.peers, bf.cfg)
   366  
   367  	bf.peers.processPeerFail(failPeer, isErr)
   368  
   369  	task.retry++
   370  	task.syncPeer = nil
   371  
   372  	//TODO sort by time because deadlock
   373  	bf.retryQueue.Push(task)
   374  
   375  	if bf.peers.isAllBad() {
   376  		return ErrAllPeerBad
   377  	}
   378  
   379  	return nil
   380  }
   381  
   382  func (bf *BlockFetcher) popNextTask(task *FetchTask) {
   383  	logger.Debug().Int("retry", task.retry).Uint64("StartNo", task.startNo).Str("start", enc.ToString(task.hashes[0])).Str("end", enc.ToString(task.hashes[task.count-1])).
   384  		Int("tasks retry", bf.retryQueue.Len()).Int("tasks pending", bf.pendingQueue.Len()).Msg("next fetchtask")
   385  
   386  	var poppedTask *FetchTask
   387  	if task.retry > 0 {
   388  		poppedTask = bf.retryQueue.Pop()
   389  	} else {
   390  		poppedTask = bf.pendingQueue.Pop()
   391  	}
   392  
   393  	if poppedTask != task {
   394  		logger.Panic().Uint64("next task", task.startNo).Uint64("popped task", poppedTask.startNo).
   395  			Int("retry", task.retry).Msg("peeked task is not popped task")
   396  		panic("peeked task is not popped task")
   397  	}
   398  }
   399  
   400  func (bf *BlockFetcher) searchCandidateTask() (*FetchTask, error) {
   401  	getNewHashSet := func() (*HashSet, error) {
   402  		if bf.curHashSet == nil { //blocking
   403  			logger.Info().Msg("BlockFetcher waiting first hashset")
   404  
   405  			select {
   406  			case hashSet := <-bf.hfCh:
   407  				return hashSet, nil
   408  			case <-bf.quitCh:
   409  				logger.Debug().Msg("BlockFetcher quitCh#2")
   410  				return nil, ErrQuitBlockFetcher
   411  			}
   412  		} else {
   413  			select { //nonblocking
   414  			case hashSet := <-bf.hfCh:
   415  				return hashSet, nil
   416  			case <-bf.quitCh:
   417  				logger.Debug().Msg("BlockFetcher quitCh#3")
   418  				return nil, ErrQuitBlockFetcher
   419  			default:
   420  				//logger.Debug().Msg("BlockFetcher has no input HashSet")
   421  				return nil, nil
   422  			}
   423  		}
   424  	}
   425  
   426  	addNewFetchTasks := func(hashSet *HashSet) {
   427  		start, end := 0, 0
   428  		count := hashSet.Count
   429  
   430  		logger.Debug().Uint64("startno", hashSet.StartNo).Str("start", enc.ToString(hashSet.Hashes[0])).Int("count", hashSet.Count).Msg("add new fetchtasks from HashSet")
   431  
   432  		for start < count {
   433  			end = start + bf.maxFetchSize
   434  			if end > count {
   435  				end = count
   436  			}
   437  
   438  			task := &FetchTask{count: end - start, hashes: hashSet.Hashes[start:end], startNo: hashSet.StartNo + uint64(start), retry: 0}
   439  
   440  			logger.Debug().Uint64("StartNo", task.startNo).Int("count", task.count).Msg("add fetchtask")
   441  
   442  			bf.pendingQueue.PushBack(task)
   443  
   444  			start = end
   445  		}
   446  		logger.Debug().Int("pendingqueue", bf.pendingQueue.Len()).Msg("addNewTasks end")
   447  	}
   448  
   449  	var newTask *FetchTask
   450  
   451  	if bf.retryQueue.Len() > 0 {
   452  		newTask = bf.retryQueue.Peek()
   453  	} else {
   454  		if bf.pendingQueue.Len() == 0 {
   455  			logger.Debug().Msg("pendingqueue is empty")
   456  
   457  			hashSet, err := getNewHashSet()
   458  			if err != nil {
   459  				logger.Debug().Err(err).Msg("failed to get new hashset")
   460  				return nil, err
   461  			}
   462  			if hashSet == nil {
   463  				logger.Debug().Msg("BlockFetcher no hashSet")
   464  				return nil, nil
   465  			}
   466  
   467  			logger.Debug().Uint64("startno", hashSet.StartNo).Str("start", enc.ToString(hashSet.Hashes[0])).Int("count", hashSet.Count).Msg("BlockFetcher got hashset")
   468  
   469  			bf.curHashSet = hashSet
   470  			addNewFetchTasks(hashSet)
   471  		}
   472  
   473  		newTask = bf.pendingQueue.Peek()
   474  	}
   475  
   476  	//logger.Debug().Int("retry", newTask.retry).Uint64("StartNo", newTask.startNo).Str("start", enc.ToString(newTask.hashes[0])).Str("end", enc.ToString(newTask.hashes[newTask.count-1])).
   477  	//	Int("tasks retry", bf.retryQueue.Len()).Int("tasks pending", bf.pendingQueue.Len()).Msg("candidate fetchtask")
   478  
   479  	return newTask, nil
   480  }
   481  
   482  func (bf *BlockFetcher) popFreePeer() (*SyncPeer, error) {
   483  	setDebugAllPeerBad := func(err error, cfg *SyncerConfig) {
   484  		if err == ErrAllPeerBad && cfg != nil && cfg.debugContext != nil {
   485  			debugCtx := cfg.debugContext
   486  			debugCtx.logAllPeersBad = true
   487  		}
   488  	}
   489  
   490  	freePeer, err := bf.peers.popFree()
   491  	if err != nil {
   492  		setDebugAllPeerBad(err, bf.cfg)
   493  		logger.Error().Err(err).Msg("pop free peer failed")
   494  		return nil, err
   495  	}
   496  
   497  	if freePeer != nil {
   498  		logger.Debug().Int("peerno", freePeer.No).Int("free", bf.peers.free).Int("total", bf.peers.total).Int("bad", bf.peers.bad).Msg("popped free peer")
   499  	} else {
   500  		logger.Debug().Int("free", bf.peers.free).Int("total", bf.peers.total).Int("bad", bf.peers.bad).Msg("not exist free peer")
   501  	}
   502  
   503  	return freePeer, nil
   504  }
   505  
   506  func (bf *BlockFetcher) pushFreePeer(syncPeer *SyncPeer) {
   507  	bf.peers.pushFree(syncPeer)
   508  
   509  	logger.Debug().Int("peerno", syncPeer.No).Int("free", bf.peers.free).Msg("pushed free peer")
   510  }
   511  
   512  func (bf *BlockFetcher) runTask(task *FetchTask, peer *SyncPeer) {
   513  	task.started = time.Now()
   514  	task.syncPeer = peer
   515  	bf.runningQueue.PushBack(task)
   516  
   517  	logger.Debug().Int("peerno", task.syncPeer.No).Int("count", task.count).Uint64("StartNo", task.startNo).Str("start", enc.ToString(task.hashes[0])).Int("runqueue", bf.runningQueue.Len()).Msg("send block fetch request")
   518  
   519  	bf.compRequester.TellTo(message.P2PSvc, &message.GetBlockChunks{Seq: bf.GetSeq(), GetBlockInfos: message.GetBlockInfos{ToWhom: peer.ID, Hashes: task.hashes}, TTL: DfltFetchTimeOut})
   520  }
   521  
   522  //TODO refactoring matchFunc
   523  func (bf *BlockFetcher) findFinished(msg *message.GetBlockChunksRsp, peerMatch bool) (*FetchTask, error) {
   524  	count := len(msg.Blocks)
   525  
   526  	var next *list.Element
   527  	for e := bf.runningQueue.Front(); e != nil; e = next {
   528  		// do something with e.Value
   529  		task := e.Value.(*FetchTask)
   530  		next = e.Next()
   531  
   532  		//find failed peer
   533  		if peerMatch {
   534  			if task.isPeerMatched(msg.ToWhom) {
   535  				bf.runningQueue.Remove(e)
   536  
   537  				logger.Debug().Str("peer", p2putil.ShortForm(msg.ToWhom)).Err(msg.Err).Str("start", enc.ToString(task.hashes[0])).Int("count", task.count).Int("runqueue", bf.runningQueue.Len()).Msg("task finished with error")
   538  				return task, nil
   539  			}
   540  		} else {
   541  			//find finished peer
   542  			if task.isMatched(msg.ToWhom, msg.Blocks, count) {
   543  				bf.runningQueue.Remove(e)
   544  
   545  				logger.Debug().Uint64("StartNo", task.startNo).Str("start", enc.ToString(task.hashes[0])).Int("count", task.count).Int("runqueue", bf.runningQueue.Len()).
   546  					Msg("task finished")
   547  
   548  				return task, nil
   549  			}
   550  		}
   551  	}
   552  
   553  	return nil, &ErrSyncMsg{msg: msg}
   554  }
   555  
   556  func (bf *BlockFetcher) handleBlockRsp(msg interface{}) error {
   557  	if bf == nil {
   558  		return nil
   559  	}
   560  
   561  	bf.responseCh <- msg
   562  	return nil
   563  }
   564  
   565  func (bf *BlockFetcher) stop() {
   566  	if bf == nil {
   567  		return
   568  	}
   569  
   570  	//logger.Info().Bool("isrunning", bf.isRunning).Bool("isnil", bf.quitCh== nil).Msg("BlockFetcher stop")
   571  
   572  	if bf.isRunning {
   573  		logger.Info().Msg("BlockFetcher stop#1")
   574  
   575  		close(bf.quitCh)
   576  		close(bf.hfCh)
   577  
   578  		bf.waitGroup.Wait()
   579  		bf.isRunning = false
   580  	}
   581  	logger.Info().Msg("BlockFetcher stopped")
   582  }
   583  
   584  func (stat *BlockFetcherStat) setMaxChunkRsp(lastBlock *types.Block) {
   585  	curMaxRspBlock := stat.getMaxChunkRsp()
   586  
   587  	if curMaxRspBlock == nil || curMaxRspBlock.GetHeader().BlockNo < lastBlock.GetHeader().BlockNo {
   588  		stat.maxRspBlock.Store(lastBlock)
   589  		logger.Debug().Uint64("no", lastBlock.GetHeader().BlockNo).Msg("last block chunk response")
   590  	}
   591  }
   592  
   593  func (stat *BlockFetcherStat) setLastAddBlock(block *types.Block) {
   594  	stat.lastAddBlock.Store(block)
   595  	logger.Debug().Uint64("no", block.GetHeader().BlockNo).Msg("last block add response")
   596  }
   597  
   598  func (stat *BlockFetcherStat) getMaxChunkRsp() *types.Block {
   599  	aopv := stat.maxRspBlock.Load()
   600  	if aopv != nil {
   601  		return aopv.(*types.Block)
   602  	}
   603  
   604  	return nil
   605  }
   606  
   607  func (stat *BlockFetcherStat) getLastAddBlock() *types.Block {
   608  	aopv := stat.lastAddBlock.Load()
   609  	if aopv != nil {
   610  		return aopv.(*types.Block)
   611  	}
   612  
   613  	return nil
   614  }
   615  
   616  func newPeerSet() *PeerSet {
   617  	ps := &PeerSet{}
   618  
   619  	ps.freePeers = list.New()
   620  	ps.badPeers = list.New()
   621  
   622  	return ps
   623  }
   624  
   625  func (ps *PeerSet) isAllBad() bool {
   626  	if ps.total == ps.bad {
   627  		return true
   628  	}
   629  
   630  	return false
   631  }
   632  
   633  func (ps *PeerSet) addNew(peerID types.PeerID) {
   634  	peerno := ps.total
   635  	ps.pushFree(&SyncPeer{No: peerno, ID: peerID})
   636  	ps.total++
   637  
   638  	logger.Info().Str("peer", p2putil.ShortForm(peerID)).Int("peerno", peerno).Int("no", ps.total).Msg("new peer added")
   639  }
   640  
   641  /*
   642  func (ps *PeerSet) print() {
   643  
   644  }*/
   645  func (ps *PeerSet) pushFree(freePeer *SyncPeer) {
   646  	ps.freePeers.PushBack(freePeer)
   647  	ps.free++
   648  
   649  	logger.Info().Int("no", freePeer.No).Int("free", ps.free).Msg("free peer added")
   650  }
   651  
   652  func (ps *PeerSet) popFree() (*SyncPeer, error) {
   653  	if ps.isAllBad() {
   654  		logger.Error().Msg("all peers are bad")
   655  		return nil, ErrAllPeerBad
   656  	}
   657  
   658  	elem := ps.freePeers.Front()
   659  	if elem == nil {
   660  		return nil, nil
   661  	}
   662  
   663  	ps.freePeers.Remove(elem)
   664  	ps.free--
   665  
   666  	if ps.freePeers.Len() != ps.free {
   667  		panic(fmt.Sprintf("free peer len mismatch %d,%d", ps.freePeers.Len(), ps.free))
   668  	}
   669  
   670  	freePeer := elem.Value.(*SyncPeer)
   671  	logger.Debug().Int("peerno", freePeer.No).Int("no", freePeer.No).Msg("free peer poped")
   672  	return freePeer, nil
   673  }
   674  
   675  func (ps *PeerSet) processPeerFail(failPeer *SyncPeer, isErr bool) {
   676  	//TODO handle connection closed
   677  	failPeer.FailCnt++
   678  	failPeer.IsErr = isErr
   679  
   680  	logger.Error().Int("peerno", failPeer.No).Int("failcnt", failPeer.FailCnt).Int("maxfailcnt", MaxPeerFailCount).Bool("iserr", failPeer.IsErr).Msg("peer failed")
   681  
   682  	if isErr || failPeer.FailCnt >= MaxPeerFailCount {
   683  		ps.badPeers.PushBack(failPeer)
   684  		ps.bad++
   685  
   686  		if ps.badPeers.Len() != ps.bad {
   687  			panic(fmt.Sprintf("bad peer len mismatch %d,%d", ps.badPeers.Len(), ps.bad))
   688  		}
   689  
   690  		logger.Error().Int("peerno", failPeer.No).Int("total", ps.total).Int("free", ps.free).Int("bad", ps.bad).Msg("peer move to bad")
   691  	} else {
   692  		ps.freePeers.PushBack(failPeer)
   693  		ps.free++
   694  
   695  		logger.Error().Int("peerno", failPeer.No).Int("total", ps.total).Int("free", ps.free).Int("bad", ps.bad).Msg("peer move to free")
   696  	}
   697  }
   698  
   699  func (tq *TaskQueue) Pop() *FetchTask {
   700  	elem := tq.Front()
   701  	if elem == nil {
   702  		return nil
   703  	}
   704  
   705  	tq.Remove(elem)
   706  	return elem.Value.(*FetchTask)
   707  }
   708  
   709  func (tq *TaskQueue) Peek() *FetchTask {
   710  	elem := tq.Front()
   711  	if elem == nil {
   712  		return nil
   713  	}
   714  
   715  	return elem.Value.(*FetchTask)
   716  }
   717  
   718  func (task *FetchTask) isTimeOut(now time.Time, timeout time.Duration) bool {
   719  	if now.Sub(task.started) > timeout {
   720  		logger.Info().Int("peerno", task.syncPeer.No).Uint64("startno", task.startNo).Str("start", enc.ToString(task.hashes[0])).Int("cout", task.count).Msg("FetchTask peer timeouted")
   721  		return true
   722  	}
   723  
   724  	return false
   725  }
   726  
   727  func (task *FetchTask) isMatched(peerID types.PeerID, blocks []*types.Block, count int) bool {
   728  	startHash, endHash := blocks[0].GetHash(), blocks[len(blocks)-1].GetHash()
   729  
   730  	if task.count != count ||
   731  		task.syncPeer.ID != peerID ||
   732  		bytes.Compare(task.hashes[0], startHash) != 0 ||
   733  		bytes.Compare(task.hashes[len(task.hashes)-1], endHash) != 0 {
   734  		return false
   735  	}
   736  
   737  	for i, block := range blocks {
   738  		if bytes.Compare(task.hashes[i], block.GetHash()) != 0 {
   739  			logger.Info().Int("peerno", task.syncPeer.No).Str("hash", enc.ToString(task.hashes[0])).Int("idx", i).Msg("task hash mismatch")
   740  			return false
   741  		}
   742  	}
   743  
   744  	return true
   745  }
   746  
   747  func (task *FetchTask) isPeerMatched(peerID types.PeerID) bool {
   748  	if task.syncPeer.ID == peerID {
   749  		return true
   750  	}
   751  
   752  	return false
   753  }