SHAMap performance improvements (RIPD-434)

This reworks the way SHAMaps are stored, flushed, backed, and
traversed. Rather than storing the linkages in the SHAMap itself,
that information is now stored in the nodes. This makes
snapshotting much cheaper and also allows traverse work done on
behalf of one SHAMap to be used by other SHAMaps that share inner
nodes with that SHAMap.

When a SHAMap is modified, nodes are modified all the way up to the
root. This means that the modified nodes in a SHAMap can easily be
traversed for flushing. So they don't need to be separately tracked.

Summary
* Remove mTNByID
* Remove mDirtyNodes
* Much faster traverses
* Much Faster snapshots
* New algorithm for flushing
* New vistNodes/visitLeaves
* Avoid I/O if a map is unbacked
This commit is contained in:
David Schwartz
2014-07-31 16:38:58 -07:00
committed by Tom Ritchford
parent d26241de0e
commit fc560179e0
15 changed files with 889 additions and 1064 deletions

View File

@@ -1027,35 +1027,19 @@ private:
// Set up to write SHAMap changes to our database, // Set up to write SHAMap changes to our database,
// perform updates, extract changes // perform updates, extract changes
newLCL->peekTransactionMap ()->armDirty (); // start tracking changes
newLCL->peekAccountStateMap ()->armDirty ();
WriteLog (lsDEBUG, LedgerConsensus) WriteLog (lsDEBUG, LedgerConsensus)
<< "Applying consensus set transactions to the" << "Applying consensus set transactions to the"
<< " last closed ledger"; << " last closed ledger";
applyTransactions (set, newLCL, newLCL, retriableTransactions, false); applyTransactions (set, newLCL, newLCL, retriableTransactions, false);
newLCL->updateSkipList (); newLCL->updateSkipList ();
newLCL->setClosed (); newLCL->setClosed ();
std::shared_ptr<SHAMap::DirtySet> acctNodes
= newLCL->peekAccountStateMap ()->disarmDirty (); // stop tracking changes
std::shared_ptr<SHAMap::DirtySet> txnNodes
= newLCL->peekTransactionMap ()->disarmDirty ();
// write out dirty nodes (temporarily done here) int asf = newLCL->peekAccountStateMap ()->flushDirty (
int fc; hotACCOUNT_NODE, newLCL->getLedgerSeq());
int tmf = newLCL->peekTransactionMap ()->flushDirty (
while ((fc = newLCL->peekAccountStateMap()->flushDirty ( hotTRANSACTION_NODE, newLCL->getLedgerSeq());
*acctNodes, 256, hotACCOUNT_NODE, newLCL->getLedgerSeq ())) > 0) WriteLog (lsDEBUG, LedgerConsensus) << "Flushed " << asf << " account and " <<
{ tmf << "transaction nodes";
WriteLog (lsTRACE, LedgerConsensus)
<< "Flushed " << fc << " dirty state nodes";
}
while ((fc = newLCL->peekTransactionMap()->flushDirty (
*txnNodes, 256, hotTRANSACTION_NODE, newLCL->getLedgerSeq ())) > 0)
{
WriteLog (lsTRACE, LedgerConsensus)
<< "Flushed " << fc << " dirty transaction nodes";
}
// Accept ledger // Accept ledger
newLCL->setAccepted (closeTime, mCloseResolution, closeTimeCorrect); newLCL->setAccepted (closeTime, mCloseResolution, closeTimeCorrect);

View File

@@ -1232,9 +1232,6 @@ Json::Value InboundLedger::getJson (int)
{ {
ret["have_state"] = mHaveState; ret["have_state"] = mHaveState;
ret["have_transactions"] = mHaveTransactions; ret["have_transactions"] = mHaveTransactions;
if (!mHaveState)
ret["state_nodes"] = static_cast<Json::Value::UInt>
(mLedger->peekAccountStateMap()->size());
} }
if (mAborted) if (mAborted)

View File

@@ -57,14 +57,9 @@ Ledger::Ledger (RippleAddress const& masterID, std::uint64_t startAmount)
WriteLog (lsTRACE, Ledger) WriteLog (lsTRACE, Ledger)
<< "root account: " << startAccount->peekSLE ().getJson (0); << "root account: " << startAccount->peekSLE ().getJson (0);
mAccountStateMap->armDirty ();
writeBack (lepCREATE, startAccount->getSLE ()); writeBack (lepCREATE, startAccount->getSLE ());
auto dirtyNodes = mAccountStateMap->disarmDirty(); mAccountStateMap->flushDirty (hotACCOUNT_NODE, mLedgerSeq);
mAccountStateMap->flushDirty (
*dirtyNodes, 256, hotACCOUNT_NODE, mLedgerSeq);
// TODO(tom): why 256?
initializeFees (); initializeFees ();
} }
@@ -243,16 +238,12 @@ Ledger::~Ledger ()
{ {
if (mTransactionMap) if (mTransactionMap)
{ {
logTimedDestroy <Ledger> (mTransactionMap, logTimedDestroy <Ledger> (mTransactionMap, "mTransactionMap");
"mTransactionMap with " +
std::to_string(mTransactionMap->size ()) + " items");
} }
if (mAccountStateMap) if (mAccountStateMap)
{ {
logTimedDestroy <Ledger> (mAccountStateMap, logTimedDestroy <Ledger> (mAccountStateMap, "mAccountStateMap");
"mAccountStateMap with " +
std::to_string (mAccountStateMap->size ()) + " items");
} }
} }

View File

@@ -239,16 +239,6 @@ public:
{ {
return mAccountStateMap; return mAccountStateMap;
} }
void dropCache ()
{
assert (isImmutable ());
if (mTransactionMap)
mTransactionMap->dropCache ();
if (mAccountStateMap)
mAccountStateMap->dropCache ();
}
// returns false on error // returns false on error
bool addSLE (SLE const& sle); bool addSLE (SLE const& sle);

View File

