mutex.Lock during c.recoverFromCorruption()

This commit is contained in:
Larry Ruane 2020-04-23 00:17:42 -06:00 committed by Larry Ruane
parent 1d11751e4e
commit 3873a4d895
1 changed files with 26 additions and 11 deletions

View File

@ -51,20 +51,27 @@ func (c *BlockCache) HashMismatch(prevhash []byte) bool {
return c.latestHash != nil && !bytes.Equal(c.latestHash, prevhash) return c.latestHash != nil && !bytes.Equal(c.latestHash, prevhash)
} }
// Make the block at the given height the lowest height that we don't have.
// In other words, wipe out this height and beyond.
// This should never increase the size of the cache, only decrease.
// Caller should hold c.mutex.Lock().
func (c *BlockCache) setDbFiles(height int) { func (c *BlockCache) setDbFiles(height int) {
index := height - c.firstBlock if height < c.nextBlock {
if err := c.lengthsFile.Truncate(int64(index * 4)); err != nil { index := height - c.firstBlock
Log.Fatal("truncate lengths file failed: ", err) if err := c.lengthsFile.Truncate(int64(index * 4)); err != nil {
Log.Fatal("truncate lengths file failed: ", err)
}
if err := c.blocksFile.Truncate(c.starts[index]); err != nil {
Log.Fatal("truncate blocks file failed: ", err)
}
c.Sync()
c.starts = c.starts[:index+1]
c.nextBlock = height
c.setLatestHash()
} }
if err := c.blocksFile.Truncate(c.starts[index]); err != nil {
Log.Fatal("truncate blocks file failed: ", err)
}
c.Sync()
c.starts = c.starts[:index+1]
c.nextBlock = height
c.setLatestHash()
} }
// Caller should hold c.mutex.Lock().
func (c *BlockCache) recoverFromCorruption(height int) { func (c *BlockCache) recoverFromCorruption(height int) {
Log.Warning("CORRUPTION detected in db blocks-cache files, height ", height, " redownloading") Log.Warning("CORRUPTION detected in db blocks-cache files, height ", height, " redownloading")
c.setDbFiles(height) c.setDbFiles(height)
@ -86,6 +93,7 @@ func checksum(height int, b []byte) []byte {
return cs.Sum(nil) return cs.Sum(nil)
} }
// Caller should hold (at least) c.mutex.RLock().
func (c *BlockCache) readBlock(height int) *walletrpc.CompactBlock { func (c *BlockCache) readBlock(height int) *walletrpc.CompactBlock {
blockLen := c.blockLength(height) blockLen := c.blockLength(height)
b := make([]byte, blockLen+8) b := make([]byte, blockLen+8)
@ -116,6 +124,7 @@ func (c *BlockCache) readBlock(height int) *walletrpc.CompactBlock {
return block return block
} }
// Caller should hold c.mutex.Lock().
func (c *BlockCache) setLatestHash() { func (c *BlockCache) setLatestHash() {
c.latestHash = nil c.latestHash = nil
// There is at least one block; get the last block's hash // There is at least one block; get the last block's hash
@ -132,6 +141,7 @@ func (c *BlockCache) setLatestHash() {
} }
// NewBlockCache returns an instance of a block cache object. // NewBlockCache returns an instance of a block cache object.
// (No locking here, we assume this is single-threaded.)
func NewBlockCache(dbPath string, chainName string, startHeight int, redownload bool) *BlockCache { func NewBlockCache(dbPath string, chainName string, startHeight int, redownload bool) *BlockCache {
c := &BlockCache{} c := &BlockCache{}
c.firstBlock = startHeight c.firstBlock = startHeight
@ -298,7 +308,12 @@ func (c *BlockCache) Get(height int) *walletrpc.CompactBlock {
} }
block := c.readBlock(height) block := c.readBlock(height)
if block == nil { if block == nil {
c.recoverFromCorruption(height) go func() {
// We hold only the read lock, need the exclusive lock.
c.mutex.Lock()
c.recoverFromCorruption(height)
c.mutex.Unlock()
}()
return nil return nil
} }
return block return block