Skip to content

Commit 5117960

Browse files
committed
Reduce memory consumption of state archival startup check, make it opt-in
1 parent 720aff0 commit 5117960

File tree

4 files changed

+84
-29
lines changed

4 files changed

+84
-29
lines changed

src/bucket/BucketListSnapshotBase.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,6 @@ class SearchableBucketListSnapshotBase : public NonMovableOrCopyable
103103
medida::Meter& mBloomMisses;
104104
medida::Meter& mBloomLookups;
105105

106-
// Loops through all buckets, starting with curr at level 0, then snap at
107-
// level 0, etc. Calls f on each bucket. Exits early if function
108-
// returns Loop::COMPLETE.
109-
void loopAllBuckets(std::function<Loop(BucketSnapshotT const&)> f,
110-
BucketListSnapshot<BucketT> const& snapshot) const;
111-
112106
SearchableBucketListSnapshotBase(
113107
BucketSnapshotManager const& snapshotManager,
114108
AppConnector const& appConnector, SnapshotPtrT<BucketT>&& snapshot,
@@ -122,6 +116,19 @@ class SearchableBucketListSnapshotBase : public NonMovableOrCopyable
122116
size_t numEntries) const;
123117

124118
public:
119+
// Loops through all buckets, starting with curr at level 0, then snap at
120+
// level 0, etc. Calls f on each bucket. Exits early if function
121+
// returns Loop::COMPLETE.
122+
void loopAllBuckets(std::function<Loop(BucketSnapshotT const&)> f,
123+
BucketListSnapshot<BucketT> const& snapshot) const;
124+
125+
// Overload that uses the internal snapshot
126+
void
127+
loopAllBuckets(std::function<Loop(BucketSnapshotT const&)> f) const
128+
{
129+
loopAllBuckets(f, *mSnapshot);
130+
}
131+
125132
uint32_t
126133
getLedgerSeq() const
127134
{

src/invariant/ArchivedStateConsistency.cpp

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,16 @@ ArchivedStateConsistency::ArchivedStateConsistency() : Invariant(true)
2727
std::string
2828
ArchivedStateConsistency::start(Application& app)
2929
{
30-
LogSlowExecution logSlow("ArchivedStateConsistency::start");
30+
releaseAssert(threadIsMain());
31+
LogSlowExecution logSlow("ArchivedStateConsistency startup");
32+
33+
if (!app.getConfig().INVARIANT_EXTRA_CHECKS)
34+
{
35+
CLOG_INFO(Invariant,
36+
"Skipping ArchivedStateConsistency startup check - "
37+
"INVARIANT_EXTRA_CHECKS is disabled");
38+
return std::string{};
39+
}
3140

3241
auto protocolVersion =
3342
app.getLedgerManager().getLastClosedLedgerHeader().header.ledgerVersion;
@@ -47,31 +56,58 @@ ArchivedStateConsistency::start(Application& app)
4756

4857
std::map<LedgerKey, LedgerEntry> archived =
4958
app.getBucketManager().loadCompleteHotArchiveState(has);
50-
std::map<LedgerKey, LedgerEntry> live =
51-
app.getBucketManager().loadCompleteLedgerState(has);
52-
auto archivedIt = archived.begin();
53-
auto liveIt = live.begin();
5459

55-
while (archivedIt != archived.end() && liveIt != live.end())
56-
{
57-
if (archivedIt->first < liveIt->first)
60+
// Get live snapshot for iterating through buckets
61+
auto liveSnapshot = app.getBucketManager()
62+
.getBucketSnapshotManager()
63+
.copySearchableLiveBucketListSnapshot();
64+
65+
// Track which keys we've already seen in live buckets (from level 0 upward)
66+
// to avoid checking duplicates
67+
UnorderedSet<LedgerKey> seenKeys;
68+
69+
// Iterate through live buckets from level 0 upward and check if any key
70+
// also exists in archived state
71+
std::string result;
72+
liveSnapshot->loopAllBuckets([&](LiveBucketSnapshot const& bucketSnapshot) {
73+
LiveBucketInputIterator it(bucketSnapshot.getRawBucket());
74+
while (it && result.empty())
5875
{
59-
archivedIt++;
60-
continue;
61-
}
62-
else if (liveIt->first < archivedIt->first)
63-
{
64-
liveIt++;
65-
continue;
66-
}
67-
else
68-
{
69-
return fmt::format(
70-
FMT_STRING(
71-
"ArchivedStateConsistency:: Entry with the same key is "
72-
"present in both live and archived state. Key: {}"),
73-
xdrToCerealString(archivedIt->first, "entry_key"));
76+
BucketEntry const& e = *it;
77+
if (e.type() == LIVEENTRY || e.type() == INITENTRY)
78+
{
79+
auto key = LedgerEntryKey(e.liveEntry());
80+
81+
// Skip if we've already seen this key in a more recent
82+
// bucket
83+
if (seenKeys.find(key) == seenKeys.end())
84+
{
85+
seenKeys.insert(key);
86+
87+
// Check if this key also exists in archived state
88+
if (archived.find(key) != archived.end())
89+
{
90+
result = fmt::format(
91+
FMT_STRING(
92+
"ArchivedStateConsistency: Entry with the "
93+
"same key is present in both live and "
94+
"archived state. Key: {}"),
95+
xdrToCerealString(key, "entry_key"));
96+
}
97+
}
98+
}
99+
else
100+
{
101+
seenKeys.insert(e.deadEntry());
102+
}
103+
++it;
74104
}
105+
return result.empty() ? Loop::INCOMPLETE : Loop::COMPLETE;
106+
});
107+
108+
if (!result.empty())
109+
{
110+
return result;
75111
}
76112

77113
CLOG_INFO(Invariant, "ArchivedStateConsistency invariant passed");
@@ -134,6 +170,10 @@ ArchivedStateConsistency::checkOnLedgerCommit(
134170
// Keys for restored entries
135171
for (auto const& [key, entry] : restoredFromArchive)
136172
{
173+
if (key.type() != TTL)
174+
{
175+
releaseAssertOrThrow(isPersistentEntry(key));
176+
}
137177
allKeys.insert(key);
138178
}
139179
for (auto const& [key, entry] : restoredFromLiveState)

src/main/Config.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ Config::Config() : NODE_SEED(SecretKey::random())
355355
COMMANDS = {};
356356
REPORT_METRICS = {};
357357
INVARIANT_CHECKS = {};
358+
INVARIANT_EXTRA_CHECKS = false;
358359

359360
#ifdef BUILD_TESTS
360361
TEST_CASES_ENABLED = false;
@@ -1464,6 +1465,8 @@ Config::processConfig(std::shared_ptr<cpptoml::table> t)
14641465
[&]() { NETWORK_PASSPHRASE = readString(item); }},
14651466
{"INVARIANT_CHECKS",
14661467
[&]() { INVARIANT_CHECKS = readArray<std::string>(item); }},
1468+
{"INVARIANT_EXTRA_CHECKS",
1469+
[&]() { INVARIANT_EXTRA_CHECKS = readBool(item); }},
14671470
{"ENTRY_CACHE_SIZE",
14681471
[&]() { ENTRY_CACHE_SIZE = readInt<uint32_t>(item); }},
14691472
{"PREFETCH_BATCH_SIZE",

src/main/Config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,11 @@ class Config : public std::enable_shared_from_this<Config>
751751
// Invariants
752752
std::vector<std::string> INVARIANT_CHECKS;
753753

754+
// Enable extra invariant checks that provide additional consistency
755+
// verification but may be slow or resource-intensive. The default value is
756+
// false.
757+
bool INVARIANT_EXTRA_CHECKS;
758+
754759
std::map<std::string, std::string> VALIDATOR_NAMES;
755760

756761
// Information necessary to compute the weight of a validator for leader

0 commit comments

Comments
 (0)