@@ -305,8 +305,6 @@ public:
return false; return false;
} }
nodeLedger->dropCache();
return true; return true;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -114,11 +114,8 @@ public:
typedef std::pair<SHAMapItem::ref, SHAMapItem::ref> DeltaRef; typedef std::pair<SHAMapItem::ref, SHAMapItem::ref> DeltaRef;
typedef std::map<uint256, DeltaItem> Delta; typedef std::map<uint256, DeltaItem> Delta;
typedef hash_map<SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash> NodeMap; typedef hash_map<SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash> NodeMap;
typedef hash_set<SHAMapNodeID, SHAMapNode_hash> DirtySet;
typedef boost::shared_mutex LockType; typedef std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> SharedPtrNodeStack;
typedef boost::shared_lock<LockType> ScopedReadLockType;
typedef boost::unique_lock<LockType> ScopedWriteLockType;
public: public:
// build new map // build new map
@@ -138,17 +135,10 @@ public:
~SHAMap (); ~SHAMap ();
std::size_t size () const noexcept // Returns a new map that's a snapshot of this one.
{ // Handles copy on write for mutable snapshots.
return mTNByID.size ();
}
// Returns a new map that's a snapshot of this one. Force CoW
SHAMap::pointer snapShot (bool isMutable); SHAMap::pointer snapShot (bool isMutable);
// Remove nodes from memory
void dropCache ();
void setLedgerSeq (std::uint32_t lseq) void setLedgerSeq (std::uint32_t lseq)
{ {
mLedgerSeq = lseq; mLedgerSeq = lseq;
@@ -159,7 +149,7 @@ public:
// normal hash access functions // normal hash access functions
bool hasItem (uint256 const& id); bool hasItem (uint256 const& id);
bool delItem (uint256 const& id); bool delItem (uint256 const& id);
bool addItem (const SHAMapItem & i, bool isTransaction, bool hasMeta); bool addItem (SHAMapItem const& i, bool isTransaction, bool hasMeta);
uint256 getHash () const uint256 getHash () const
{ {
@@ -182,7 +172,9 @@ public:
SHAMapItem::pointer peekNextItem (uint256 const& ); SHAMapItem::pointer peekNextItem (uint256 const& );
SHAMapItem::pointer peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type); SHAMapItem::pointer peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type);
SHAMapItem::pointer peekPrevItem (uint256 const& ); SHAMapItem::pointer peekPrevItem (uint256 const& );
void visitLeaves(std::function<void (SHAMapItem::ref)>);
void visitNodes (std::function<void (SHAMapTreeNode&)> const&);
void visitLeaves(std::function<void (SHAMapItem::ref)> const&);
// comparison/sync functions // comparison/sync functions
void getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max, void getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max,
@@ -191,11 +183,11 @@ public:
std::list<Blob >& rawNode, bool fatRoot, bool fatLeaves); std::list<Blob >& rawNode, bool fatRoot, bool fatLeaves);
bool getRootNode (Serializer & s, SHANodeFormat format); bool getRootNode (Serializer & s, SHANodeFormat format);
std::vector<uint256> getNeededHashes (int max, SHAMapSyncFilter * filter); std::vector<uint256> getNeededHashes (int max, SHAMapSyncFilter * filter);
SHAMapAddNode addRootNode (uint256 const& hash, Blob const & rootNode, SHANodeFormat format, SHAMapAddNode addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter * filter); SHAMapSyncFilter * filter);
SHAMapAddNode addRootNode (Blob const & rootNode, SHANodeFormat format, SHAMapAddNode addRootNode (Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter * filter); SHAMapSyncFilter * filter);
SHAMapAddNode addKnownNode (const SHAMapNodeID & nodeID, Blob const & rawNode, SHAMapAddNode addKnownNode (SHAMapNodeID const& nodeID, Blob const& rawNode,
SHAMapSyncFilter * filter); SHAMapSyncFilter * filter);
// status functions // status functions
@@ -225,10 +217,8 @@ public:
// return value: true=successfully completed, false=too different // return value: true=successfully completed, false=too different
bool compare (SHAMap::ref otherMap, Delta & differences, int maxCount); bool compare (SHAMap::ref otherMap, Delta & differences, int maxCount);
int armDirty (); int flushDirty (NodeObjectType t, std::uint32_t seq);
int flushDirty (DirtySet & dirtySet, int maxNodes, NodeObjectType t, int unshare ();
std::uint32_t seq);
std::shared_ptr<DirtySet> disarmDirty ();
void walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing); void walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing);
@@ -238,81 +228,105 @@ public:
void getFetchPack (SHAMap * have, bool includeLeaves, int max, std::function<void (uint256 const&, const Blob&)>); void getFetchPack (SHAMap * have, bool includeLeaves, int max, std::function<void (uint256 const&, const Blob&)>);
void setTXMap () void setUnbacked ()
{ {
mTXMap = true; mBacked = false;
} }
void dump (bool withHashes = false);
private: private:
// trusted path operations - prove a particular node is in a particular ledger // trusted path operations - prove a particular node is in a particular ledger
std::list<Blob > getTrustedPath (uint256 const& index); std::list<Blob > getTrustedPath (uint256 const& index);
SHAMapTreeNode::pointer fetchNodeExternal (const SHAMapNodeID & id,
uint256 const& hash); // throws
SHAMapTreeNode::pointer fetchNodeExternalNT (const SHAMapNodeID & id,
uint256 const& hash); // no throw
bool getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeFormat format); bool getPath (uint256 const& index, std::vector< Blob >& nodes, SHANodeFormat format);
void dump (bool withHashes = false);
// tree node cache operations // tree node cache operations
SHAMapTreeNode::pointer getCache (uint256 const& hash); SHAMapTreeNode::pointer getCache (uint256 const& hash);
void canonicalize (uint256 const& hash, SHAMapTreeNode::pointer&); void canonicalize (uint256 const& hash, SHAMapTreeNode::pointer&);
void dirtyUp (std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>>& stack, // database operations
uint256 const& target, uint256 prevHash); SHAMapTreeNode::pointer fetchNodeFromDB (uint256 const& hash);
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>>
SHAMapTreeNode::pointer fetchNodeNT (uint256 const& hash);
SHAMapTreeNode::pointer fetchNodeNT (
SHAMapNodeID const& id,
uint256 const& hash,
SHAMapSyncFilter *filter);
SHAMapTreeNode::pointer fetchNode (uint256 const& hash);
SHAMapTreeNode::pointer checkFilter (uint256 const& hash, SHAMapNodeID const& id,
SHAMapSyncFilter* filter);
/** Update hashes up to the root */
void dirtyUp (SharedPtrNodeStack& stack,
uint256 const& target, SHAMapTreeNode::pointer terminal);
/** Get the path from the root to the specified node */
SharedPtrNodeStack
getStack (uint256 const& id, bool include_nonmatching_leaf); getStack (uint256 const& id, bool include_nonmatching_leaf);
SHAMapTreeNode::pointer walkTo (uint256 const& id, bool modify);
/** Walk to the specified index, returning the node */
SHAMapTreeNode* walkToPointer (uint256 const& id); SHAMapTreeNode* walkToPointer (uint256 const& id);
SHAMapTreeNode::pointer checkCacheNode (const SHAMapNodeID&);
void returnNode (SHAMapTreeNode::pointer&, SHAMapNodeID const& nodeID,
bool modify);
void trackNewNode (SHAMapTreeNode::pointer&, SHAMapNodeID const&);
SHAMapTreeNode::pointer getNode (const SHAMapNodeID & id); /** Unshare the node, allowing it to be modified */
SHAMapTreeNode::pointer getNode (const SHAMapNodeID & id, uint256 const& hash, bool modify); void unshareNode (SHAMapTreeNode::pointer&, SHAMapNodeID const& nodeID);
SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id);
SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id, uint256 const& hash);
SHAMapTreeNode* getNodePointerNT (const SHAMapNodeID & id, uint256 const& hash);
SHAMapTreeNode* getNodePointer (const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter);
SHAMapTreeNode* getNodePointerNT (const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter);
SHAMapTreeNode* firstBelow (SHAMapTreeNode*, SHAMapNodeID);
SHAMapTreeNode* lastBelow (SHAMapTreeNode*, SHAMapNodeID);
// Non-blocking version of getNodePointerNT /** prepare a node to be modified before flushing */
SHAMapTreeNode* getNodeAsync ( void preFlushNode (SHAMapTreeNode::pointer& node);
const SHAMapNodeID & id, uint256 const& hash, SHAMapSyncFilter * filter, bool& pending);
SHAMapItem::pointer onlyBelow (SHAMapTreeNode*, SHAMapNodeID); /** write and canonicalize modified node */
void eraseChildren (SHAMapTreeNode::pointer, SHAMapNodeID); void writeNode (NodeObjectType t, std::uint32_t seq,
void dropBelow (SHAMapTreeNode*, SHAMapNodeID); SHAMapTreeNode::pointer& node);
bool hasInnerNode (const SHAMapNodeID & nodeID, uint256 const& hash);
SHAMapTreeNode* firstBelow (SHAMapTreeNode*);
SHAMapTreeNode* lastBelow (SHAMapTreeNode*);
// Simple descent
// Get a child of the specified node
SHAMapTreeNode* descend (SHAMapTreeNode*, int branch);
SHAMapTreeNode* descendThrow (SHAMapTreeNode*, int branch);
SHAMapTreeNode::pointer descend (SHAMapTreeNode::ref, int branch);
SHAMapTreeNode::pointer descendThrow (SHAMapTreeNode::ref, int branch);
// Descend with filter
SHAMapTreeNode* descendAsync (SHAMapTreeNode* parent, int branch,
SHAMapNodeID const& childID, SHAMapSyncFilter* filter, bool& pending);
std::pair <SHAMapTreeNode*, SHAMapNodeID>
descend (SHAMapTreeNode* parent, SHAMapNodeID const& parentID,
int branch, SHAMapSyncFilter* filter);
// Non-storing
// Does not hook the returned node to its parent
SHAMapTreeNode::pointer descendNoStore (SHAMapTreeNode::ref, int branch);
/** If there is only one leaf below this node, get its contents */
SHAMapItem::pointer onlyBelow (SHAMapTreeNode*);
bool hasInnerNode (SHAMapNodeID const& nodeID, uint256 const& hash);
bool hasLeafNode (uint256 const& tag, uint256 const& hash); bool hasLeafNode (uint256 const& tag, uint256 const& hash);
bool walkBranch (SHAMapTreeNode* node, SHAMapNodeID nodeID, bool walkBranch (SHAMapTreeNode* node,
SHAMapItem::ref otherMapItem, bool isFirstMap, SHAMapItem::ref otherMapItem, bool isFirstMap,
Delta & differences, int & maxCount); Delta & differences, int & maxCount);
void visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function); void visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function);
private: int walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq);
// This lock protects key SHAMap structures. private:
// One may change anything with a write lock.
// With a read lock, one may not invalidate pointers to existing members of mTNByID
mutable LockType mLock;
FullBelowCache& m_fullBelowCache; FullBelowCache& m_fullBelowCache;
std::uint32_t mSeq; std::uint32_t mSeq;
std::uint32_t mLedgerSeq; // sequence number of ledger this is part of std::uint32_t mLedgerSeq; // sequence number of ledger this is part of
SyncUnorderedMapType< SHAMapNodeID, SHAMapTreeNode::pointer, SHAMapNode_hash > mTNByID;
std::shared_ptr<DirtySet> mDirtyNodes;
TreeNodeCache& mTreeNodeCache; TreeNodeCache& mTreeNodeCache;
SHAMapTreeNode::pointer root; SHAMapTreeNode::pointer root;
SHAMapState mState; SHAMapState mState;
SHAMapType mType; SHAMapType mType;
bool mTXMap; // Map of transactions without metadata bool mBacked; // Map is backed by the database
MissingNodeHandler m_missing_node_handler; MissingNodeHandler m_missing_node_handler;
}; };

