Skip to content

Commit

Permalink
index: Allow atomic commits of index state to be extended.
Browse files Browse the repository at this point in the history
Summary: This is a partial backport of Core [[bitcoin/bitcoin#14121 | PR14121]] : bitcoin/bitcoin@4368384

Test Plan:
  ninja all check-all

Reviewers: #bitcoin_abc, jasonbcox

Reviewed By: #bitcoin_abc, jasonbcox

Differential Revision: https://reviews.bitcoinabc.org/D6332
  • Loading branch information
jimpo authored and ftrader committed Jul 3, 2020
1 parent 6342f98 commit d06d0be
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 15 deletions.
43 changes: 31 additions & 12 deletions src/index/base.cpp
Expand Up @@ -41,8 +41,9 @@ bool BaseIndex::DB::ReadBestBlock(CBlockLocator &locator) const {
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() {
Expand Down Expand Up @@ -91,17 +92,23 @@ 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;
}

{
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;
Expand All @@ -116,8 +123,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;
Expand All @@ -141,14 +150,22 @@ void BaseIndex::ThreadSync() {
}
}

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<const CBlock> &block, const CBlockIndex *pindex,
const std::vector<CTransactionRef> &txn_conflicted) {
Expand Down Expand Up @@ -225,9 +242,11 @@ 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() {
Expand Down
19 changes: 16 additions & 3 deletions src/index/base.h
Expand Up @@ -30,7 +30,7 @@ class BaseIndex : public CValidationInterface {
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:
Expand All @@ -52,8 +52,17 @@ class BaseIndex : public CValidationInterface {
/// 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
Expand All @@ -71,6 +80,10 @@ class BaseIndex : public CValidationInterface {
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.
Expand Down

0 comments on commit d06d0be

Please sign in to comment.