Rescue nodes from the tree node cache, too

This commit is contained in:
Ed Hennis
2026-07-22 19:55:17 -04:00
parent 4de913a477
commit e3e4b85eb6
2 changed files with 57 additions and 21 deletions

View File

@@ -262,6 +262,45 @@ SHAMapStoreImp::fdRequired() const
return fdRequired_;
}
void
SHAMapStoreImp::rescueNode(SHAMapTreeNode const& node)
{
XRPL_ASSERT(node.cowid() == 0, "SHAMapStoreImp::copyNode : rescued node must be clean");
// Reachable from the validated state map in memory, but present in
// neither backend: its only on-disk copy lived in a backend removed by
// an earlier rotation, and it was never rewritten because it is clean
// (cowid == 0, so flushDirty skips it). Persist the in-memory body
// directly into the writable backend so it survives this rotation
// instead of later surfacing as an unresolvable SHAMapMissingNode.
auto const nodeType = node.getType();
auto const objectType = std::invoke([nodeType] {
switch (nodeType)
{
case SHAMapNodeType::TnAccountState:
return NodeObjectType::AccountNode;
case SHAMapNodeType::TnTransactionNm:
return NodeObjectType::TransactionNode;
default:
return NodeObjectType::Unknown;
}
});
auto const hash = node.getHash().asUInt256();
if (objectType == NodeObjectType::Unknown)
{
JLOG(journal_.warn()) << "copyNode: unable to re-store node with unknown type, hash="
<< hash << " type=" << static_cast<int>(nodeType);
return;
}
Serializer s;
node.serializeWithPrefix(s);
dbRotating_->store(objectType, std::move(s.modData()), hash, 0);
JLOG(journal_.warn()) << "copyNode: re-stored node missing from both backends, hash=" << hash
<< " type=" << static_cast<int>(nodeType);
}
bool
SHAMapStoreImp::copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node)
{
@@ -270,24 +309,7 @@ SHAMapStoreImp::copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node)
node.getHash().asUInt256(), 0, NodeStore::FetchType::Synchronous, true);
if (!obj)
{
XRPL_ASSERT(node.cowid() == 0, "SHAMapStoreImp::copyNode : rescued node must be clean");
// Reachable from the validated state map in memory, but present in
// neither backend: its only on-disk copy lived in a backend removed by
// an earlier rotation, and it was never rewritten because it is clean
// (cowid == 0, so flushDirty skips it). Persist the in-memory body
// directly into the writable backend so it survives this rotation
// instead of later surfacing as an unresolvable SHAMapMissingNode.
auto const hash = node.getHash().asUInt256();
Serializer s;
node.serializeWithPrefix(s);
dbRotating_->store(NodeObjectType::AccountNode, std::move(s.modData()), hash, 0);
{
auto const& cached = treeNodeCache_->fetch(hash);
JLOG(journal_.warn()) << "copyNode: re-stored node missing from both backends, hash="
<< hash << " type=" << static_cast<int>(node.getType())
<< ". Node is " << (cached ? "" : "not ")
<< "in the tree node cache";
}
rescueNode(node);
}
if ((++nodeCount % checkHealthInterval_) == 0u)
{
@@ -648,8 +670,7 @@ SHAMapStoreImp::freshenCaches()
{
if (freshenCache(*treeNodeCache_))
return;
if (freshenCache(app_.getMasterTransaction().getCache()))
return;
freshenCache(app_.getMasterTransaction().getCache());
}
void

View File

@@ -21,6 +21,7 @@
#include <algorithm>
#include <atomic>
#include <chrono>
#include <concepts>
#include <condition_variable>
#include <cstdint>
#include <functional>
@@ -177,6 +178,9 @@ public:
minimumOnline() const override;
private:
// Force write a node to the writable backend during rotation so it doesn't get lost
void
rescueNode(SHAMapTreeNode const& node);
// callback for visitNodes
bool
copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node);
@@ -196,7 +200,18 @@ private:
for (auto const& key : cache.getKeys())
{
dbRotating_->fetchNodeObject(key, 0, NodeStore::FetchType::Synchronous, true);
[[maybe_unused]]
auto const obj =
dbRotating_->fetchNodeObject(key, 0, NodeStore::FetchType::Synchronous, true);
if constexpr (std::derived_from<typename CacheInstance::mapped_type, SHAMapTreeNode>)
{
if (!obj)
{
auto const node = cache.fetch(key);
if (node)
rescueNode(*node);
}
}
if (!(++check % checkHealthInterval_) && healthWait() == HealthResult::Stopping)
return true;
}