Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9b3dd70
fix: Allocate TaggedCache::getKeys() memory outside of lock
ximinez Jun 17, 2026
eb4681d
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jun 17, 2026
0542847
Apply suggestion from @xrplf-ai-reviewer[bot]
ximinez Jun 17, 2026
d7e7baa
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jun 22, 2026
d62ad9a
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jun 23, 2026
0e8714a
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jun 25, 2026
0ded97b
Make the getKeys() allocation more robust
ximinez Jun 30, 2026
c2e54d1
Use the right type in include/xrpl/basics/TaggedCache.ipp
ximinez Jun 30, 2026
1a3d460
Add missed header
ximinez Jun 30, 2026
4f0738f
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jun 30, 2026
9c03931
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 1, 2026
5d2bc88
Document the reasons for the post-allocation assert
ximinez Jul 1, 2026
956ed0b
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 2, 2026
098ae7e
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 6, 2026
8938d26
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 7, 2026
85c201a
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 7, 2026
78f241d
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 8, 2026
3ecb992
build: Set CMAKE_OSX_DEPLOYMENT_TARGET to 26.0
mathbunnyru Jul 10, 2026
348e716
Document minimum macOS supported version
mathbunnyru Jul 10, 2026
0c5c3d8
Merge remote-tracking branch 'mathbunnyru/set_min_osx_target' into xi…
ximinez Jul 10, 2026
affea3d
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 13, 2026
0f0e2f4
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 14, 2026
e7d335e
Clean up the loop to make it more understandable, add logging
ximinez Jul 14, 2026
2e5897e
Merge branch 'develop' into ximinez/fix-getkeys
ximinez Jul 14, 2026
50a7a8e
Experiment. Do not push
ximinez Jul 15, 2026
e1ca014
Speed up rotation when disconnected, log resurrected fields
ximinez Jul 12, 2026
970b0f3
Include the location from which a SHAMapMissingNode is thrown
ximinez Jul 12, 2026
9bf58e6
Merge remote-tracking branch 'mywork/ximinez/online-delete-gaps' into…
ximinez Jul 12, 2026
223badb
Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into …
ximinez Jul 13, 2026
f7ebf02
Log sequence differences at the start and end of rotation
ximinez Jul 13, 2026
b65e4d9
Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into …
ximinez Jul 15, 2026
f6f2c18
Merge branch 'ximinez/fix-getkeys' into ximinez/online-delete-gaps2
ximinez Jul 15, 2026
56d247d
Start setup to re-store nodes based on ledger index
ximinez Jul 15, 2026
94c7873
Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into …
ximinez Jul 15, 2026
c0cd461
Fix minor merge issues
ximinez Jul 15, 2026
306bfd3
Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into …
ximinez Jul 15, 2026
0efa990
Limit copy-forward to ledgers we're not about to delete, and unknown
ximinez Jul 16, 2026
3be7f1d
Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into …
ximinez Jul 16, 2026
2d41ea0
Merge remote-tracking branch 'XRPLF/ximinez/online-delete-gaps' into …
ximinez Jul 17, 2026
01efe00
Merge branch 'ximinez/online-delete-gaps' into ximinez/online-delete-…
ximinez Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ Our Linux CI tooling is distro-independent and uses a Nix-based environment, so

### macOS

Many `xrpld` engineers use macOS for development.
Many `xrpld` engineers use macOS for development. The minimum supported version
is macOS 26, which is also what our CI builds and tests against. The build
defaults `CMAKE_OSX_DEPLOYMENT_TARGET` accordingly, so you do not need to pass it
yourself.

### Windows

Expand Down
15 changes: 15 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@ if(DEFINED CMAKE_MODULE_PATH)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

# Default the macOS deployment target so it does not have to be passed on the
# command line. Must be set before project() because project() consumes it when
# configuring the compiler and SDK. A user-provided -DCMAKE_OSX_DEPLOYMENT_TARGET
# still takes precedence.
if(
CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin"
AND NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET
)
set(CMAKE_OSX_DEPLOYMENT_TARGET
"26.0"
CACHE STRING
"Minimum macOS deployment version"
)
endif()

project(xrpl)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 23)
Expand Down
5 changes: 3 additions & 2 deletions include/xrpl/basics/TaggedCache.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -623,10 +623,11 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
// Allocate the current size plus a little extra, in case the cache grows while
// allocating. Each time another allocation is needed, the extra also gets bigger until
// it ultimately doubles the size + 1.
constexpr std::size_t baseShift = 5;
constexpr std::size_t baseShift = 63;
auto const bufferOffset = std::min(allocationIterations, std::size_t{baseShift});
auto const bufferShift = baseShift - bufferOffset;
size += (size >> bufferShift) + 1;
auto const buffer = (size >> bufferShift) + 1;
size += buffer;
v.reserve(size);
++allocationIterations;
}
Expand Down
3 changes: 2 additions & 1 deletion include/xrpl/nodestore/DatabaseRotating.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <xrpl/nodestore/Backend.h>
#include <xrpl/nodestore/Database.h>
#include <xrpl/nodestore/Scheduler.h>
#include <xrpl/protocol/Protocol.h>

