mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-28 07:48:01 +00:00
test(app): assert online-delete keeps retained ledgers resolvable
Adds SHAMapStore_test coverage for the freshen guarantee: after two online-delete rotations delete the first archive, a retained validated ledger must still resolve from the writable backend. The test mutates account state across ledgers, drives two rotations, clears the tree-node and ledger caches so the check reads the backend, then reloads a retained ledger and asserts walkLedger finds no missing node. Catches a regression where the rotation's copy-forward/freshen fails to rewrite an archive-only node into the writable backend: the reload would return null or the walk would report a missing node.
This commit is contained in:
@@ -1035,6 +1035,78 @@ public:
|
||||
BEAST_EXPECT(lastRotated != store.getLastRotated());
|
||||
}
|
||||
|
||||
void
|
||||
testFreshenKeepsLedgersResolvable()
|
||||
{
|
||||
// After a rotation deletes the archived shard, every node of a
|
||||
// retained validated ledger must still be reachable from the writable
|
||||
// backend. run() copies the validated state map forward and then
|
||||
// freshens the caches so a node whose only on-disk copy was in the
|
||||
// doomed archive is rewritten into the writable backend first. Were
|
||||
// that copy-forward dropped, reloading a retained ledger from disk once
|
||||
// the archive is gone would hit a missing node.
|
||||
//
|
||||
// Drive two rotations (so the first archive is actually deleted),
|
||||
// clear the in-memory caches so the check must read the backend, then
|
||||
// reload a retained ledger and assert it resolves with no missing node.
|
||||
testcase("freshen keeps retained ledgers resolvable");
|
||||
using namespace jtx;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
Env env(*this, envconfig(onlineDelete));
|
||||
auto& store = env.app().getSHAMapStore();
|
||||
auto& lm = env.app().getLedgerMaster();
|
||||
|
||||
// Real account state, so the state map carries inner nodes that the
|
||||
// rotation must copy forward rather than an almost-empty tree.
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env.close();
|
||||
|
||||
auto ledgerSeq = waitForReady(env);
|
||||
auto lastRotated = store.getLastRotated();
|
||||
|
||||
// Two rotations. A payment each ledger mutates the state map so
|
||||
// successive rotations copy forward genuinely different nodes, while
|
||||
// alice's and bob's account roots persist unchanged across the archive
|
||||
// boundary -- exactly the nodes the copy-forward must preserve.
|
||||
for (int rotation = 0; rotation < 2; ++rotation)
|
||||
{
|
||||
for (auto const target = store.getLastRotated() + kDeleteInterval + 1;
|
||||
ledgerSeq < target;
|
||||
++ledgerSeq)
|
||||
{
|
||||
env(pay(alice, bob, XRP(1)));
|
||||
env(pay(bob, alice, XRP(1)));
|
||||
env.close();
|
||||
|
||||
auto ledger = env.rpc("ledger", "validated");
|
||||
BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
|
||||
}
|
||||
|
||||
BEAST_EXPECT(syncStore(env));
|
||||
BEAST_EXPECT(store.getLastRotated() != lastRotated);
|
||||
lastRotated = store.getLastRotated();
|
||||
}
|
||||
|
||||
// A validated ledger the store still retains after both rotations.
|
||||
auto const retainedSeq = lastRotated;
|
||||
|
||||
// Force the walk onto the backend: drop the tree-node cache and the
|
||||
// cached ledger objects, so getLedgerBySeq must rebuild the ledger from
|
||||
// the store. A node that was not copied forward is now only in the
|
||||
// deleted archive, so the rebuild or the walk would fail.
|
||||
env.app().getNodeFamily().getTreeNodeCache()->clear();
|
||||
lm.clearLedgerCachePrior(retainedSeq + 1);
|
||||
|
||||
auto const retained = lm.getLedgerBySeq(retainedSeq);
|
||||
if (BEAST_EXPECT(retained != nullptr))
|
||||
{
|
||||
BEAST_EXPECT(retained->walkLedger(env.app().getJournal("SHAMapStoreTest"), false));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCanDelete()
|
||||
{
|
||||
@@ -1925,6 +1997,7 @@ public:
|
||||
testConfig();
|
||||
testClear();
|
||||
testAutomatic();
|
||||
testFreshenKeepsLedgersResolvable();
|
||||
testCanDelete();
|
||||
testRotate();
|
||||
testLedgerGaps();
|
||||
|
||||
Reference in New Issue
Block a user