From f1f48314ea45836b5e87a5b2d775afa6b27fb4bb Mon Sep 17 00:00:00 2001 From: Nicholas Dudfield Date: Tue, 14 Apr 2026 17:09:26 +0700 Subject: [PATCH] fix: drop sticky TRACKING gate on memory-resident retirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate was defensive against fetchForHistory re-inserting historical seqs into mCompleteLedgers and fighting the retire-prune. Now that fetchForHistory is !memoryResidentMode-gated in doAdvance, there's nothing to fight. With the gate in place, a fresh process starts pre-TRACKING and retirement never fires until the first TRACKING observation — so mCompleteLedgers grows unboundedly across catch-up even though mRetainedLedgers is already capped at ledger_history. Drop the gate so the bookkeeping tracks the structural retention from publish zero. --- src/xrpld/app/ledger/detail/LedgerMaster.cpp | 33 ++++++-------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/xrpld/app/ledger/detail/LedgerMaster.cpp b/src/xrpld/app/ledger/detail/LedgerMaster.cpp index 13d5a8858..85cacf948 100644 --- a/src/xrpld/app/ledger/detail/LedgerMaster.cpp +++ b/src/xrpld/app/ledger/detail/LedgerMaster.cpp @@ -911,29 +911,16 @@ LedgerMaster::setFullLedger( } } - // Memory-resident retirement is gated on a STICKY "at least TRACKING" - // observation. OperatingMode values are ordered by how-caught-up we - // are: - // - // DISCONNECTED=0, CONNECTED=1, SYNCING=2, TRACKING=3, FULL=4 - // - // TRACKING ("convinced we agree with the network") is the right - // threshold — it means we're in step with the network's current - // ledger. FULL additionally requires validator participation, so a - // tracking-only node would never reach it; using FULL as the gate - // would leave retirement disabled forever on non-validator nodes, - // letting mCompleteLedgers grow unbounded. - // - // Once we've ever observed >= TRACKING, retirement stays enabled for - // the rest of the process's lifetime — transient drops (network - // hiccups, brief consensus stalls) don't cause mCompleteLedgers to - // drift. Static atomic is process-wide; xahaud runs a single - // Application per process so effectively per-instance. - static std::atomic sawCaughtUp{false}; - if (app_.getOPs().getOperatingMode() >= OperatingMode::TRACKING) - sawCaughtUp.store(true, std::memory_order_release); - bool const shouldRetire = app_.getSHAMapStore().memoryResidentMode() && - sawCaughtUp.load(std::memory_order_acquire); + // In memory-resident mode we retire every time a Ledger falls off + // mRetainedLedgers. No OperatingMode gate: mRetainedLedgers already + // caps at ledger_history_ on every publish regardless of + // DISCONNECTED/SYNCING/TRACKING/FULL, so mCompleteLedgers should + // track it step-for-step. The earlier sticky-TRACKING gate was + // defensive against fetchForHistory re-inserting historical seqs + // and fighting the prune, but fetchForHistory is now + // !memoryResidentMode-gated in doAdvance, so there's nothing left + // to fight. + bool const shouldRetire = app_.getSHAMapStore().memoryResidentMode(); // The mCompleteLedgers insert of the new seq AND the bulk-prefix prune // of retired seqs both run under one mCompleteLock acquisition. This