View File

@@ -25,47 +25,29 @@ namespace ripple {
// branches with the same branch hash. A limit can be passed so // branches with the same branch hash. A limit can be passed so
// that we will abort early if a node sends a map to us that // that we will abort early if a node sends a map to us that
// makes no sense at all. (And our sync algorithm will avoid // makes no sense at all. (And our sync algorithm will avoid
// synchronizing matching brances too.) // synchronizing matching branches too.)
class SHAMapDeltaNode bool SHAMap::walkBranch (SHAMapTreeNode* node,
{
public:
SHAMapNodeID mNodeID;
uint256 mOurHash, mOtherHash;
SHAMapDeltaNode (const SHAMapNodeID& id, uint256 const& ourHash, uint256 const& otherHash) :
mNodeID (id), mOurHash (ourHash), mOtherHash (otherHash)
{
;
}
};
bool SHAMap::walkBranch (SHAMapTreeNode* node, SHAMapNodeID nodeID,
SHAMapItem::ref otherMapItem, bool isFirstMap, SHAMapItem::ref otherMapItem, bool isFirstMap,
Delta& differences, int& maxCount) Delta& differences, int& maxCount)
{ {
// Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map // Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map
std::stack<std::pair<SHAMapTreeNode*, SHAMapNodeID>> nodeStack; std::stack <SHAMapTreeNode*, std::vector<SHAMapTreeNode*>> nodeStack;
nodeStack.push ({node, nodeID}); nodeStack.push ({node});
bool emptyBranch = !otherMapItem; bool emptyBranch = !otherMapItem;
while (!nodeStack.empty ()) while (!nodeStack.empty ())
{ {
std::tie(node, nodeID) = nodeStack.top (); node = nodeStack.top ();
nodeStack.pop (); nodeStack.pop ();
if (node->isInner ()) if (node->isInner ())
{ {
// This is an inner node, add all non-empty branches // This is an inner node, add all non-empty branches
uint256 childNodeHash;
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ if (!node->isEmptyBranch (i))
SHAMapNodeID childNodeID = nodeID; nodeStack.push ({descendThrow (node, i)});
if (node->descend (i, childNodeID, childNodeHash))
nodeStack.push ({getNodePointer (childNodeID, childNodeHash),
childNodeID});
}
} }
else else
{ {
@@ -136,27 +118,23 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
assert (isValid () && otherMap && otherMap->isValid ()); assert (isValid () && otherMap && otherMap->isValid ());
std::stack<SHAMapDeltaNode> nodeStack; // track nodes we've pushed using StackEntry = std::pair <SHAMapTreeNode*, SHAMapTreeNode*>;
std::stack <StackEntry, std::vector<StackEntry>> nodeStack; // track nodes we've pushed
ScopedReadLockType sl (mLock);
if (getHash () == otherMap->getHash ()) if (getHash () == otherMap->getHash ())
return true; return true;
nodeStack.push (SHAMapDeltaNode (SHAMapNodeID (), getHash (), nodeStack.push ({root.get(), otherMap->root.get()});
otherMap->getHash ()));
while (!nodeStack.empty ()) while (!nodeStack.empty ())
{ {
SHAMapDeltaNode dNode (nodeStack.top ()); SHAMapTreeNode* ourNode = nodeStack.top().first;
SHAMapTreeNode* otherNode = nodeStack.top().second;
nodeStack.pop (); nodeStack.pop ();
SHAMapTreeNode* ourNode = getNodePointer (dNode.mNodeID, dNode.mOurHash);
SHAMapTreeNode* otherNode = otherMap->getNodePointer (dNode.mNodeID,
dNode.mOtherHash);
if (!ourNode || !otherNode) if (!ourNode || !otherNode)
{ {
assert (false); assert (false);
throw SHAMapMissingNode (mType, dNode.mNodeID, uint256 ()); throw SHAMapMissingNode (mType, uint256 ());
} }
if (ourNode->isLeaf () && otherNode->isLeaf ()) if (ourNode->isLeaf () && otherNode->isLeaf ())
@@ -190,14 +168,14 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
} }
else if (ourNode->isInner () && otherNode->isLeaf ()) else if (ourNode->isInner () && otherNode->isLeaf ())
{ {
if (!walkBranch (ourNode, dNode.mNodeID, otherNode->peekItem (), if (!walkBranch (ourNode, otherNode->peekItem (),
true, differences, maxCount)) true, differences, maxCount))
return false; return false;
} }
else if (ourNode->isLeaf () && otherNode->isInner ()) else if (ourNode->isLeaf () && otherNode->isInner ())
{ {
if (!otherMap->walkBranch (otherNode, dNode.mNodeID, if (!otherMap->walkBranch (otherNode, ourNode->peekItem (),
ourNode->peekItem (), false, differences, maxCount)) false, differences, maxCount))
return false; return false;
} }
else if (ourNode->isInner () && otherNode->isInner ()) else if (ourNode->isInner () && otherNode->isInner ())
@@ -208,10 +186,8 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
if (otherNode->isEmptyBranch (i)) if (otherNode->isEmptyBranch (i))
{ {
// We have a branch, the other tree does not // We have a branch, the other tree does not
SHAMapNodeID childNodeID = dNode.mNodeID.getChildNodeID(i); SHAMapTreeNode* iNode = descendThrow (ourNode, i);
SHAMapTreeNode* iNode = getNodePointer (childNodeID, if (!walkBranch (iNode,
ourNode->getChildHash (i));
if (!walkBranch (iNode, childNodeID,
SHAMapItem::pointer (), true, SHAMapItem::pointer (), true,
differences, maxCount)) differences, maxCount))
return false; return false;
@@ -219,20 +195,16 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
else if (ourNode->isEmptyBranch (i)) else if (ourNode->isEmptyBranch (i))
{ {
// The other tree has a branch, we do not // The other tree has a branch, we do not
SHAMapNodeID childNodeID = dNode.mNodeID.getChildNodeID(i);
SHAMapTreeNode* iNode = SHAMapTreeNode* iNode =
otherMap->getNodePointer(childNodeID, otherMap->descendThrow(otherNode, i);
otherNode->getChildHash (i)); if (!otherMap->walkBranch (iNode,
if (!otherMap->walkBranch (iNode, childNodeID,
SHAMapItem::pointer(), SHAMapItem::pointer(),
false, differences, maxCount)) false, differences, maxCount))
return false; return false;
} }
else // The two trees have different non-empty branches else // The two trees have different non-empty branches
nodeStack.push (SHAMapDeltaNode ( nodeStack.push ({descendThrow (ourNode, i),
dNode.mNodeID.getChildNodeID (i), otherMap->descendThrow (otherNode, i)});
ourNode->getChildHash (i),
otherNode->getChildHash (i)));
} }
} }
else else
@@ -244,42 +216,37 @@ bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing) void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing)
{ {
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> nodeStack; std::stack <SHAMapTreeNode::pointer,
std::vector <SHAMapTreeNode::pointer>> nodeStack;
ScopedReadLockType sl (mLock);
if (!root->isInner ()) // root is only node, and we have it if (!root->isInner ()) // root is only node, and we have it
return; return;
nodeStack.push ({root, SHAMapNodeID{}}); nodeStack.push (root);
while (!nodeStack.empty ()) while (!nodeStack.empty ())
{ {
SHAMapTreeNode::pointer node; SHAMapTreeNode::pointer node = std::move (nodeStack.top());
SHAMapNodeID nodeID;
std::tie(node, nodeID) = nodeStack.top ();
nodeStack.pop (); nodeStack.pop ();
uint256 childNodeHash;
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
SHAMapNodeID childNodeID = nodeID; if (!node->isEmptyBranch (i))
if (node->descend (i, childNodeID, childNodeHash))
{ {
try SHAMapTreeNode::pointer nextNode = descendNoStore (node, i);
{
SHAMapTreeNode::pointer d = getNode(childNodeID,
childNodeHash, false);
if (d->isInner ())
nodeStack.push ({d, childNodeID});
}
catch (SHAMapMissingNode& n)
{
missingNodes.push_back (n);
if (--maxMissing <= 0) if (nextNode)
return; {
} if (nextNode->isInner ())
} nodeStack.push (std::move (nextNode));
}
else
{
missingNodes.emplace_back (mType, node->getChildHash (i));
if (--maxMissing <= 0)
return;
}
}
} }
} }
} }