#include <functional>
#include <memory>
Expand Down Expand Up @@ -53,7 +54,7 @@ class DatabaseRotating : public Database
* dropped.
*/
virtual void
setRotationInFlight(bool inFlight) = 0;
setRotationInFlight(LedgerIndex inFlight) = 0;
};

} // namespace xrpl::NodeStore
18 changes: 12 additions & 6 deletions include/xrpl/nodestore/detail/DatabaseRotatingImp.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <xrpl/nodestore/DatabaseRotating.h>
#include <xrpl/nodestore/NodeObject.h>
#include <xrpl/nodestore/Scheduler.h>
#include <xrpl/protocol/Protocol.h>

#include <atomic>
#include <cstdint>
Expand Down Expand Up @@ -71,20 +72,25 @@ class DatabaseRotatingImp : public DatabaseRotating
sweep() override;

void
setRotationInFlight(bool inFlight) override;
setRotationInFlight(LedgerIndex inFlight) override;

private:
std::shared_ptr<Backend> writableBackend_;
std::shared_ptr<Backend> archiveBackend_;
mutable std::mutex mutex_;

// True between SHAMapStore starting the cache-freshen phase and the
// completion of rotate(). While true, archive hits on ordinary
// (duplicate == false) fetches are copied forward into the writable
// backend; copyForwardCount_ tallies them per rotation for the
// Set to the index of the last rotated ledger between SHAMapStore
// starting the cache-freshen phase and the completion of rotate().
// While non-zero, archive hits on ordinary (duplicate == false)
// fetches are copied forward into the writable backend if they are
// for that ledger or later, since those are the ones we'll keep.
// To be safe, copy forward if the provided ledger index is 0.
// copyForwardCount_ tallies them per rotation for the
// summary line logged at swap.
std::atomic<bool> rotationInFlight_{false};
// copyRejectCount_ tallies the ones that weren't copied.
std::atomic<LedgerIndex> rotationInFlight_{0};
std::atomic<std::uint64_t> copyForwardCount_{0};
std::atomic<std::uint64_t> copyRejectCount_{0};

