Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into ximinez/online-delete-gaps2

* XRPLF/ximinez/online-delete-gaps:
  fix: Re-store nodes missing from both backends during online_delete rotation (7763)
This commit is contained in:
Ed Hennis
2026-07-14 21:35:04 -04:00
3 changed files with 27 additions and 15 deletions

View File

@@ -42,15 +42,16 @@ public:
std::function<void(std::string const& writableName, std::string const& archiveName)> const&
f) = 0;
/** Marks an online-delete rotation as in progress (or completed).
While in flight, a read served by the archive backend is copied
forward into the writable backend even for ordinary
(duplicate == false) fetches: the archive is about to be deleted,
and a node body canonicalized into caches during the rotation
window would otherwise survive only in RAM once the archive is
dropped.
*/
/**
* Marks an online-delete rotation as in progress (or completed).
*
* While in flight, a read served by the archive backend is copied
* forward into the writable backend even for ordinary
* (duplicate == false) fetches: the archive is about to be deleted,
* and a node body canonicalized into caches during the rotation
* window would otherwise survive only in RAM once the archive is
* dropped.
*/
virtual void
setRotationInFlight(bool inFlight) = 0;
};

View File

@@ -13,6 +13,7 @@
#include <xrpl/nodestore/Scheduler.h>
#include <xrpl/nodestore/Types.h>
#include <atomic>
#include <cstdint>
#include <exception>
#include <functional>
@@ -64,7 +65,7 @@ DatabaseRotatingImp::rotate(
writableBackend_ = std::move(newBackend);
copyForwards = copyForwardCount_.exchange(0, std::memory_order_acq_rel);
copyForwards = copyForwardCount_.exchange(0, std::memory_order_relaxed);
}
if (copyForwards > 0)
@@ -203,13 +204,22 @@ DatabaseRotatingImp::fetchNodeObject(
writable = writableBackend_;
}
if (!duplicate)
// Update writable backend with data from the archive backend.
// While a rotation is in flight, ordinary (duplicate == false)
// reads served by the archive are copied forward too: the
// archive is about to be deleted, and a body canonicalized
// into the cache after the freshen getKeys() snapshot would
// otherwise survive only in RAM once the archive is dropped.
if (duplicate || rotationInFlight_.load(std::memory_order_acquire))
{
JLOG(j_.warn()) << "Rotating: copy node for ledger " << ledgerSeq
<< " from archive to writable backend: " << hash;
++copyForwardCount_;
if (!duplicate)
{
JLOG(j_.warn()) << "Rotating: copy node for ledger " << ledgerSeq
<< " from archive to writable backend: " << hash;
copyForwardCount_.fetch_add(1, std::memory_order_relaxed);
}
writable->store(nodeObject);
}
writable->store(nodeObject);
}
}
}

View File

@@ -270,6 +270,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