flush ncc trie to disk at the same time pcoins and pblocktree are flushed to disk, so that if the program crashes they are at the same block

This commit is contained in:
Jimmy Kiselak 2015-03-23 14:51:29 -04:00
parent 7813f3157b
commit c9785bb21f
5 changed files with 62 additions and 64 deletions

View file

@ -1046,7 +1046,7 @@ bool AppInit2(boost::thread_group& threadGroup)
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
pcoinsTip = new CCoinsViewCache(pcoinscatcher); pcoinsTip = new CCoinsViewCache(pcoinscatcher);
pnccTrie = new CNCCTrie(); pnccTrie = new CNCCTrie(false, fReindex);
if (fReindex) if (fReindex)
pblocktree->WriteReindexing(true); pblocktree->WriteReindexing(true);

View file

@ -2081,6 +2081,8 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
} }
} }
// Finally flush the chainstate (which may refer to block index entries). // Finally flush the chainstate (which may refer to block index entries).
if (!pnccTrie->WriteToDisk())
return state.Abort("Failed to write to ncc trie database");
if (!pcoinsTip->Flush()) if (!pcoinsTip->Flush())
return state.Abort("Failed to write to coin database"); return state.Abort("Failed to write to coin database");
// Update best block in wallet (so we can detect restored wallets). // Update best block in wallet (so we can detect restored wallets).

View file