std::shared_ptr<NodeObject>
fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
Expand Down
10 changes: 6 additions & 4 deletions include/xrpl/shamap/SHAMapMissingNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@
class SHAMapMissingNode : public std::runtime_error
{
public:
SHAMapMissingNode(SHAMapType t, SHAMapHash const& hash)
: std::runtime_error("Missing Node: " + to_string(t) + ": hash " + to_string(hash))
SHAMapMissingNode(SHAMapType t, SHAMapHash const& hash, std::string const& location)
: std::runtime_error(

Check warning on line 39 in include/xrpl/shamap/SHAMapMissingNode.h

View check run for this annotation

Codecov / codecov/patch

include/xrpl/shamap/SHAMapMissingNode.h#L38-L39

Added lines #L38 - L39 were not covered by tests
"Missing Node: " + to_string(t) + ": hash " + to_string(hash) + " in: " + location)
{
}

SHAMapMissingNode(SHAMapType t, uint256 const& id)
: std::runtime_error("Missing Node: " + to_string(t) + ": id " + to_string(id))
SHAMapMissingNode(SHAMapType t, uint256 const& id, std::string const& location)
: std::runtime_error(

Check warning on line 45 in include/xrpl/shamap/SHAMapMissingNode.h

View check run for this annotation

Codecov / codecov/patch

include/xrpl/shamap/SHAMapMissingNode.h#L44-L45

Added lines #L44 - L45 were not covered by tests
"Missing Node: " + to_string(t) + ": id " + to_string(id) + " in: " + location)
{
}
};
Expand Down
6 changes: 4 additions & 2 deletions src/libxrpl/ledger/Ledger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,8 @@
if (stateMap_.getHash().isZero() && !header_.accountHash.isZero() &&
!stateMap_.fetchRoot(SHAMapHash{header_.accountHash}, nullptr))
{
missingNodes1.emplace_back(SHAMapType::STATE, SHAMapHash{header_.accountHash});
missingNodes1.emplace_back(

Check warning on line 749 in src/libxrpl/ledger/Ledger.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/ledger/Ledger.cpp#L749

Added line #L749 was not covered by tests
SHAMapType::STATE, SHAMapHash{header_.accountHash}, "Ledger::walkLedger");
}
else
{
Expand All @@ -770,7 +771,8 @@
if (txMap_.getHash().isZero() && header_.txHash.isNonZero() &&
!txMap_.fetchRoot(SHAMapHash{header_.txHash}, nullptr))
{
missingNodes2.emplace_back(SHAMapType::TRANSACTION, SHAMapHash{header_.txHash});
missingNodes2.emplace_back(

Check warning on line 774 in src/libxrpl/ledger/Ledger.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/ledger/Ledger.cpp#L774

Added line #L774 was not covered by tests
SHAMapType::TRANSACTION, SHAMapHash{header_.txHash}, "Ledger::walkLedger");
}
else
{
Expand Down
38 changes: 26 additions & 12 deletions src/libxrpl/nodestore/DatabaseRotatingImp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <xrpl/nodestore/NodeObject.h>
#include <xrpl/nodestore/Scheduler.h>
#include <xrpl/nodestore/Types.h>
#include <xrpl/protocol/Protocol.h>

#include <atomic>
#include <cstdint>
Expand Down Expand Up @@ -54,6 +55,7 @@
// deleted.
std::shared_ptr<NodeStore::Backend> oldArchiveBackend;
std::uint64_t copyForwards = 0;
std::uint64_t copyRejects = 0;
{
std::scoped_lock const lock(mutex_);

Expand All @@ -66,24 +68,25 @@
writableBackend_ = std::move(newBackend);

copyForwards = copyForwardCount_.exchange(0, std::memory_order_relaxed);
copyRejects = copyRejectCount_.exchange(0, std::memory_order_relaxed);
}

if (copyForwards > 0)
{
JLOG(j_.warn()) << "Rotating: copied forward " << copyForwards
<< " archive-served reads into the writable backend "
"during the rotation window";
"during the rotation window. Rejected "
<< copyRejects;
}

f(newWritableBackendName, newArchiveBackendName);
}

void
DatabaseRotatingImp::setRotationInFlight(bool inFlight)
DatabaseRotatingImp::setRotationInFlight(LedgerIndex inFlight)
{
rotationInFlight_.store(inFlight, std::memory_order_release);
JLOG(j_.debug()) << "Rotating: copy-forward on archive reads "
<< (inFlight ? "enabled" : "disabled");
JLOG(j_.debug()) << "Rotating: copy-forward on archive reads from " << inFlight << " forward";
}

std::string
Expand Down Expand Up @@ -141,7 +144,7 @@
std::shared_ptr<NodeObject>
DatabaseRotatingImp::fetchNodeObject(
uint256 const& hash,
std::uint32_t,
std::uint32_t ledgerSeq,
FetchReport& fetchReport,
bool duplicate)
{
Expand Down Expand Up @@ -190,24 +193,35 @@
nodeObject = fetch(archive);
if (nodeObject)
{
{
// Refresh the writable backend pointer
std::scoped_lock const lock(mutex_);
writable = writableBackend_;
}

// 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))
auto const inFlight = rotationInFlight_.load(std::memory_order_acquire);
if (duplicate || (inFlight != 0 && (ledgerSeq == 0 || ledgerSeq >= inFlight)))
{
{
// Refresh the writable backend pointer
std::scoped_lock const lock(mutex_);
writable = writableBackend_;
}

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);
}
else if (inFlight != 0)
{
JLOG(j_.warn()) << "Rotating: DO NOT copy node for ledger " << ledgerSeq
<< " from archive to writable backend: " << hash;
copyRejectCount_.fetch_add(1, std::memory_order_relaxed);

Check warning on line 223 in src/libxrpl/nodestore/DatabaseRotatingImp.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/nodestore/DatabaseRotatingImp.cpp#L223

Added line #L223 was not covered by tests
}
}
}

Expand Down
18 changes: 9 additions & 9 deletions src/libxrpl/shamap/SHAMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@
auto node = fetchNodeNT(hash);

if (!node)
Throw<SHAMapMissingNode>(type_, hash);
Throw<SHAMapMissingNode>(type_, hash, "fetchNode");

Check warning on line 275 in src/libxrpl/shamap/SHAMap.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/shamap/SHAMap.cpp#L275

Added line #L275 was not covered by tests

return node;
}
Expand All @@ -283,7 +283,7 @@
SHAMapTreeNode* ret = descend(parent, branch); // NOLINT(misc-const-correctness)

if ((ret == nullptr) && !parent->isEmptyBranch(branch))
Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch));
Throw<SHAMapMissingNode>(type_, parent->getChildHash(branch), "descendThrow");

Check warning on line 286 in src/libxrpl/shamap/SHAMap.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/shamap/SHAMap.cpp#L286

Added line #L286 was not covered by tests

return ret;
}
Expand All @@ -294,7 +294,7 @@
SHAMapTreeNodePtr ret = descend(parent, branch);

