github.com/iotexproject/iotex-core@v1.14.1-rc1/blocksync/buffer.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package blocksync
     7  
     8  import (
     9  	"sync"
    10  
    11  	"go.uber.org/zap"
    12  
    13  	"github.com/iotexproject/iotex-core/pkg/log"
    14  )
    15  
    16  // blockBuffer is used to keep in-coming block in order.
    17  type blockBuffer struct {
    18  	mu           sync.RWMutex
    19  	blockQueues  map[uint64]*uniQueue
    20  	bufferSize   uint64
    21  	intervalSize uint64
    22  }
    23  
    24  type syncBlocksInterval struct {
    25  	Start uint64
    26  	End   uint64
    27  }
    28  
    29  func newBlockBuffer(bufferSize, intervalSize uint64) *blockBuffer {
    30  	return &blockBuffer{
    31  		blockQueues:  map[uint64]*uniQueue{},
    32  		bufferSize:   bufferSize,
    33  		intervalSize: intervalSize,
    34  	}
    35  }
    36  
    37  func (b *blockBuffer) Pop(height uint64) []*peerBlock {
    38  	b.mu.Lock()
    39  	defer b.mu.Unlock()
    40  	queue, ok := b.blockQueues[height]
    41  	if !ok {
    42  		return nil
    43  	}
    44  	blks := queue.dequeAll()
    45  	delete(b.blockQueues, height)
    46  
    47  	return blks
    48  }
    49  
    50  func (b *blockBuffer) Cleanup(height uint64) {
    51  	b.mu.Lock()
    52  	defer b.mu.Unlock()
    53  
    54  	size := len(b.blockQueues)
    55  	if size > int(b.bufferSize)*2 {
    56  		log.L().Warn("blockBuffer is leaking memory.", zap.Int("bufferSize", size))
    57  		newQueues := map[uint64]*uniQueue{}
    58  		for h := range b.blockQueues {
    59  			if h > height {
    60  				newQueues[h] = b.blockQueues[h]
    61  			}
    62  		}
    63  		b.blockQueues = newQueues
    64  	}
    65  }
    66  
    67  // AddBlock tries to put given block into buffer and flush buffer into blockchain.
    68  func (b *blockBuffer) AddBlock(tipHeight uint64, blk *peerBlock) (bool, uint64) {
    69  	b.mu.Lock()
    70  	defer b.mu.Unlock()
    71  	blkHeight := blk.block.Height()
    72  	if blkHeight <= tipHeight {
    73  		return false, blkHeight
    74  	}
    75  	if blkHeight > tipHeight+b.bufferSize {
    76  		return false, tipHeight + b.bufferSize
    77  	}
    78  	if _, ok := b.blockQueues[blkHeight]; !ok {
    79  		b.blockQueues[blkHeight] = newUniQueue()
    80  	}
    81  	b.blockQueues[blkHeight].enque(blk)
    82  	return true, blkHeight
    83  }
    84  
    85  // GetBlocksIntervalsToSync returns groups of syncBlocksInterval are missing upto targetHeight.
    86  func (b *blockBuffer) GetBlocksIntervalsToSync(confirmedHeight uint64, targetHeight uint64) []syncBlocksInterval {
    87  	b.mu.RLock()
    88  	defer b.mu.RUnlock()
    89  	var (
    90  		start    uint64
    91  		startSet bool
    92  		bi       []syncBlocksInterval
    93  	)
    94  
    95  	// The sync range shouldn't go beyond tip height + buffer size to avoid being too aggressive
    96  	if targetHeight > confirmedHeight+b.bufferSize {
    97  		targetHeight = confirmedHeight + b.bufferSize
    98  	}
    99  	// The sync range should at least contain one interval to speculatively fetch missing blocks
   100  	if targetHeight < confirmedHeight+b.intervalSize {
   101  		targetHeight = confirmedHeight + b.intervalSize
   102  	}
   103  
   104  	var iLen uint64
   105  	for h := confirmedHeight + 1; h <= targetHeight; h++ {
   106  		if _, ok := b.blockQueues[h]; !ok {
   107  			iLen++
   108  			if !startSet {
   109  				start = h
   110  				startSet = true
   111  			}
   112  			if iLen >= b.intervalSize {
   113  				bi = append(bi, syncBlocksInterval{Start: start, End: h})
   114  				startSet = false
   115  				iLen = 0
   116  			}
   117  			continue
   118  		}
   119  		if startSet {
   120  			bi = append(bi, syncBlocksInterval{Start: start, End: h - 1})
   121  			startSet = false
   122  			iLen = 0
   123  		}
   124  	}
   125  
   126  	// handle last interval
   127  	if startSet {
   128  		bi = append(bi, syncBlocksInterval{Start: start, End: targetHeight})
   129  	}
   130  	return bi
   131  }