@ -40,7 +40,6 @@ bool CNCCTrieNode::removeValue(uint256& txhash, uint32_t nOut, CNodeValue& val,
CNodeValue currentTop = values.front(); CNodeValue currentTop = values.front();
//std::vector<CNodeValue>::iterator position = std::find(values.begin(), values.end(), val);
std::vector<CNodeValue>::iterator position; std::vector<CNodeValue>::iterator position;
for (position = values.begin(); position != values.end(); ++position) for (position = values.begin(); position != values.end(); ++position)
{ {
@ -214,11 +213,13 @@ bool CNCCTrie::recursiveCheckConsistency(CNCCTrieNode* node)
return calculatedHash == node->hash; return calculatedHash == node->hash;
} }
valueQueueType::iterator CNCCTrie::getQueueRow(int nHeight) valueQueueType::iterator CNCCTrie::getQueueRow(int nHeight, bool createIfNotExists)
{ {
valueQueueType::iterator itQueueRow = valueQueue.find(nHeight); valueQueueType::iterator itQueueRow = valueQueue.find(nHeight);
if (itQueueRow == valueQueue.end()) if (itQueueRow == valueQueue.end())
{ {
if (!createIfNotExists)
return itQueueRow;
std::vector<CValueQueueEntry> queueRow; std::vector<CValueQueueEntry> queueRow;
std::pair<valueQueueType::iterator, bool> ret; std::pair<valueQueueType::iterator, bool> ret;
ret = valueQueue.insert(std::pair<int, std::vector<CValueQueueEntry> >(nHeight, queueRow)); ret = valueQueue.insert(std::pair<int, std::vector<CValueQueueEntry> >(nHeight, queueRow));
@ -250,56 +251,54 @@ bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256&
// This can probably be optimized by checking each substring against // This can probably be optimized by checking each substring against
// the caches each time, but that will come after this is shown to // the caches each time, but that will come after this is shown to
// work correctly. // work correctly.
// As far as saving to disk goes, the idea is to use the list of // Disk strategy: keep a map of <string: dirty node>, where
// hashes to construct a list of (pointers to) nodes that have been // any nodes that are changed get put into the map, and any nodes
// altered in the update, and to construct a list of names of nodes // to be deleted will simply be empty (no value, no children). Nodes
// that have been deleted, and to use a leveldb batch to write them // whose hashes change will also be inserted into the map.
// all to disk. As of right now, txundo stuff will be handled by // As far as the queue goes, just keep a list of dirty queue entries.
// appending extra data to the normal txundo, which will call the // When the time comes, send all of that to disk in one batch, and
// normal insert/remove names, but obviously the opposite and in // empty the map/list.
// reverse order (though the order shouldn't ever matter).
bool success = true; bool success = true;
std::vector<std::string> deletedNames;
nodeCacheType changedNodes;
for (nodeCacheType::iterator itcache = cache.begin(); itcache != cache.end(); ++itcache) for (nodeCacheType::iterator itcache = cache.begin(); itcache != cache.end(); ++itcache)
{ {
CNCCTrieNode* pNode; success = updateName(itcache->first, itcache->second);
success = updateName(itcache->first, itcache->second, deletedNames, &pNode);
if (!success) if (!success)
return false; return false;
changedNodes[itcache->first] = pNode;
} }
for (hashMapType::iterator ithash = hashes.begin(); ithash != hashes.end(); ++ithash) for (hashMapType::iterator ithash = hashes.begin(); ithash != hashes.end(); ++ithash)
{ {
CNCCTrieNode* pNode; success = updateHash(ithash->first, ithash->second);
success = updateHash(ithash->first, ithash->second, &pNode);
if (!success) if (!success)
return false; return false;
changedNodes[ithash->first] = pNode;
} }
std::vector<int> vChangedQueueRows;
std::vector<int> vDeletedQueueRows;
for (valueQueueType::iterator itQueueCacheRow = queueCache.begin(); itQueueCacheRow != queueCache.end(); ++itQueueCacheRow) for (valueQueueType::iterator itQueueCacheRow = queueCache.begin(); itQueueCacheRow != queueCache.end(); ++itQueueCacheRow)
{ {
if (itQueueCacheRow->second.empty()) if (itQueueCacheRow->second.empty())
{ {
vDeletedQueueRows.push_back(itQueueCacheRow->first);
deleteQueueRow(itQueueCacheRow->first); deleteQueueRow(itQueueCacheRow->first);
} }
else else
{ {
vChangedQueueRows.push_back(itQueueCacheRow->first); valueQueueType::iterator itQueueRow = getQueueRow(itQueueCacheRow->first, true);
valueQueueType::iterator itQueueRow = getQueueRow(itQueueCacheRow->first);
itQueueRow->second.swap(itQueueCacheRow->second); itQueueRow->second.swap(itQueueCacheRow->second);
} }
vDirtyQueueRows.push_back(itQueueCacheRow->first);
} }
BatchWrite(changedNodes, deletedNames, hashBlockIn, vChangedQueueRows, vDeletedQueueRows, nNewHeight);
hashBlock = hashBlockIn; hashBlock = hashBlockIn;
nCurrentHeight = nNewHeight; nCurrentHeight = nNewHeight;
return true; return true;
} }
bool CNCCTrie::updateName(const std::string &name, CNCCTrieNode* updatedNode, std::vector<std::string>& deletedNames, CNCCTrieNode** pNodeRet) void CNCCTrie::markNodeDirty(const std::string &name, CNCCTrieNode* node)
{
std::pair<nodeCacheType::iterator, bool> ret;
ret = dirtyNodes.insert(std::pair<std::string, CNCCTrieNode*>(name, node));
if (ret.second == false)
ret.first->second = node;
}
bool CNCCTrie::updateName(const std::string &name, CNCCTrieNode* updatedNode)
{ {
CNCCTrieNode* current = &root; CNCCTrieNode* current = &root;
for (std::string::const_iterator itname = name.begin(); itname != name.end(); ++itname) for (std::string::const_iterator itname = name.begin(); itname != name.end(); ++itname)
@ -323,7 +322,7 @@ bool CNCCTrie::updateName(const std::string &name, CNCCTrieNode* updatedNode, st
} }
assert(current != NULL); assert(current != NULL);
current->values.swap(updatedNode->values); current->values.swap(updatedNode->values);
*pNodeRet = current; markNodeDirty(name, current);
for (nodeMapType::iterator itchild = current->children.begin(); itchild != current->children.end();) for (nodeMapType::iterator itchild = current->children.begin(); itchild != current->children.end();)
{ {
nodeMapType::iterator itupdatechild = updatedNode->children.find(itchild->first); nodeMapType::iterator itupdatechild = updatedNode->children.find(itchild->first);
@ -334,7 +333,7 @@ bool CNCCTrie::updateName(const std::string &name, CNCCTrieNode* updatedNode, st
std::stringstream ss; std::stringstream ss;
ss << name << itchild->first; ss << name << itchild->first;
std::string newName = ss.str(); std::string newName = ss.str();
if (!recursiveNullify(itchild->second, newName, deletedNames)) if (!recursiveNullify(itchild->second, newName))
return false; return false;
current->children.erase(itchild++); current->children.erase(itchild++);
} }
@ -344,7 +343,7 @@ bool CNCCTrie::updateName(const std::string &name, CNCCTrieNode* updatedNode, st
return true; return true;
} }
bool CNCCTrie::recursiveNullify(CNCCTrieNode* node, std::string& name, std::vector<std::string>& deletedNames) bool CNCCTrie::recursiveNullify(CNCCTrieNode* node, std::string& name)
{ {
assert(node != NULL); assert(node != NULL);
for (nodeMapType::iterator itchild = node->children.begin(); itchild != node->children.end(); ++itchild) for (nodeMapType::iterator itchild = node->children.begin(); itchild != node->children.end(); ++itchild)
@ -352,16 +351,16 @@ bool CNCCTrie::recursiveNullify(CNCCTrieNode* node, std::string& name, std::vect
std::stringstream ss; std::stringstream ss;
ss << name << itchild->first; ss << name << itchild->first;
std::string newName = ss.str(); std::string newName = ss.str();
if (!recursiveNullify(itchild->second, newName, deletedNames)) if (!recursiveNullify(itchild->second, newName))
return false; return false;
} }
node->children.clear(); node->children.clear();
markNodeDirty(name, NULL);
delete node; delete node;
deletedNames.push_back(name);
return true; return true;
} }
bool CNCCTrie::updateHash(const std::string& name, uint256& hash, CNCCTrieNode** pNodeRet) bool CNCCTrie::updateHash(const std::string& name, uint256& hash)
{ {
CNCCTrieNode* current = &root; CNCCTrieNode* current = &root;
for (std::string::const_iterator itname = name.begin(); itname != name.end(); ++itname) for (std::string::const_iterator itname = name.begin(); itname != name.end(); ++itname)
@ -373,45 +372,39 @@ bool CNCCTrie::updateHash(const std::string& name, uint256& hash, CNCCTrieNode**
} }
assert(current != NULL); assert(current != NULL);
current->hash = hash; current->hash = hash;
*pNodeRet = current; markNodeDirty(name, current);
return true; return true;
} }
void CNCCTrie::BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CNCCTrieNode* pNode) const void CNCCTrie::BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CNCCTrieNode* pNode) const
{ {
LogPrintf("%s: Writing %s to disk with %d values\n", __func__, name, pNode->values.size()); LogPrintf("%s: Writing %s to disk with %d values\n", __func__, name, pNode->values.size());
batch.Write(std::make_pair('n', name), *pNode); if (pNode)
} batch.Write(std::make_pair('n', name), *pNode);
else
void CNCCTrie::BatchEraseNode(CLevelDBBatch& batch, const std::string& name) const batch.Erase(std::make_pair('n', name));
{
batch.Erase(std::make_pair('n', name));
} }
void CNCCTrie::BatchWriteQueueRow(CLevelDBBatch& batch, int nRowNum) void CNCCTrie::BatchWriteQueueRow(CLevelDBBatch& batch, int nRowNum)
{ {
valueQueueType::iterator itQueueRow = getQueueRow(nRowNum); valueQueueType::iterator itQueueRow = getQueueRow(nRowNum, false);
batch.Write(std::make_pair('r', nRowNum), itQueueRow->second); if (itQueueRow != valueQueue.end())
batch.Write(std::make_pair('r', nRowNum), itQueueRow->second);
else
batch.Erase(std::make_pair('r', nRowNum));
} }
void CNCCTrie::BatchEraseQueueRow(CLevelDBBatch& batch, int nRowNum) bool CNCCTrie::WriteToDisk()
{
batch.Erase(std::make_pair('r', nRowNum));
}
bool CNCCTrie::BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames, const uint256& hashBlockIn, std::vector<int> vChangedQueueRows, std::vector<int> vDeletedQueueRows, int nNewHeight)
{ {
CLevelDBBatch batch; CLevelDBBatch batch;
for (nodeCacheType::iterator itcache = changedNodes.begin(); itcache != changedNodes.end(); ++itcache) for (nodeCacheType::iterator itcache = dirtyNodes.begin(); itcache != dirtyNodes.end(); ++itcache)
BatchWriteNode(batch, itcache->first, itcache->second); BatchWriteNode(batch, itcache->first, itcache->second);
for (std::vector<std::string>::iterator itname = deletedNames.begin(); itname != deletedNames.end(); ++itname) dirtyNodes.clear();
BatchEraseNode(batch, *itname); for (std::vector<int>::iterator itRowNum = vDirtyQueueRows.begin(); itRowNum != vDirtyQueueRows.end(); ++itRowNum)
for (std::vector<int>::iterator itRowNum = vChangedQueueRows.begin(); itRowNum != vChangedQueueRows.end(); ++itRowNum)
BatchWriteQueueRow(batch, *itRowNum); BatchWriteQueueRow(batch, *itRowNum);
for (std::vector<int>::iterator itRowNum = vDeletedQueueRows.begin(); itRowNum != vDeletedQueueRows.end(); ++itRowNum) vDirtyQueueRows.clear();
BatchEraseQueueRow(batch, *itRowNum); batch.Write('h', hashBlock);
batch.Write('h', hashBlockIn); batch.Write('t', nCurrentHeight);
batch.Write('t', nNewHeight);
return db.WriteBatch(batch); return db.WriteBatch(batch);
} }
@ -468,7 +461,7 @@ bool CNCCTrie::ReadFromDisk(bool check)
int nHeight; int nHeight;
ssKey >> nHeight; ssKey >> nHeight;
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
valueQueueType::iterator itQueueRow = getQueueRow(nHeight); valueQueueType::iterator itQueueRow = getQueueRow(nHeight, true);
ssValue >> itQueueRow->second; ssValue >> itQueueRow->second;
} }
pcursor->Next(); pcursor->Next();