View File

@@ -24,16 +24,16 @@ std::ostream& operator<< (std::ostream& out, const SHAMapMissingNode& mn)
switch (mn.getMapType ()) switch (mn.getMapType ())
{ {
case smtTRANSACTION: case smtTRANSACTION:
out << "Missing/TXN(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")"; out << "Missing/TXN(" << mn.getNodeHash () << ")";
break; break;
case smtSTATE: case smtSTATE:
out << "Missing/STA(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")"; out << "Missing/STA(" << mn.getNodeHash () << ")";
break; break;
case smtFREE: case smtFREE:
default: default:
out << "Missing/" << mn.getNodeID (); out << "Missing/" << mn.getNodeHash ();
break; break;
}; };

View File

@@ -33,11 +33,9 @@ class SHAMapMissingNode : public std::runtime_error
{ {
public: public:
SHAMapMissingNode (SHAMapType t, SHAMapMissingNode (SHAMapType t,
SHAMapNodeID const& nodeID,
uint256 const& nodeHash) uint256 const& nodeHash)
: std::runtime_error ("SHAMapMissingNode") : std::runtime_error ("SHAMapMissingNode")
, mType (t) , mType (t)
, mNodeID (nodeID)
, mNodeHash (nodeHash) , mNodeHash (nodeHash)
{ {
} }
@@ -51,11 +49,6 @@ public:
return mType; return mType;
} }
SHAMapNodeID const& getNodeID () const
{
return mNodeID;
}
uint256 const& getNodeHash () const uint256 const& getNodeHash () const
{ {
return mNodeHash; return mNodeHash;
@@ -63,7 +56,6 @@ public:
private: private:
SHAMapType mType; SHAMapType mType;
SHAMapNodeID mNodeID;
uint256 mNodeHash; uint256 mNodeHash;
}; };

View File

@@ -134,6 +134,7 @@ std::string SHAMapNodeID::getRawString () const
SHAMapNodeID SHAMapNodeID::getChildNodeID (int m) const SHAMapNodeID SHAMapNodeID::getChildNodeID (int m) const
{ {
assert ((m >= 0) && (m < 16)); assert ((m >= 0) && (m < 16));
assert (mDepth <= 64);
uint256 child (mNodeID); uint256 child (mNodeID);
child.begin ()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4); child.begin ()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4);

View File

