Set the "rotation in flight" index right at the beginning of the rotation

- Because the node copy process can take a long time, other ledgers may
  get validated. Any reads for those ledgers have the potential to be
  served by the archive DB and thus lost, too. Why wait?
- Also added an assertion suggested on @vlntb in #7763.
This commit is contained in:
Ed Hennis
2026-07-22 19:15:55 -04:00
parent 3dbcec5777
commit 897b42354c
4 changed files with 39 additions and 26 deletions

View File

@@ -46,15 +46,15 @@ public:
/**
* 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.
* While in flight, a read for ledgers after the inFlight value 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(LedgerIndex inFlight) = 0;
virtual LedgerIndex
getRotationInFlight() const = 0;
};
} // namespace xrpl::NodeStore

View File

@@ -73,6 +73,8 @@ public:
void
setRotationInFlight(LedgerIndex inFlight) override;
LedgerIndex
getRotationInFlight() const override;
private:
std::shared_ptr<Backend> writableBackend_;

View File

@@ -89,6 +89,12 @@ DatabaseRotatingImp::setRotationInFlight(LedgerIndex inFlight)
JLOG(j_.debug()) << "Rotating: copy-forward on archive reads from " << inFlight << " forward";
}
LedgerIndex
DatabaseRotatingImp::getRotationInFlight() const
{
return rotationInFlight_.load(std::memory_order_acquire);
}
std::string
DatabaseRotatingImp::getName() const
{
@@ -199,7 +205,7 @@ DatabaseRotatingImp::fetchNodeObject(
// 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.
auto const inFlight = rotationInFlight_.load(std::memory_order_acquire);
auto const inFlight = getRotationInFlight();
if (duplicate || (inFlight != 0 && (ledgerSeq == 0 || ledgerSeq >= inFlight)))
{
{

View File

@@ -313,6 +313,11 @@ SHAMapStoreImp::run()
while (true)
{
XRPL_ASSERT(
dbRotating_->getRotationInFlight() == 0,
"SHAMapStoreImp::run : rotationInFlight_ must be zero "
"outside rotation window");
healthy_ = true;
std::shared_ptr<Ledger const> validatedLedger;
@@ -366,6 +371,25 @@ SHAMapStoreImp::run()
<< ledgerMaster_->getValidatedLedgerAge().count()
<< "s. Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
// Close the getKeys()->swap exposure window: from here until
// rotate() completes, an ordinary read for new ledgers served by the archive is
// copied forward into the writable backend, so a node fetched
// from the doomed archive cannot be left RAM-only when the
// archive is deleted. RAII so the early returns below (and any
// exception) also clear the flag.
struct RotationExposureGuard
{
NodeStore::DatabaseRotating& db;
~RotationExposureGuard()
{
db.setRotationInFlight(0);
}
};
RotationExposureGuard const rotationExposureGuard{*dbRotating_};
// Anything before lastRotated is going to get deleted soon, so we don't care about
// moving it to the writable DB.
dbRotating_->setRotationInFlight(lastRotated);
clearPrior(lastRotated);
if (healthWait() == HealthResult::Stopping)
return;
@@ -393,25 +417,6 @@ SHAMapStoreImp::run()
JLOG(journal_.debug())
<< "copied ledger " << validatedSeq << " nodecount " << nodeCount;
// Close the getKeys()->swap exposure window: from here until
// rotate() completes, an ordinary read served by the archive is
// copied forward into the writable backend, so a node fetched
// from the doomed archive cannot be left RAM-only when the
// archive is deleted. RAII so the early returns below (and any
// exception) also clear the flag.
struct RotationExposureGuard
{
NodeStore::DatabaseRotating& db;
~RotationExposureGuard()
{
db.setRotationInFlight(0);
}
};
RotationExposureGuard const rotationExposureGuard{*dbRotating_};
// Anything before lastRotated is going to get deleted soon, so we don't care about
// moving it to the writable DB.
dbRotating_->setRotationInFlight(lastRotated);
JLOG(journal_.debug()) << "freshening caches";
freshenCaches();
if (healthWait() == HealthResult::Stopping)