if (!ret && !parent.isEmptyBranch(branch))
Throw<SHAMapMissingNode>(type_, parent.getChildHash(branch));
Throw<SHAMapMissingNode>(type_, parent.getChildHash(branch), "descendThrow");

return ret;
}
Expand Down Expand Up @@ -566,7 +566,7 @@
node = descendThrow(*inner, i);
auto leaf = firstBelow(node, stack, i);
if (leaf == nullptr)
Throw<SHAMapMissingNode>(type_, id);
Throw<SHAMapMissingNode>(type_, id, "peekNextItem");

Check warning on line 569 in src/libxrpl/shamap/SHAMap.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/shamap/SHAMap.cpp#L569

Added line #L569 was not covered by tests
XRPL_ASSERT(leaf->isLeaf(), "xrpl::SHAMap::peekNextItem : leaf is valid");
return leaf;
}
Expand Down Expand Up @@ -624,7 +624,7 @@
node = descendThrow(*inner, branch);
auto leaf = firstBelow(node, stack, branch);
if (leaf == nullptr)
Throw<SHAMapMissingNode>(type_, id);
Throw<SHAMapMissingNode>(type_, id, "upperBound");

Check warning on line 627 in src/libxrpl/shamap/SHAMap.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/shamap/SHAMap.cpp#L627

Added line #L627 was not covered by tests
return ConstIterator(this, leaf->peekItem().get(), std::move(stack));
}
}
Expand Down Expand Up @@ -657,7 +657,7 @@
node = descendThrow(*inner, branch);
auto leaf = lastBelow(node, stack, branch);
if (leaf == nullptr)
Throw<SHAMapMissingNode>(type_, id);
Throw<SHAMapMissingNode>(type_, id, "lowerBound");

Check warning on line 660 in src/libxrpl/shamap/SHAMap.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/shamap/SHAMap.cpp#L660

Added line #L660 was not covered by tests
return ConstIterator(this, leaf->peekItem().get(), std::move(stack));
}
}
Expand All @@ -684,7 +684,7 @@
walkTowardsKey(id, &stack);

if (stack.empty())
Throw<SHAMapMissingNode>(type_, id);
Throw<SHAMapMissingNode>(type_, id, "delItem");

Check warning on line 687 in src/libxrpl/shamap/SHAMap.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/shamap/SHAMap.cpp#L687

Added line #L687 was not covered by tests

auto leaf = intr_ptr::dynamicPointerCast<SHAMapLeafNode>(stack.top().first);
stack.pop();
Expand Down Expand Up @@ -770,7 +770,7 @@
walkTowardsKey(tag, &stack);

if (stack.empty())
Throw<SHAMapMissingNode>(type_, tag);
Throw<SHAMapMissingNode>(type_, tag, "addGiveItem");

Check warning on line 773 in src/libxrpl/shamap/SHAMap.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/shamap/SHAMap.cpp#L773

Added line #L773 was not covered by tests

auto [node, nodeID] = stack.top();
stack.pop();
Expand Down Expand Up @@ -857,7 +857,7 @@
walkTowardsKey(tag, &stack);

if (stack.empty())
Throw<SHAMapMissingNode>(type_, tag);
Throw<SHAMapMissingNode>(type_, tag, "updateGiveItem");

Check warning on line 860 in src/libxrpl/shamap/SHAMap.cpp

View check run for this annotation

Codecov / codecov/patch

src/libxrpl/shamap/SHAMap.cpp#L860

Added line #L860 was not covered by tests

auto node = intr_ptr::dynamicPointerCast<SHAMapLeafNode>(stack.top().first);
auto nodeID = stack.top().second;
Expand Down
7 changes: 4 additions & 3 deletions src/libxrpl/shamap/SHAMapDelta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::SHAMap::compare : missing a node");
Throw<SHAMapMissingNode>(type_, uint256());
Throw<SHAMapMissingNode>(type_, uint256(), "compare");
// LCOV_EXCL_STOP
}

Expand Down Expand Up @@ -270,7 +270,7 @@ SHAMap::walkMap(std::vector<SHAMapMissingNode>& missingNodes, int maxMissing) co
}
else
{
missingNodes.emplace_back(type_, node->getChildHash(i));
missingNodes.emplace_back(type_, node->getChildHash(i), "walkMap");
if (--maxMissing <= 0)
return;
}
Expand Down Expand Up @@ -344,7 +344,8 @@ SHAMap::walkMapParallel(std::vector<SHAMapMissingNode>& missingNodes, int maxMis
else
{
std::scoped_lock const l{m};
missingNodes.emplace_back(type_, node->getChildHash(i));
missingNodes.emplace_back(
type_, node->getChildHash(i), "walkMapParallel");
if (--maxMissing <= 0)
return;
}
Expand Down
Loading
Loading