@@ -26,46 +26,52 @@ namespace ripple {
static const uint256 uZero; static const uint256 uZero;
void SHAMap::visitLeaves (std::function<void (SHAMapItem::ref item)> function) static void visitLeavesHelper (
std::function <void (SHAMapItem::ref)> const& function,
SHAMapTreeNode& node)
{ {
// Make a snapshot of this map so we don't need to hold // Adapt visitNodes to visitLeaves
// a lock on the map we're visiting if (!node.isInner ())
snapShot (false)->visitLeavesInternal (function); function (node.peekItem ());
} }
void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& function) void SHAMap::visitLeaves (std::function<void (SHAMapItem::ref item)> const& leafFunction)
{ {
visitNodes (std::bind (visitLeavesHelper,
std::cref (leafFunction), std::placeholders::_1));
}
void SHAMap::visitNodes(std::function<void (SHAMapTreeNode&)> const& function)
{
// Visit every node in a SHAMap
assert (root->isValid ()); assert (root->isValid ());
if (!root || root->isEmpty ()) if (!root || root->isEmpty ())
return; return;
if (!root->isInner ()) function (*root);
{
function (root->peekItem ());
return;
}
std::stack<std::tuple<int, SHAMapTreeNode*, SHAMapNodeID>> stack; if (!root->isInner ())
SHAMapTreeNode* node = root.get (); return;
SHAMapNodeID nodeID;
using StackEntry = std::pair <int, SHAMapTreeNode::pointer>;
std::stack <StackEntry, std::vector <StackEntry>> stack;
SHAMapTreeNode::pointer node = root;
int pos = 0; int pos = 0;
while (1) while (1)
{ {
while (pos < 16) while (pos < 16)
{ {
SHAMapNodeID childID = nodeID;
uint256 childHash; uint256 childHash;
if (node->descend (pos, childID, childHash)) if (!node->isEmptyBranch (pos))
{ {
SHAMapTreeNode* child = getNodePointer (childID, childHash); SHAMapTreeNode::pointer child = descendNoStore (node, pos);
function (*child);
if (child->isLeaf ()) if (child->isLeaf ())
{
function (child->peekItem ());
mTNByID.erase (childID); // don't need this leaf anymore
++pos; ++pos;
}
else else
{ {
// If there are no more children, don't push this node // If there are no more children, don't push this node
@@ -75,14 +81,11 @@ void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& fu
if (pos != 15) if (pos != 15)
{ {
// save next position to resume at // save next position to resume at
stack.push (std::make_tuple(pos + 1, node, nodeID)); stack.push (std::make_pair(pos + 1, std::move (node)));
} }
else
mTNByID.erase (nodeID); // don't need this inner node anymore
// descend to the child's first position // descend to the child's first position
node = child; node = child;
nodeID = childID;
pos = 0; pos = 0;
} }
} }
@@ -92,13 +95,10 @@ void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& fu
} }
} }
// We are done with this inner node
mTNByID.erase (nodeID);
if (stack.empty ()) if (stack.empty ())
break; break;
std::tie(pos, node, nodeID) = stack.top (); std::tie(pos, node) = stack.top ();
stack.pop (); stack.pop ();
} }
} }
@@ -110,12 +110,9 @@ void SHAMap::visitLeavesInternal (std::function<void (SHAMapItem::ref item)>& fu
void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max, void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max,
SHAMapSyncFilter* filter) SHAMapSyncFilter* filter)
{ {
ScopedReadLockType sl (mLock);
assert (root->isValid ()); assert (root->isValid ());
assert (root->getNodeHash().isNonZero ()); assert (root->getNodeHash().isNonZero ());
if (root->isFullBelow ()) if (root->isFullBelow ())
{ {
clearSynching (); clearSynching ();
@@ -136,11 +133,12 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
while (1) while (1)
{ {
std::vector <std::pair <SHAMapNodeID, uint256>> deferredReads; std::vector <std::tuple <SHAMapTreeNode*, int, SHAMapNodeID>> deferredReads;
deferredReads.reserve (maxDefer + 16); deferredReads.reserve (maxDefer + 16);
std::stack <std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>> using StackEntry = std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>;
stack; std::stack <StackEntry, std::vector<StackEntry>> stack;
// Traverse the map without blocking // Traverse the map without blocking
SHAMapTreeNode *node = root.get (); SHAMapTreeNode *node = root.get ();
@@ -164,11 +162,11 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
{ {
uint256 const& childHash = node->getChildHash (branch); uint256 const& childHash = node->getChildHash (branch);
if (! m_fullBelowCache.touch_if_exists (childHash)) if (! mBacked || ! m_fullBelowCache.touch_if_exists (childHash))
{ {
SHAMapNodeID childID = nodeID.getChildNodeID (branch); SHAMapNodeID childID = nodeID.getChildNodeID (branch);
bool pending = false; bool pending = false;
SHAMapTreeNode* d = getNodeAsync (childID, childHash, filter, pending); SHAMapTreeNode* d = descendAsync (node, branch, childID, filter, pending);
if (!d) if (!d)
{ {
@@ -186,7 +184,7 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
else else
{ {
// read is deferred // read is deferred
deferredReads.emplace_back (childID, childHash); deferredReads.emplace_back (node, branch, childID);
} }
fullBelow = false; // This node is not known full below fullBelow = false; // This node is not known full below
@@ -212,7 +210,7 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
if (fullBelow) if (fullBelow)
{ // No partial node encountered below this node { // No partial node encountered below this node
node->setFullBelow (); node->setFullBelow ();
if (mType == smtSTATE) if (mBacked)
m_fullBelowCache.insert (node->getNodeHash ()); m_fullBelowCache.insert (node->getNodeHash ());
} }
@@ -238,10 +236,19 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
// Process all deferred reads // Process all deferred reads
for (auto const& node : deferredReads) for (auto const& node : deferredReads)
{ {
auto const& nodeID = node.first; auto parent = std::get<0>(node);
auto const& nodeHash = node.second; auto branch = std::get<1>(node);
SHAMapTreeNode* nodePtr = getNodePointerNT (nodeID, nodeHash, filter); auto const& nodeID = std::get<2>(node);
if (!nodePtr && missingHashes.insert (nodeHash).second) auto const& nodeHash = parent->getChildHash (branch);
SHAMapTreeNode::pointer nodePtr = fetchNodeNT (nodeID, nodeHash, filter);
if (nodePtr)
{
if (mBacked)
canonicalize (nodeHash, nodePtr);
parent->canonicalizeChild (branch, nodePtr);
}
else if (missingHashes.insert (nodeHash).second)
{ {
nodeIDs.push_back (nodeID); nodeIDs.push_back (nodeID);
hashes.push_back (nodeHash); hashes.push_back (nodeHash);
@@ -273,11 +280,19 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
std::list<Blob >& rawNodes, bool fatRoot, bool fatLeaves) std::list<Blob >& rawNodes, bool fatRoot, bool fatLeaves)
{ {
// Gets a node and some of its children // Gets a node and some of its children
ScopedReadLockType sl (mLock);
SHAMapTreeNode* node = getNodePointer(wanted); SHAMapTreeNode* node = root.get ();
if (!node) SHAMapNodeID nodeID;
while (node && node->isInner () && (nodeID.getDepth() < wanted.getDepth()))
{
int branch = nodeID.selectBranch (wanted.getNodeID());
node = descendThrow (node, branch);
nodeID = nodeID.getChildNodeID (branch);
}
if (!node || (nodeID != wanted))
{ {
WriteLog (lsWARNING, SHAMap) << "peer requested node that is not in the map: " << wanted; WriteLog (lsWARNING, SHAMap) << "peer requested node that is not in the map: " << wanted;
throw std::runtime_error ("Peer requested node not in map"); throw std::runtime_error ("Peer requested node not in map");
@@ -300,8 +315,8 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
{ {
Serializer s; Serializer s;
node->addRaw (s, snfWIRE); node->addRaw (s, snfWIRE);
nodeIDs.push_back(wanted); nodeIDs.push_back (wanted);
rawNodes.push_back (s.peekData ()); rawNodes.push_back (std::move (s.peekData ()));
} }
if ((!fatRoot && wanted.isRoot ()) || node->isLeaf ()) // don't get a fat root, can't get a fat leaf if ((!fatRoot && wanted.isRoot ()) || node->isLeaf ()) // don't get a fat root, can't get a fat leaf
@@ -313,23 +328,22 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
count = 0; count = 0;
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
SHAMapNodeID tempNodeID = wanted; if (!node->isEmptyBranch (i))
uint256 nextNodeHash;
if (node->descend (i, tempNodeID, nextNodeHash))
{ {
nextNodeID = tempNodeID; SHAMapNodeID nextNodeID = wanted.getChildNodeID (i);
nextNode = getNodePointer (nextNodeID, nextNodeHash); nextNode = descendThrow (node, i);
++count; ++count;
if (fatLeaves || nextNode->isInner ()) if (fatLeaves || nextNode->isInner ())
{ {
Serializer s; Serializer s;
nextNode->addRaw (s, snfWIRE); nextNode->addRaw (s, snfWIRE);
nodeIDs.push_back (nextNodeID); nodeIDs.push_back (nextNodeID);
rawNodes.push_back (s.peekData ()); rawNodes.push_back (std::move (s.peekData ()));
skipNode = true; // Don't add this node again if we loop skipNode = true; // Don't add this node again if we loop
} }
} }
} }
node = nextNode; node = nextNode;
wanted = nextNodeID; wanted = nextNodeID;
@@ -341,7 +355,6 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted, std::vector<SHAMapNodeID>& nodeIDs
bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format) bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
{ {
ScopedReadLockType sl (mLock);
root->addRaw (s, format); root->addRaw (s, format);
return true; return true;
} }
@@ -349,8 +362,6 @@ bool SHAMap::getRootNode (Serializer& s, SHANodeFormat format)
SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format, SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter* filter) SHAMapSyncFilter* filter)
{ {
ScopedWriteLockType sl (mLock);
// we already have a root node // we already have a root node
if (root->getNodeHash ().isNonZero ()) if (root->getNodeHash ().isNonZero ())
{ {
@@ -360,7 +371,7 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
assert (mSeq >= 1); assert (mSeq >= 1);
SHAMapTreeNode::pointer node = SHAMapTreeNode::pointer node =
std::make_shared<SHAMapTreeNode> (rootNode, mSeq - 1, std::make_shared<SHAMapTreeNode> (rootNode, 0,
format, uZero, false); format, uZero, false);
if (!node) if (!node)
@@ -370,8 +381,10 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
node->dump (SHAMapNodeID ()); node->dump (SHAMapNodeID ());
#endif #endif
if (mBacked)
canonicalize (node->getNodeHash (), node);
root = node; root = node;
mTNByID.replace(SHAMapNodeID{}, root);
if (root->isLeaf()) if (root->isLeaf())
clearSynching (); clearSynching ();
@@ -390,8 +403,6 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format,
SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format, SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SHANodeFormat format,
SHAMapSyncFilter* filter) SHAMapSyncFilter* filter)
{ {
ScopedWriteLockType sl (mLock);
// we already have a root node // we already have a root node
if (root->getNodeHash ().isNonZero ()) if (root->getNodeHash ().isNonZero ())
{ {
@@ -402,14 +413,16 @@ SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SH
assert (mSeq >= 1); assert (mSeq >= 1);
SHAMapTreeNode::pointer node = SHAMapTreeNode::pointer node =
std::make_shared<SHAMapTreeNode> (rootNode, mSeq - 1, std::make_shared<SHAMapTreeNode> (rootNode, 0,
format, uZero, false); format, uZero, false);
if (!node || node->getNodeHash () != hash) if (!node || node->getNodeHash () != hash)
return SHAMapAddNode::invalid (); return SHAMapAddNode::invalid ();
if (mBacked)
canonicalize (hash, node);
root = node; root = node;
mTNByID.replace(SHAMapNodeID{}, root);
if (root->isLeaf()) if (root->isLeaf())
clearSynching (); clearSynching ();
@@ -429,8 +442,6 @@ SHAMapAddNode
SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode, SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
SHAMapSyncFilter* filter) SHAMapSyncFilter* filter)
{ {
ScopedWriteLockType sl (mLock);
// return value: true=okay, false=error // return value: true=okay, false=error
assert (!node.isRoot ()); assert (!node.isRoot ());
@@ -440,18 +451,10 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
return SHAMapAddNode::duplicate (); return SHAMapAddNode::duplicate ();
} }
if (checkCacheNode (node)) // Do we already have this node? SHAMapNodeID iNodeID;
return SHAMapAddNode::duplicate (); SHAMapTreeNode* iNode = root.get ();
SHAMapNodeID iNodeID = node.getParentNodeID(); while (iNode->isInner () && !iNode->isFullBelow () &&
SHAMapTreeNode* iNode = checkCacheNode(iNodeID).get();
if (iNode == nullptr)
{
iNode = root.get ();
iNodeID = SHAMapNodeID{};
}
while (!iNode->isLeaf () && !iNode->isFullBelow () &&
(iNodeID.getDepth () < node.getDepth ())) (iNodeID.getDepth () < node.getDepth ()))
{ {
int branch = iNodeID.selectBranch (node.getNodeID ()); int branch = iNodeID.selectBranch (node.getNodeID ());
@@ -467,12 +470,13 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
uint256 childHash = iNode->getChildHash (branch); uint256 childHash = iNode->getChildHash (branch);
if (m_fullBelowCache.touch_if_exists (childHash)) if (m_fullBelowCache.touch_if_exists (childHash))
return SHAMapAddNode::duplicate (); return SHAMapAddNode::duplicate ();
SHAMapNodeID nextNodeID = iNodeID.getChildNodeID (branch);
SHAMapTreeNode* nextNode = getNodePointerNT(nextNodeID, childHash, SHAMapTreeNode* prevNode = iNode;
filter); std::tie (iNode, iNodeID) = descend (iNode, iNodeID, branch, filter);
if (!nextNode)
if (!iNode)
{ {
if (iNodeID.getDepth () != (node.getDepth () - 1)) if (iNodeID != node)
{ {
// Either this node is broken or we didn't request it (yet) // Either this node is broken or we didn't request it (yet)
WriteLog (lsWARNING, SHAMap) << "unable to hook node " << node; WriteLog (lsWARNING, SHAMap) << "unable to hook node " << node;
@@ -487,22 +491,25 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
std::make_shared<SHAMapTreeNode> (rawNode, 0, snfWIRE, std::make_shared<SHAMapTreeNode> (rawNode, 0, snfWIRE,
uZero, false); uZero, false);
if (childHash != newNode->getNodeHash ()) if (!newNode->isInBounds (iNodeID))
{
WriteLog (lsWARNING, SHAMap) << "Corrupt node received";
return SHAMapAddNode::invalid ();
}
canonicalize (childHash, newNode);
if (!iNode->isInBounds (iNodeID))
{ {
// Map is provably invalid // Map is provably invalid
mState = smsInvalid; mState = smsInvalid;
return SHAMapAddNode::useful (); return SHAMapAddNode::useful ();
} }
if (mTNByID.canonicalize(node, &newNode) && filter) if (childHash != newNode->getNodeHash ())
{
WriteLog (lsWARNING, SHAMap) << "Corrupt node received";
return SHAMapAddNode::invalid ();
}
if (mBacked)
canonicalize (childHash, newNode);
prevNode->canonicalizeChild (branch, newNode);
if (filter)
{ {
Serializer s; Serializer s;
newNode->addRaw (s, snfPREFIX); newNode->addRaw (s, snfPREFIX);
@@ -512,8 +519,6 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
return SHAMapAddNode::useful (); return SHAMapAddNode::useful ();
} }
iNode = nextNode;
iNodeID = nextNodeID;
} }
WriteLog (lsTRACE, SHAMap) << "got node, already had it (late)"; WriteLog (lsTRACE, SHAMap) << "got node, already had it (late)";
@@ -523,41 +528,29 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
bool SHAMap::deepCompare (SHAMap& other) bool SHAMap::deepCompare (SHAMap& other)
{ {
// Intended for debug/test only // Intended for debug/test only
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> stack; std::stack <std::pair <SHAMapTreeNode*, SHAMapTreeNode*> > stack;
ScopedReadLockType sl (mLock);
stack.push ({root, SHAMapNodeID{}}); stack.push ({root.get(), other.root.get()});
while (!stack.empty ()) while (!stack.empty ())
{ {
SHAMapTreeNode::pointer node; SHAMapTreeNode *node, *otherNode;
SHAMapNodeID nodeID; std::tie(node, otherNode) = stack.top ();
std::tie(node, nodeID) = stack.top ();
stack.pop (); stack.pop ();
SHAMapTreeNode::pointer otherNode; if (!node || !otherNode)
if (nodeID.isRoot ())
otherNode = other.root;
else
otherNode = other.getNode (nodeID, node->getNodeHash (), false);
if (!otherNode)
{ {
WriteLog (lsINFO, SHAMap) << "unable to fetch node"; WriteLog (lsINFO, SHAMap) << "unable to fetch node";
return false; return false;
} }
else if (otherNode->getNodeHash () != node->getNodeHash ()) else if (otherNode->getNodeHash () != node->getNodeHash ())
{ {
WriteLog (lsWARNING, SHAMap) << "node hash mismatch " << nodeID; WriteLog (lsWARNING, SHAMap) << "node hash mismatch";
return false; return false;
} }
// WriteLog (lsTRACE) << "Comparing inner nodes " << *node; // WriteLog (lsTRACE) << "Comparing inner nodes " << *node;
if (node->getNodeHash () != otherNode->getNodeHash ())
return false;
if (node->isLeaf ()) if (node->isLeaf ())
{ {
if (!otherNode->isLeaf ()) if (!otherNode->isLeaf ())
@@ -583,16 +576,17 @@ bool SHAMap::deepCompare (SHAMap& other)
} }
else else
{ {
SHAMapNodeID nextNodeID = nodeID; if (otherNode->isEmptyBranch (i))
uint256 nextNodeHash; return false;
if (!node->descend (i, nextNodeID, nextNodeHash))
SHAMapTreeNode *next = descend (node, i);
SHAMapTreeNode *otherNext = other.descend (otherNode, i);
if (!next || !otherNext)
{ {
WriteLog (lsWARNING, SHAMap) << "unable to fetch inner node"; WriteLog (lsWARNING, SHAMap) << "unable to fetch inner node";
return false; return false;
} }
SHAMapTreeNode::pointer next = getNode (nextNodeID, stack.push ({next, otherNext});
nextNodeHash, false);
stack.push ({next, nextNodeID});
} }
} }
} }
@@ -602,49 +596,50 @@ bool SHAMap::deepCompare (SHAMap& other)
} }
/** Does this map have this inner node? /** Does this map have this inner node?
You must hold a read lock to call this function
*/ */
bool bool
SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID, SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID,
uint256 const& targetNodeHash) uint256 const& targetNodeHash)
{ {
SHAMapTreeNode::pointer ptr = mTNByID.retrieve (targetNodeID);
if (ptr)
return ptr->getNodeHash() == targetNodeHash;
SHAMapTreeNode* node = root.get (); SHAMapTreeNode* node = root.get ();
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
uint256 nodeHash;
while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ())) while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ()))
{ {
int branch = nodeID.selectBranch (targetNodeID.getNodeID ()); int branch = nodeID.selectBranch (targetNodeID.getNodeID ());
if (!node->descend (branch, nodeID, nodeHash))
if (node->isEmptyBranch (branch))
return false; return false;
node = getNodePointer (nodeID, nodeHash);
node = descendThrow (node, branch);
nodeID = nodeID.getChildNodeID (branch);
} }
return nodeHash == targetNodeHash; return (node->isInner()) && (node->getNodeHash() == targetNodeHash);
} }
/** Does this map have this leaf node? /** Does this map have this leaf node?
You must hold a read lock to call this function
*/ */
bool SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash) bool SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash)
{ {
SHAMapTreeNode* node = root.get (); SHAMapTreeNode* node = root.get ();
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
uint256 nodeHash;
if (!node->isInner()) // only one leaf node in the tree if (!node->isInner()) // only one leaf node in the tree
return node->getNodeHash() == targetNodeHash; return node->getNodeHash() == targetNodeHash;
do do
{ {
int branch = nodeID.selectBranch (tag); int branch = nodeID.selectBranch (tag);
if (!node->descend (branch, nodeID, nodeHash))
if (node->isEmptyBranch (branch))
return false; // Dead end, node must not be here return false; // Dead end, node must not be here
if (nodeHash == targetNodeHash) // Matching leaf, no need to retrieve it
if (node->getChildHash (branch) == targetNodeHash) // Matching leaf, no need to retrieve it
return true; return true;
node = getNodePointer (nodeID, nodeHash);
node = descendThrow (node, branch);
nodeID = nodeID.getChildNodeID (branch);
} }
while (node->isInner()); while (node->isInner());
@@ -663,18 +658,6 @@ There's no point in including the leaves of transaction trees.
void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max, void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
std::function<void (uint256 const&, const Blob&)> func) std::function<void (uint256 const&, const Blob&)> func)
{ {
ScopedReadLockType ul1 (mLock, boost::defer_lock);
ScopedReadLockType ul2;
if (have)
{
assert(this != have);
ul2 = ScopedReadLockType (have->mLock, boost::defer_lock);
std::lock(ul1, ul2);
}
else
ul1.lock();
if (root->getNodeHash ().isZero ()) if (root->getNodeHash ().isZero ())
return; return;
@@ -695,14 +678,16 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
return; return;
} }
// contains unexplored non-matching inner node entries // contains unexplored non-matching inner node entries
std::stack<std::pair<SHAMapTreeNode*, SHAMapNodeID>> stack; using StackEntry = std::pair <SHAMapTreeNode*, SHAMapNodeID>;
std::stack <StackEntry, std::vector<StackEntry>> stack;
stack.push ({root.get(), SHAMapNodeID{}}); stack.push ({root.get(), SHAMapNodeID{}});
while (!stack.empty() && (max > 0)) while (!stack.empty() && (max > 0))
{ {
SHAMapTreeNode* node; SHAMapTreeNode* node;
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
std::tie(node, nodeID) = stack.top (); std::tie (node, nodeID) = stack.top ();
stack.pop (); stack.pop ();
// 1) Add this node to the pack // 1) Add this node to the pack
@@ -718,8 +703,7 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
{ {
uint256 const& childHash = node->getChildHash (i); uint256 const& childHash = node->getChildHash (i);
SHAMapNodeID childID = nodeID.getChildNodeID (i); SHAMapNodeID childID = nodeID.getChildNodeID (i);
SHAMapTreeNode* next = descendThrow (node, i);
SHAMapTreeNode* next = getNodePointer (childID, childHash);
if (next->isInner ()) if (next->isInner ())
{ {
@@ -738,12 +722,9 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
} }
} }
std::list<Blob > SHAMap::getTrustedPath (uint256 const& index) std::list <Blob> SHAMap::getTrustedPath (uint256 const& index)
{ {
ScopedReadLockType sl (mLock); auto stack = getStack (index, false);
std::stack<std::pair<SHAMapTreeNode::pointer, SHAMapNodeID>> stack =
getStack (index, false);
if (stack.empty () || !stack.top ().first->isLeaf ()) if (stack.empty () || !stack.top ().first->isLeaf ())
throw std::runtime_error ("requested leaf not present"); throw std::runtime_error ("requested leaf not present");
@@ -796,6 +777,7 @@ public:
{ {
log << log <<
"Unable to add item to map"; "Unable to add item to map";
assert (false);
return false; return false;
} }
} }
@@ -806,6 +788,7 @@ public:
{ {
log << log <<
"Unable to remove item from map"; "Unable to remove item from map";
assert (false);
return false; return false;
} }
} }
@@ -813,7 +796,8 @@ public:
if (beforeHash != map.getHash ()) if (beforeHash != map.getHash ())
{ {
log << log <<
"Hashes do not match"; "Hashes do not match " << beforeHash << " " << map.getHash ();
assert (false);
return false; return false;
} }

View File

@@ -21,10 +21,10 @@
namespace ripple { namespace ripple {
std::mutex SHAMapTreeNode::childLock;
SHAMapTreeNode::SHAMapTreeNode (std::uint32_t seq) SHAMapTreeNode::SHAMapTreeNode (std::uint32_t seq)
: mHash (std::uint64_t(0)) : mSeq (seq)
, mSeq (seq)
, mAccessSeq (seq)
, mType (tnERROR) , mType (tnERROR)
, mIsBranch (0) , mIsBranch (0)
, mFullBelow (false) , mFullBelow (false)
@@ -41,7 +41,14 @@ SHAMapTreeNode::SHAMapTreeNode (const SHAMapTreeNode& node, std::uint32_t seq)
if (node.mItem) if (node.mItem)
mItem = node.mItem; mItem = node.mItem;
else else
{
memcpy (mHashes, node.mHashes, sizeof (mHashes)); memcpy (mHashes, node.mHashes, sizeof (mHashes));
std::unique_lock <std::mutex> lock (childLock);
for (int i = 0; i < 16; ++i)
mChildren[i] = node.mChildren[i];
}
} }
SHAMapTreeNode::SHAMapTreeNode (SHAMapItem::ref item, SHAMapTreeNode::SHAMapTreeNode (SHAMapItem::ref item,
@@ -471,11 +478,13 @@ std::string SHAMapTreeNode::getString (const SHAMapNodeID & id) const
return ret; return ret;
} }
bool SHAMapTreeNode::setChildHash (int m, uint256 const& hash) // We are modifying an inner node
bool SHAMapTreeNode::setChild (int m, uint256 const& hash, SHAMapTreeNode::ref child)
{ {
assert ((m >= 0) && (m < 16)); assert ((m >= 0) && (m < 16));
assert (mType == tnINNER); assert (mType == tnINNER);
assert (mSeq != 0); assert (mSeq != 0);
assert (child.get() != this);
if (mHashes[m] == hash) if (mHashes[m] == hash)
return false; return false;
@@ -483,31 +492,72 @@ bool SHAMapTreeNode::setChildHash (int m, uint256 const& hash)
mHashes[m] = hash; mHashes[m] = hash;
if (hash.isNonZero ()) if (hash.isNonZero ())
{
assert (child && (child->getNodeHash() == hash));
mIsBranch |= (1 << m); mIsBranch |= (1 << m);
}
else else
{
assert (!child);
mIsBranch &= ~ (1 << m); mIsBranch &= ~ (1 << m);
}
mChildren[m] = child;
return updateHash (); return updateHash ();
} }
// Descends along the specified branch // finished modifying, now make shareable
// On invocation, nodeID must be the ID of this node void SHAMapTreeNode::shareChild (int m, SHAMapTreeNode::ref child)
// Returns false if there is no node down that branch {
// Otherwise, returns true and fills in the node's ID and hash assert ((m >= 0) && (m < 16));
assert (mType == tnINNER);
assert (mSeq != 0);
assert (child);
assert (child.get() != this);
assert (child->getNodeHash() == mHashes[m]);
bool mChildren[m] = child;
SHAMapTreeNode::descend (int branch, SHAMapNodeID& nodeID, uint256& nodeHash) }
SHAMapTreeNode* SHAMapTreeNode::getChildPointer (int branch)
{ {
assert (branch >= 0 && branch < 16); assert (branch >= 0 && branch < 16);
assert (isInnerNode ()); assert (isInnerNode ());
if (isEmptyBranch (branch)) std::unique_lock <std::mutex> lock (childLock);
return false; return mChildren[branch].get ();
nodeID = nodeID.getChildNodeID (branch);
nodeHash = mHashes [branch];
return true;
} }
SHAMapTreeNode::pointer SHAMapTreeNode::getChild (int branch)
{
assert (branch >= 0 && branch < 16);
assert (isInnerNode ());
std::unique_lock <std::mutex> lock (childLock);
assert (!mChildren[branch] || (mHashes[branch] == mChildren[branch]->getNodeHash()));
return mChildren[branch];
}
void SHAMapTreeNode::canonicalizeChild (int branch, SHAMapTreeNode::pointer& node)
{
assert (branch >= 0 && branch < 16);
assert (isInnerNode ());
assert (node);
assert (node->getNodeHash() == mHashes[branch]);
std::unique_lock <std::mutex> lock (childLock);
if (mChildren[branch])
{
// There is already a node hooked up, return it
node = mChildren[branch];
}
else
{
// Hook this node up
mChildren[branch] = node;
}
}
} // ripple } // ripple

View File

@@ -77,12 +77,7 @@ public:
} }
void setSeq (std::uint32_t s) void setSeq (std::uint32_t s)
{ {
mAccessSeq = mSeq = s; mSeq = s;
}
void touch (std::uint32_t s)
{
if (mSeq != 0)
mAccessSeq = s;
} }
uint256 const& getNodeHash () const uint256 const& getNodeHash () const
{ {
@@ -130,7 +125,13 @@ public:
{ {
return !mItem; return !mItem;
} }
bool setChildHash (int m, uint256 const& hash);
// We are modifying the child hash
bool setChild (int m, uint256 const& hash, std::shared_ptr<SHAMapTreeNode> const& child);
// We are sharing/unsharing the child
void shareChild (int m, std::shared_ptr<SHAMapTreeNode> const& child);
bool isEmptyBranch (int m) const bool isEmptyBranch (int m) const
{ {
return (mIsBranch & (1 << m)) == 0; return (mIsBranch & (1 << m)) == 0;
@@ -178,32 +179,27 @@ public:
virtual void dump (SHAMapNodeID const&); virtual void dump (SHAMapNodeID const&);
virtual std::string getString (SHAMapNodeID const&) const; virtual std::string getString (SHAMapNodeID const&) const;
/** Descend along the specified branch SHAMapTreeNode* getChildPointer (int branch);
On invocation, nodeID must be the ID of this node SHAMapTreeNode::pointer getChild (int branch);
Returns `false` if there is no node down that branch void canonicalizeChild (int branch, SHAMapTreeNode::pointer& node);
Otherwise, returns `true` and fills in the node's ID and hash
@param branch The branch to descend [0, 15]
@param nodeID On entry the ID of the parent. On exit the ID of the child
@param nodeHash On exit the hash of the child node.
@return `true` if nodeID and nodeHash are altered.
*/
bool descend (int branch, SHAMapNodeID& nodeID, uint256& nodeHash);
private: private:
// VFALCO TODO remove the use of friend // VFALCO TODO remove the use of friend
friend class SHAMap; friend class SHAMap;
uint256 mHash; uint256 mHash;
uint256 mHashes[16]; uint256 mHashes[16];
SHAMapItem::pointer mItem; SHAMapTreeNode::pointer mChildren[16];
std::uint32_t mSeq, mAccessSeq; SHAMapItem::pointer mItem;
TNType mType; std::uint32_t mSeq;
int mIsBranch; TNType mType;
bool mFullBelow; int mIsBranch;
bool mFullBelow;
bool updateHash (); bool updateHash ();
static std::mutex childLock;
}; };
using TreeNodeCache = TaggedCache <uint256, SHAMapTreeNode>; using TreeNodeCache = TaggedCache <uint256, SHAMapTreeNode>;

View File

@@ -37,7 +37,7 @@ TransactionAcquire::TransactionAcquire (uint256 const& hash, clock_type& clock)
Application& app = getApp(); Application& app = getApp();
mMap = std::make_shared<SHAMap> (smtTRANSACTION, hash, mMap = std::make_shared<SHAMap> (smtTRANSACTION, hash,
app.getFullBelowCache (), app.getTreeNodeCache()); app.getFullBelowCache (), app.getTreeNodeCache());
mMap->setTXMap (); mMap->setUnbacked ();
} }
TransactionAcquire::~TransactionAcquire () TransactionAcquire::~TransactionAcquire ()