diff --git a/src/dbwrapper.h b/src/dbwrapper.h index d016daf62..b606b4585 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -356,14 +356,16 @@ public: }; +template class CDBTransaction { -private: - CDBWrapper &db; +protected: + Parent &parent; + CommitTarget &commitTarget; struct KeyHolder { virtual ~KeyHolder() = default; virtual bool Less(const KeyHolder &b) const = 0; - virtual void Erase(CDBBatch &batch) = 0; + virtual void Erase(CommitTarget &commitTarget) = 0; }; typedef std::unique_ptr KeyHolderPtr; @@ -376,15 +378,15 @@ private: auto *b2 = dynamic_cast*>(&b); return key < b2->key; } - virtual void Erase(CDBBatch &batch) { - batch.Erase(key); + virtual void Erase(CommitTarget &commitTarget) { + commitTarget.Erase(key); } K key; }; struct KeyValueHolder { virtual ~KeyValueHolder() = default; - virtual void Write(CDBBatch &batch) = 0; + virtual void Write(CommitTarget &parent) = 0; }; typedef std::unique_ptr KeyValueHolderPtr; @@ -393,8 +395,13 @@ private: KeyValueHolderImpl(const KeyHolderImpl &_key, const V &_value) : key(_key), value(_value) { } - virtual void Write(CDBBatch &batch) { - batch.Write(key.key, value); + KeyValueHolderImpl(const KeyHolderImpl &_key, V &&_value) + : key(_key), + value(std::forward(_value)) { } + virtual void Write(CommitTarget &commitTarget) { + // we're moving the value instead of copying it. This means that Write() can only be called once per + // KeyValueHolderImpl instance. Commit() clears the write maps, so this ok. + commitTarget.Write(key.key, std::move(value)); } const KeyHolderImpl &key; V value; @@ -434,22 +441,34 @@ private: return getMapForType(deletes, create); } -public: - CDBTransaction(CDBWrapper &_db) : db(_db) {} - - template - void Write(const K& key, const V& value) { - KeyHolderPtr k(new KeyHolderImpl(key)); - KeyHolderImpl* k2 = dynamic_cast*>(k.get()); - KeyValueHolderPtr kv(new KeyValueHolderImpl(*k2, value)); + template + void writeImpl(KeyHolderImpl* k, KV&& kv) { + auto k2 = KeyHolderPtr(k); KeyValueMap *ds = getDeletesMap(false); if (ds) - ds->erase(k); + ds->erase(k2); KeyValueMap *ws = getWritesMap(true); - ws->erase(k); - ws->emplace(std::make_pair(std::move(k), std::move(kv))); + ws->erase(k2); + ws->emplace(std::make_pair(std::move(k2), std::forward(kv))); + } + +public: + CDBTransaction(Parent &_parent, CommitTarget &_commitTarget) : parent(_parent), commitTarget(_commitTarget) {} + + template + void Write(const K& key, const V& v) { + auto k = new KeyHolderImpl(key); + auto kv = std::make_unique>(*k, v); + writeImpl(k, std::move(kv)); + } + + template + void Write(const K& key, V&& v) { + auto k = new KeyHolderImpl(key); + auto kv = std::make_unique::type>>(*k, std::forward(v)); + writeImpl(k, std::move(kv)); } template @@ -462,7 +481,7 @@ public: KeyValueMap *ws = getWritesMap(false); if (ws) { - KeyValueMap::iterator it = ws->find(k); + auto it = ws->find(k); if (it != ws->end()) { auto *impl = dynamic_cast *>(it->second.get()); if (!impl) @@ -472,7 +491,7 @@ public: } } - return db.Read(key, value); + return parent.Read(key, value); } template @@ -487,7 +506,7 @@ public: if (ws && ws->count(k)) return true; - return db.Exists(key); + return parent.Exists(key); } template @@ -506,21 +525,18 @@ public: deletes.clear(); } - bool Commit() { - CDBBatch batch(db); + void Commit() { for (auto &p : deletes) { for (auto &p2 : p.second) { - p2.first->Erase(batch); + p2.first->Erase(commitTarget); } } for (auto &p : writes) { for (auto &p2 : p.second) { - p2.second->Write(batch); + p2.second->Write(commitTarget); } } - bool ret = db.WriteBatch(batch, true); Clear(); - return ret; } bool IsClean() { @@ -528,26 +544,29 @@ public: } }; +template class CScopedDBTransaction { +public: + typedef CDBTransaction Transaction; + private: - CDBTransaction &dbTransaction; + Transaction &dbTransaction; std::function commitHandler; std::function rollbackHandler; bool didCommitOrRollback{}; public: - CScopedDBTransaction(CDBTransaction &dbTx) : dbTransaction(dbTx) {} + CScopedDBTransaction(Transaction &dbTx) : dbTransaction(dbTx) {} ~CScopedDBTransaction() { if (!didCommitOrRollback) Rollback(); } - bool Commit() { + void Commit() { assert(!didCommitOrRollback); didCommitOrRollback = true; - bool result = dbTransaction.Commit(); + dbTransaction.Commit(); if (commitHandler) commitHandler(); - return result; } void Rollback() { assert(!didCommitOrRollback); @@ -557,9 +576,9 @@ public: rollbackHandler(); } - static std::unique_ptr Begin(CDBTransaction &dbTx) { + static std::unique_ptr> Begin(Transaction &dbTx) { assert(dbTx.IsClean()); - return std::unique_ptr(new CScopedDBTransaction(dbTx)); + return std::make_unique>(dbTx); } void SetCommitHandler(const std::function &h) { diff --git a/src/evo/evodb.cpp b/src/evo/evodb.cpp index b041be3d9..138353dba 100644 --- a/src/evo/evodb.cpp +++ b/src/evo/evodb.cpp @@ -8,10 +8,21 @@ CEvoDB* evoDb; CEvoDB::CEvoDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(fMemory ? "" : (GetDataDir() / "evodb"), nCacheSize, fMemory, fWipe), - dbTransaction(db) + rootBatch(db), + rootDBTransaction(db, rootBatch), + curDBTransaction(rootDBTransaction, rootDBTransaction) { } +bool CEvoDB::CommitRootTransaction() +{ + assert(curDBTransaction.IsClean()); + rootDBTransaction.Commit(); + bool ret = db.WriteBatch(rootBatch); + rootBatch.Clear(); + return ret; +} + bool CEvoDB::VerifyBestBlock(const uint256& hash) { // Make sure evodb is consistent. diff --git a/src/evo/evodb.h b/src/evo/evodb.h index 2506264c0..b7fef6d78 100644 --- a/src/evo/evodb.h +++ b/src/evo/evodb.h @@ -16,15 +16,22 @@ class CEvoDB private: CCriticalSection cs; CDBWrapper db; - CDBTransaction dbTransaction; + + typedef CDBTransaction RootTransaction; + typedef CDBTransaction CurTransaction; + typedef CScopedDBTransaction ScopedTransaction; + + CDBBatch rootBatch; + RootTransaction rootDBTransaction; + CurTransaction curDBTransaction; public: CEvoDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); - std::unique_ptr BeginTransaction() + std::unique_ptr BeginTransaction() { LOCK(cs); - auto t = CScopedDBTransaction::Begin(dbTransaction); + auto t = ScopedTransaction::Begin(curDBTransaction); return t; } @@ -32,28 +39,28 @@ public: bool Read(const K& key, V& value) { LOCK(cs); - return dbTransaction.Read(key, value); + return curDBTransaction.Read(key, value); } template void Write(const K& key, const V& value) { LOCK(cs); - dbTransaction.Write(key, value); + curDBTransaction.Write(key, value); } template bool Exists(const K& key) { LOCK(cs); - return dbTransaction.Exists(key); + return curDBTransaction.Exists(key); } template void Erase(const K& key) { LOCK(cs); - dbTransaction.Erase(key); + curDBTransaction.Erase(key); } CDBWrapper& GetRawDB() @@ -61,6 +68,8 @@ public: return db; } + bool CommitRootTransaction(); + bool VerifyBestBlock(const uint256& hash); void WriteBestBlock(const uint256& hash); }; diff --git a/src/validation.cpp b/src/validation.cpp index ad43ece34..e57409bcf 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2394,6 +2394,9 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n // Flush the chainstate (which may refer to block index entries). if (!pcoinsTip->Flush()) return AbortNode(state, "Failed to write to coin database"); + if (!evoDb->CommitRootTransaction()) { + return AbortNode(state, "Failed to commit EvoDB"); + } nLastFlush = nNow; } if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { @@ -2499,12 +2502,11 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); bool flushed = view.Flush(); assert(flushed); - bool committed = dbTx->Commit(); - assert(committed); + dbTx->Commit(); } LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(state, IsInitialBlockDownload() ? FLUSH_STATE_IF_NEEDED : FLUSH_STATE_ALWAYS)) + if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; // Resurrect mempool transactions from the disconnected block. std::vector vHashUpdate; @@ -2589,13 +2591,12 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); bool flushed = view.Flush(); assert(flushed); - bool committed = dbTx->Commit(); - assert(committed); + dbTx->Commit(); } int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(state, IsInitialBlockDownload() ? FLUSH_STATE_IF_NEEDED : FLUSH_STATE_ALWAYS)) + if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);