@@ -27,7 +27,16 @@ ArchivedStateConsistency::ArchivedStateConsistency() : Invariant(true)
2727std::string
2828ArchivedStateConsistency::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)
0 commit comments