diff --git a/src/index/base.h b/src/index/base.h --- a/src/index/base.h +++ b/src/index/base.h @@ -30,7 +30,7 @@ bool ReadBestBlock(CBlockLocator &locator) const; /// Write block locator of the chain that the txindex is in sync with. - bool WriteBestBlock(const CBlockLocator &locator); + void WriteBestBlock(CDBBatch &batch, const CBlockLocator &locator); }; private: @@ -52,8 +52,17 @@ /// over and the sync thread exits. void ThreadSync(); - /// Write the current chain block locator to the DB. - bool WriteBestBlock(const CBlockIndex *block_index); + /// Write the current index state (eg. chain block locator and + /// subclass-specific items) to disk. + /// + /// Recommendations for error handling: + /// If called on a successor of the previous committed best block in the + /// index, the index can continue processing without risk of corruption, + /// though the index state will need to catch up from further behind on + /// reboot. If the new state is not a successor of the previous state (due + /// to a chain reorganization), the index must halt until Commit succeeds or + /// else it could end up getting corrupted. + bool Commit(); protected: void @@ -71,6 +80,10 @@ return true; } + /// Virtual method called internally by Commit that can be overridden to + /// atomically commit more index state. + virtual bool CommitInternal(CDBBatch &batch); + virtual DB &GetDB() const = 0; /// Get the name of the index for display in logs. diff --git a/src/index/base.cpp b/src/index/base.cpp --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -41,8 +41,9 @@ return success; } -bool BaseIndex::DB::WriteBestBlock(const CBlockLocator &locator) { - return Write(DB_BEST_BLOCK, locator); +void BaseIndex::DB::WriteBestBlock(CDBBatch &batch, + const CBlockLocator &locator) { + batch.Write(DB_BEST_BLOCK, locator); } BaseIndex::~BaseIndex() { @@ -91,7 +92,12 @@ int64_t last_locator_write_time = 0; while (true) { if (m_interrupt) { - WriteBestBlock(pindex); + m_best_block_index = pindex; + // No need to handle errors in Commit. If it fails, the error + // will be already be logged. The best way to recover is to + // continue, as index cannot be corrupted by a missed commit to + // disk for an advanced index state. + Commit(); return; } @@ -99,9 +105,10 @@ LOCK(cs_main); const CBlockIndex *pindex_next = NextSyncBlock(pindex); if (!pindex_next) { - WriteBestBlock(pindex); m_best_block_index = pindex; m_synced = true; + // No need to handle errors in Commit. See rationale above. + Commit(); break; } pindex = pindex_next; @@ -116,8 +123,10 @@ if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) { - WriteBestBlock(pindex); + m_best_block_index = pindex; last_locator_write_time = current_time; + // No need to handle errors in Commit. See rationale above. + Commit(); } CBlock block; @@ -141,14 +150,22 @@ } } -bool BaseIndex::WriteBestBlock(const CBlockIndex *block_index) { - LOCK(cs_main); - if (!GetDB().WriteBestBlock(::ChainActive().GetLocator(block_index))) { - return error("%s: Failed to write locator to disk", __func__); +bool BaseIndex::Commit() { + CDBBatch batch(GetDB()); + if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) { + return error("%s: Failed to commit latest %s state", __func__, + GetName()); } return true; } +bool BaseIndex::CommitInternal(CDBBatch &batch) { + LOCK(cs_main); + GetDB().WriteBestBlock(batch, + ::ChainActive().GetLocator(m_best_block_index)); + return true; +} + void BaseIndex::BlockConnected( const std::shared_ptr &block, const CBlockIndex *pindex, const std::vector &txn_conflicted) { @@ -225,9 +242,11 @@ return; } - if (!GetDB().WriteBestBlock(locator)) { - error("%s: Failed to write locator to disk", __func__); - } + // No need to handle errors in Commit. If it fails, the error will be + // already be logged. The best way to recover is to continue, as index + // cannot be corrupted by a missed commit to disk for an advanced index + // state. + Commit(); } bool BaseIndex::BlockUntilSyncedToCurrentChain() {