View file

@ -26,7 +26,6 @@ public:
int nHeight; int nHeight;
int nValidAtHeight; int nValidAtHeight;
CNodeValue() {}; CNodeValue() {};
//CNodeValue(uint256 txhash, uint32_t nOut) : txhash(txhash), nOut(nOut), nAmount(0), nHeight(0), nValidAtHeight(0) {}
CNodeValue(uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) : txhash(txhash), nOut(nOut), nAmount(nAmount), nHeight(nHeight), nValidAtHeight(nValidAtHeight) {} CNodeValue(uint256 txhash, uint32_t nOut, CAmount nAmount, int nHeight, int nValidAtHeight) : txhash(txhash), nOut(nOut), nAmount(nAmount), nHeight(nHeight), nValidAtHeight(nValidAtHeight) {}
std::string ToString(); std::string ToString();
@ -149,11 +148,12 @@ class CNCCTrieCache;
class CNCCTrie class CNCCTrie
{ {
public: public:
CNCCTrie() : db(GetDataDir() / "ncctrie", 100, false, false), nCurrentHeight(1), root(uint256S("0000000000000000000000000000000000000000000000000000000000000001")) {} CNCCTrie(bool fMemory = false, bool fWipe = false) : db(GetDataDir() / "ncctrie", 100, fMemory, fWipe), nCurrentHeight(1), root(uint256S("0000000000000000000000000000000000000000000000000000000000000001")) {}
uint256 getMerkleHash(); uint256 getMerkleHash();
CLevelDBWrapper db; CLevelDBWrapper db;
bool empty() const; bool empty() const;
bool checkConsistency(); bool checkConsistency();
bool WriteToDisk();
bool ReadFromDisk(bool check = false); bool ReadFromDisk(bool check = false);
json_spirit::Array dumpToJSON() const; json_spirit::Array dumpToJSON() const;
bool getInfoForName(const std::string& name, CNodeValue& val) const; bool getInfoForName(const std::string& name, CNodeValue& val) const;
@ -163,17 +163,20 @@ public:
friend class CNCCTrieCache; friend class CNCCTrieCache;
private: private:
bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock, valueQueueType& queueCache, int nNewHeight); bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock, valueQueueType& queueCache, int nNewHeight);
bool updateName(const std::string& name, CNCCTrieNode* updatedNode, std::vector<std::string>& deletedNames, CNCCTrieNode** pNodeRet); bool updateName(const std::string& name, CNCCTrieNode* updatedNode);
bool updateHash(const std::string& name, uint256& hash, CNCCTrieNode** pNodeRet); bool updateHash(const std::string& name, uint256& hash);
bool recursiveNullify(CNCCTrieNode* node, std::string& name, std::vector<std::string>& deletedNames); bool recursiveNullify(CNCCTrieNode* node, std::string& name);
bool recursiveCheckConsistency(CNCCTrieNode* node); bool recursiveCheckConsistency(CNCCTrieNode* node);
bool BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames, const uint256& hashBlock, std::vector<int> vChangedQueueRows, std::vector<int> vDeletedQueueRows, int nNewHeight);
bool InsertFromDisk(const std::string& name, CNCCTrieNode* node); bool InsertFromDisk(const std::string& name, CNCCTrieNode* node);
bool recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const; bool recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const;
CNCCTrieNode root; CNCCTrieNode root;
uint256 hashBlock; uint256 hashBlock;
valueQueueType valueQueue; valueQueueType valueQueue;
valueQueueType::iterator getQueueRow(int nHeight); valueQueueType::iterator getQueueRow(int nHeight, bool deleteIfNotExists);
nodeCacheType dirtyNodes;
std::vector<int> vDirtyQueueRows;
void markNodeDirty(const std::string& name, CNCCTrieNode* node);
void deleteQueueRow(int nHeight); void deleteQueueRow(int nHeight);
void BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CNCCTrieNode* pNode) const; void BatchWriteNode(CLevelDBBatch& batch, const std::string& name, const CNCCTrieNode* pNode) const;
void BatchEraseNode(CLevelDBBatch& batch, const std::string& nome) const; void BatchEraseNode(CLevelDBBatch& batch, const std::string& nome) const;

View file

@ -42,7 +42,7 @@ struct TestingSetup {
pblocktree = new CBlockTreeDB(1 << 20, true); pblocktree = new CBlockTreeDB(1 << 20, true);
pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true);
pcoinsTip = new CCoinsViewCache(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinsdbview);
pnccTrie = new CNCCTrie(); pnccTrie = new CNCCTrie(true);
InitBlockIndex(); InitBlockIndex();
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
bool fFirstRun; bool fFirstRun;