Skip to content

Commit e8e2895

Browse files
committed
rabbit hole: refactor dirAdd to find gaps in "full" directories.
- This would potentially be very expensive to implement, so don't. - However, it might be a good start for a ledger fix option.
1 parent d22a505 commit e8e2895

File tree

2 files changed

+134
-50
lines changed

2 files changed

+134
-50
lines changed

include/xrpl/protocol/detail/features.macro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
// in include/xrpl/protocol/Feature.h.
3434

3535
// Check flags in Credential transactions
36+
XRPL_FEATURE(DefragDirectories, Supported::no, VoteBehavior::DefaultNo)
3637
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo)
3738
XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo)
3839
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo)

src/xrpld/ledger/detail/ApplyView.cpp

Lines changed: 133 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -25,46 +25,59 @@
2525

2626
namespace ripple {
2727

28+
struct Gap
29+
{
30+
uint64_t const page;
31+
SLE::pointer node;
32+
uint64_t const nextPage;
33+
SLE::pointer next;
34+
};
35+
2836
std::optional<std::uint64_t>
2937
ApplyView::dirAdd(
3038
bool preserveOrder,
3139
Keylet const& directory,
3240
uint256 const& key,
3341
std::function<void(std::shared_ptr<SLE> const&)> const& describe)
3442
{
35-
auto root = peek(directory);
43+
auto createRoot =
44+
[this](
45+
Keylet const& directory,
46+
uint256 const& key,
47+
std::function<void(std::shared_ptr<SLE> const&)> const& describe) {
48+
auto newRoot = std::make_shared<SLE>(directory);
49+
newRoot->setFieldH256(sfRootIndex, directory.key);
50+
describe(newRoot);
3651

37-
if (!root)
38-
{
39-
// No root, make it.
40-
root = std::make_shared<SLE>(directory);
41-
root->setFieldH256(sfRootIndex, directory.key);
42-
describe(root);
52+
STVector256 v;
53+
v.push_back(key);
54+
newRoot->setFieldV256(sfIndexes, v);
4355

44-
STVector256 v;
45-
v.push_back(key);
46-
root->setFieldV256(sfIndexes, v);
56+
insert(newRoot);
57+
return std::uint64_t{0};
58+
};
4759

48-
insert(root);
49-
return std::uint64_t{0};
50-
}
60+
auto findPreviousPage = [this](Keylet const& directory, SLE::ref start) {
61+
std::uint64_t page = start->getFieldU64(sfIndexPrevious);
5162

52-
std::uint64_t page = root->getFieldU64(sfIndexPrevious);
63+
auto node = start;
5364

54-
auto node = root;
55-
56-
if (page)
57-
{
58-
node = peek(keylet::page(directory, page));
59-
if (!node)
60-
LogicError("Directory chain: root back-pointer broken.");
61-
}
62-
63-
auto indexes = node->getFieldV256(sfIndexes);
65+
if (page)
66+
{
67+
node = peek(keylet::page(directory, page));
68+
if (!node)
69+
LogicError("Directory chain: root back-pointer broken.");
70+
}
6471

65-
// If there's space, we use it:
66-
if (indexes.size() < dirNodeMaxEntries)
67-
{
72+
auto indexes = node->getFieldV256(sfIndexes);
73+
return std::make_tuple(page, node, indexes);
74+
};
75+
auto insertKey = [this](
76+
SLE::ref node,
77+
std::uint64_t page,
78+
bool preserveOrder,
79+
STVector256& indexes,
80+
uint256 const& key) {
6881
if (preserveOrder)
6982
{
7083
if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
@@ -90,36 +103,106 @@ ApplyView::dirAdd(
90103
node->setFieldV256(sfIndexes, indexes);
91104
update(node);
92105
return page;
93-
}
106+
};
107+
auto insertPage =
108+
[this](
109+
std::uint64_t page,
110+
SLE::pointer node,
111+
std::uint64_t nextPage,
112+
SLE::ref next,
113+
uint256 const& key,
114+
STVector256& indexes,
115+
Keylet const& directory,
116+
std::function<void(std::shared_ptr<SLE> const&)> const& describe)
117+
-> std::optional<std::uint64_t> {
118+
// Check whether we're out of pages.
119+
if (++page >= dirNodeMaxPages)
120+
{
121+
return std::nullopt;
122+
}
94123

95-
// Check whether we're out of pages.
96-
if (++page >= dirNodeMaxPages)
97-
return std::nullopt;
124+
// We are about to create a new node; we'll link it to
125+
// the chain first:
126+
node->setFieldU64(sfIndexNext, page);
127+
update(node);
98128

99-
// We are about to create a new node; we'll link it to
100-
// the chain first:
101-
node->setFieldU64(sfIndexNext, page);
102-
update(node);
129+
next->setFieldU64(sfIndexPrevious, page);
130+
update(next);
103131

104-
root->setFieldU64(sfIndexPrevious, page);
105-
update(root);
132+
// Insert the new key:
133+
indexes.clear();
134+
indexes.push_back(key);
106135

107-
// Insert the new key:
108-
indexes.clear();
109-
indexes.push_back(key);
136+
node = std::make_shared<SLE>(keylet::page(directory, page));
137+
node->setFieldH256(sfRootIndex, directory.key);
138+
node->setFieldV256(sfIndexes, indexes);
110139

111-
node = std::make_shared<SLE>(keylet::page(directory, page));
112-
node->setFieldH256(sfRootIndex, directory.key);
113-
node->setFieldV256(sfIndexes, indexes);
140+
// Save some space by not specifying the value 0 since
141+
// it's the default.
142+
if (page != 1)
143+
node->setFieldU64(sfIndexPrevious, page - 1);
144+
if (nextPage)
145+
node->setFieldU64(sfIndexNext, nextPage);
146+
describe(node);
147+
insert(node);
148+
149+
return page;
150+
};
114151

115-
// Save some space by not specifying the value 0 since
116-
// it's the default.
117-
if (page != 1)
118-
node->setFieldU64(sfIndexPrevious, page - 1);
119-
describe(node);
120-
insert(node);
152+
auto const root = peek(directory);
153+
154+
if (!root)
155+
{
156+
// No root, make it.
157+
return createRoot(directory, key, describe);
158+
}
159+
160+
auto [page, node, indexes] = findPreviousPage(directory, root);
161+
162+
if (rules().enabled(featureDefragDirectories))
163+
{
164+
// If there are more nodes than just the root, and there's no space in
165+
// the last one, walk backwards to find one with space, or to find one
166+
// missing.
167+
std::optional<Gap> gapPages;
168+
while (page && indexes.size() >= dirNodeMaxEntries)
169+
{
170+
// Find a page with space, or a gap in pages.
171+
auto [prevPage, prevNode, prevIndexes] =
172+
findPreviousPage(directory, node);
173+
if (!gapPages && prevPage != page - 1)
174+
gapPages.emplace(prevPage, prevNode, page, node);
175+
page = prevPage;
176+
node = prevNode;
177+
indexes = prevIndexes;
178+
}
179+
// We looped through all the pages back to the root.
180+
if (!page)
181+
{
182+
// If we found a gap, use it.
183+
if (gapPages)
184+
{
185+
return insertPage(
186+
gapPages->page,
187+
gapPages->node,
188+
gapPages->nextPage,
189+
gapPages->node,
190+
key,
191+
indexes,
192+
directory,
193+
describe);
194+
}
195+
std::tie(page, node, indexes) = findPreviousPage(directory, root);
196+
}
197+
}
198+
199+
// If there's space, we use it:
200+
if (indexes.size() < dirNodeMaxEntries)
201+
{
202+
return insertKey(node, page, preserveOrder, indexes, key);
203+
}
121204

122-
return page;
205+
return insertPage(page, node, 0, root, key, indexes, directory, describe);
123206
}
124207

125208
bool

0 commit comments

Comments
 (0)