From 4368384f1d267b011e03a805f934f5c47e2ca1b2 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Sat, 2 Mar 2019 18:35:55 -0800 Subject: index: Allow atomic commits of index state to be extended. --- src/index/base.cpp | 39 +++++++++++++++++++++++++++------------ src/index/base.h | 17 ++++++++++++++--- 2 files changed, 41 insertions(+), 15 deletions(-) (limited to 'src/index') diff --git a/src/index/base.cpp b/src/index/base.cpp index f6f59572ce..15ad04d462 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -41,9 +41,9 @@ bool BaseIndex::DB::ReadBestBlock(CBlockLocator& locator) const return success; } -bool BaseIndex::DB::WriteBestBlock(const CBlockLocator& locator) +void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator) { - return Write(DB_BEST_BLOCK, locator); + batch.Write(DB_BEST_BLOCK, locator); } BaseIndex::~BaseIndex() @@ -95,7 +95,11 @@ void BaseIndex::ThreadSync() 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; } @@ -103,9 +107,10 @@ void BaseIndex::ThreadSync() 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; @@ -119,8 +124,10 @@ void BaseIndex::ThreadSync() } 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; @@ -144,15 +151,22 @@ void BaseIndex::ThreadSync() } } -bool BaseIndex::WriteBestBlock(const CBlockIndex* block_index) +bool BaseIndex::Commit() { - LOCK(cs_main); - if (!GetDB().WriteBestBlock(chainActive.GetLocator(block_index))) { - return error("%s: Failed to write locator to disk", __func__); + 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) { @@ -224,9 +238,10 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator) 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() diff --git a/src/index/base.h b/src/index/base.h index 04ee6e6cc2..77e614a3af 100644 --- a/src/index/base.h +++ b/src/index/base.h @@ -32,7 +32,7 @@ protected: 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: @@ -54,8 +54,15 @@ private: /// 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 BlockConnected(const std::shared_ptr& block, const CBlockIndex* pindex, @@ -69,6 +76,10 @@ protected: /// Write update index entries for a newly connected block. virtual bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) { 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. -- cgit v1.2.3