From baf27fa6037822638591bb4908b7e6c4ff792a38 Mon Sep 17 00:00:00 2001 From: Garand Tyson Date: Mon, 9 Feb 2026 14:03:24 -0800 Subject: [PATCH 1/5] Remove LedgerHeader from BucketListSnapshotData --- src/bucket/BucketListSnapshot.cpp | 33 +++++++--------- src/bucket/BucketListSnapshot.h | 26 +++++++------ src/bucket/BucketSnapshotManager.cpp | 57 ++++++++++++---------------- src/bucket/BucketSnapshotManager.h | 4 ++ 4 files changed, 57 insertions(+), 63 deletions(-) diff --git a/src/bucket/BucketListSnapshot.cpp b/src/bucket/BucketListSnapshot.cpp index eae919bc06..4172445641 100644 --- a/src/bucket/BucketListSnapshot.cpp +++ b/src/bucket/BucketListSnapshot.cpp @@ -40,7 +40,7 @@ BucketListSnapshotData::Level::Level( template BucketListSnapshotData::BucketListSnapshotData( - BucketListBase const& bl, LedgerHeader const& header) + BucketListBase const& bl) : levels([&bl]() { std::vector v; v.reserve(BucketListBase::kNumLevels); @@ -50,17 +50,9 @@ BucketListSnapshotData::BucketListSnapshotData( } return v; }()) - , header(header) { } -template -uint32_t -BucketListSnapshotData::getLedgerSeq() const -{ - return header.ledgerSeq; -} - // // SearchableBucketListSnapshot // @@ -70,9 +62,11 @@ SearchableBucketListSnapshot::SearchableBucketListSnapshot( MetricsRegistry& metrics, std::shared_ptr const> data, std::map const>> - historicalSnapshots) + historicalSnapshots, + LedgerHeader const& header) : mData(std::move(data)) , mHistoricalSnapshots(std::move(historicalSnapshots)) + , mHeader(header) , mMetrics(metrics) , mBulkLoadMeter( metrics.NewMeter({BucketT::METRIC_STRING, "query", "loads"}, "query")) @@ -336,7 +330,7 @@ SearchableBucketListSnapshot::loadKeysInternal( return keys.empty() ? Loop::COMPLETE : Loop::INCOMPLETE; }; - if (!ledgerSeq || *ledgerSeq == mData->getLedgerSeq()) + if (!ledgerSeq || *ledgerSeq == mHeader.ledgerSeq) { loopAllBuckets(loadKeysLoop, *mData); } @@ -388,8 +382,7 @@ template uint32_t SearchableBucketListSnapshot::getLedgerSeq() const { - releaseAssert(mData); - return mData->getLedgerSeq(); + return mHeader.ledgerSeq; } template @@ -397,7 +390,7 @@ LedgerHeader const& SearchableBucketListSnapshot::getLedgerHeader() const { releaseAssert(mData); - return mData->header; + return mHeader; } template @@ -424,9 +417,10 @@ SearchableLiveBucketListSnapshot::SearchableLiveBucketListSnapshot( std::shared_ptr const> data, std::map const>> - historicalSnapshots) - : SearchableBucketListSnapshot(metrics, std::move(data), - std::move(historicalSnapshots)) + historicalSnapshots, + LedgerHeader const& header) + : SearchableBucketListSnapshot( + metrics, std::move(data), std::move(historicalSnapshots), header) { } @@ -855,9 +849,10 @@ SearchableHotArchiveBucketListSnapshot::SearchableHotArchiveBucketListSnapshot( std::shared_ptr const> data, std::map const>> - historicalSnapshots) + historicalSnapshots, + LedgerHeader const& header) : SearchableBucketListSnapshot( - metrics, std::move(data), std::move(historicalSnapshots)) + metrics, std::move(data), std::move(historicalSnapshots), header) { } diff --git a/src/bucket/BucketListSnapshot.h b/src/bucket/BucketListSnapshot.h index 0412a5f966..092f11f9a6 100644 --- a/src/bucket/BucketListSnapshot.h +++ b/src/bucket/BucketListSnapshot.h @@ -44,9 +44,9 @@ class EvictionStatistics; template class BucketListBase; template class BucketLevel; -// BucketListSnapshotData holds the immutable snapshot data that can be safely -// shared across threads. It contains bucket references and the ledger header, -// but no mutable state like file streams. +// BucketListSnapshotData holds the immutable bucket references that can be +// safely shared across threads. It contains only bucket pointers and no +// metadata (headers, sequence numbers, etc.) or mutable state (file streams). template struct BucketListSnapshotData { BUCKET_TYPE_ASSERT(BucketT); @@ -62,12 +62,8 @@ template struct BucketListSnapshotData }; std::vector const levels; - LedgerHeader const header; - BucketListSnapshotData(BucketListBase const& bl, - LedgerHeader const& header); - - uint32_t getLedgerSeq() const; + explicit BucketListSnapshotData(BucketListBase const& bl); }; // SearchableBucketListSnapshot provides BucketList lookup functionality. @@ -88,6 +84,11 @@ template class SearchableBucketListSnapshot std::map const>> mHistoricalSnapshots; + // Ledger header associated with this snapshot. Stored separately from + // BucketListSnapshotData so that the snapshot data only contains bucket + // references. + LedgerHeader const mHeader; + // Per-snapshot mutable stream cache mutable UnorderedMap> mStreams; @@ -151,7 +152,8 @@ template class SearchableBucketListSnapshot std::shared_ptr const> data, std::map const>> - historicalSnapshots); + historicalSnapshots, + LedgerHeader const& header); public: virtual ~SearchableBucketListSnapshot() = default; @@ -191,7 +193,8 @@ class SearchableLiveBucketListSnapshot std::shared_ptr const> data, std::map const>> - historicalSnapshots); + historicalSnapshots, + LedgerHeader const& header); Loop scanForEvictionInBucket( std::shared_ptr const& bucket, EvictionIterator& iter, @@ -234,7 +237,8 @@ class SearchableHotArchiveBucketListSnapshot std::shared_ptr const> data, std::map const>> - historicalSnapshots); + historicalSnapshots, + LedgerHeader const& header); public: std::vector diff --git a/src/bucket/BucketSnapshotManager.cpp b/src/bucket/BucketSnapshotManager.cpp index cac2d6f0c0..92b8b495f6 100644 --- a/src/bucket/BucketSnapshotManager.cpp +++ b/src/bucket/BucketSnapshotManager.cpp @@ -19,10 +19,11 @@ BucketSnapshotManager::BucketSnapshotManager( uint32_t numHistoricalLedgers) : mAppConnector(app) , mCurrLiveSnapshot( - std::make_shared>(liveBL, header)) + std::make_shared>(liveBL)) , mCurrHotArchiveSnapshot( std::make_shared>( - hotArchiveBL, header)) + hotArchiveBL)) + , mCurrHeader(header) , mNumHistoricalSnapshots(numHistoricalLedgers) { releaseAssert(threadIsMain()); @@ -53,9 +54,9 @@ BucketSnapshotManager::copySearchableLiveBucketListSnapshot( { // Can't use std::make_shared due to private constructor return std::shared_ptr( - new SearchableLiveBucketListSnapshot(mAppConnector.getMetrics(), - mCurrLiveSnapshot, - mLiveHistoricalSnapshots)); + new SearchableLiveBucketListSnapshot( + mAppConnector.getMetrics(), mCurrLiveSnapshot, + mLiveHistoricalSnapshots, mCurrHeader)); } SearchableSnapshotConstPtr @@ -67,7 +68,7 @@ BucketSnapshotManager::copySearchableLiveBucketListSnapshot( return std::shared_ptr( new SearchableLiveBucketListSnapshot( metrics, snapshot->getSnapshotData(), - snapshot->getHistoricalSnapshots())); + snapshot->getHistoricalSnapshots(), snapshot->getLedgerHeader())); } SearchableHotArchiveSnapshotConstPtr @@ -80,7 +81,7 @@ BucketSnapshotManager::copySearchableHotArchiveBucketListSnapshot( return std::shared_ptr( new SearchableHotArchiveBucketListSnapshot( metrics, snapshot->getSnapshotData(), - snapshot->getHistoricalSnapshots())); + snapshot->getHistoricalSnapshots(), snapshot->getLedgerHeader())); } SearchableHotArchiveSnapshotConstPtr @@ -92,18 +93,7 @@ BucketSnapshotManager::copySearchableHotArchiveBucketListSnapshot( return std::shared_ptr( new SearchableHotArchiveBucketListSnapshot( mAppConnector.getMetrics(), mCurrHotArchiveSnapshot, - mHotArchiveHistoricalSnapshots)); -} - -namespace -{ -template -bool -needsUpdate(std::shared_ptr const& snapshot, - std::shared_ptr const& currData) -{ - return !snapshot || snapshot->getLedgerSeq() < currData->getLedgerSeq(); -} + mHotArchiveHistoricalSnapshots, mCurrHeader)); } void @@ -114,7 +104,7 @@ BucketSnapshotManager::maybeCopySearchableBucketListSnapshot( // modified. Rather, a thread is checking it's copy against the canonical // snapshot, so use a shared lock. SharedLockShared guard(mSnapshotMutex); - if (needsUpdate(snapshot, mCurrLiveSnapshot)) + if (!snapshot || snapshot->getLedgerSeq() < mCurrHeader.ledgerSeq) { snapshot = copySearchableLiveBucketListSnapshot(guard); } @@ -128,7 +118,7 @@ BucketSnapshotManager::maybeCopySearchableHotArchiveBucketListSnapshot( // modified. Rather, a thread is checking it's copy against the canonical // snapshot, so use a shared lock. SharedLockShared guard(mSnapshotMutex); - if (needsUpdate(snapshot, mCurrHotArchiveSnapshot)) + if (!snapshot || snapshot->getLedgerSeq() < mCurrHeader.ledgerSeq) { snapshot = copySearchableHotArchiveBucketListSnapshot(guard); } @@ -144,12 +134,13 @@ BucketSnapshotManager::maybeCopyLiveAndHotArchiveSnapshots( // snapshot, so use a shared lock. For consistency we hold the lock while // updating both snapshots. SharedLockShared guard(mSnapshotMutex); - if (needsUpdate(liveSnapshot, mCurrLiveSnapshot)) + if (!liveSnapshot || liveSnapshot->getLedgerSeq() < mCurrHeader.ledgerSeq) { liveSnapshot = copySearchableLiveBucketListSnapshot(guard); } - if (needsUpdate(hotArchiveSnapshot, mCurrHotArchiveSnapshot)) + if (!hotArchiveSnapshot || + hotArchiveSnapshot->getLedgerSeq() < mCurrHeader.ledgerSeq) { hotArchiveSnapshot = copySearchableHotArchiveBucketListSnapshot(guard); } @@ -170,13 +161,11 @@ BucketSnapshotManager::updateCurrentSnapshot( { auto updateSnapshot = [numHistoricalSnapshots = mNumHistoricalSnapshots]( auto& currentSnapshot, auto& historicalSnapshots, - auto newSnapshot) { + auto newSnapshot, uint32_t currLedgerSeq) { releaseAssert(newSnapshot); - releaseAssert(!currentSnapshot || newSnapshot->getLedgerSeq() >= - currentSnapshot->getLedgerSeq()); // First update historical snapshots - if (numHistoricalSnapshots != 0) + if (numHistoricalSnapshots != 0 && currentSnapshot) { // If historical snapshots are full, delete the oldest one if (historicalSnapshots.size() == numHistoricalSnapshots) @@ -184,7 +173,7 @@ BucketSnapshotManager::updateCurrentSnapshot( historicalSnapshots.erase(historicalSnapshots.begin()); } - historicalSnapshots.emplace(currentSnapshot->getLedgerSeq(), + historicalSnapshots.emplace(currLedgerSeq, std::move(currentSnapshot)); } @@ -192,16 +181,18 @@ BucketSnapshotManager::updateCurrentSnapshot( }; auto newLiveSnapshot = - std::make_shared>(liveBL, header); + std::make_shared>(liveBL); auto newHotArchiveSnapshot = - std::make_shared>(hotArchiveBL, - header); + std::make_shared>( + hotArchiveBL); // Updating canonical snapshots requires exclusive write access SharedLockExclusive guard(mSnapshotMutex); + releaseAssert(header.ledgerSeq >= mCurrHeader.ledgerSeq); updateSnapshot(mCurrLiveSnapshot, mLiveHistoricalSnapshots, - std::move(newLiveSnapshot)); + std::move(newLiveSnapshot), mCurrHeader.ledgerSeq); updateSnapshot(mCurrHotArchiveSnapshot, mHotArchiveHistoricalSnapshots, - std::move(newHotArchiveSnapshot)); + std::move(newHotArchiveSnapshot), mCurrHeader.ledgerSeq); + mCurrHeader = header; } } diff --git a/src/bucket/BucketSnapshotManager.h b/src/bucket/BucketSnapshotManager.h index 09fbaf1575..caf79cab18 100644 --- a/src/bucket/BucketSnapshotManager.h +++ b/src/bucket/BucketSnapshotManager.h @@ -47,6 +47,10 @@ class BucketSnapshotManager : NonMovableOrCopyable std::shared_ptr const> mCurrHotArchiveSnapshot GUARDED_BY(mSnapshotMutex){}; + // LedgerHeader corresponding to the current snapshots. Stored separately + // because BucketListSnapshotData no longer contains the header. + LedgerHeader mCurrHeader GUARDED_BY(mSnapshotMutex){}; + // ledgerSeq that the snapshot is based on -> snapshot std::map const>> From e7ab27ef726d9c83addf9a1317def342e3f2a976 Mon Sep 17 00:00:00 2001 From: Garand Tyson Date: Tue, 10 Feb 2026 16:24:07 -0800 Subject: [PATCH 2/5] Consolidated state access to LedgerStateSnapshot --- src/bucket/BucketListSnapshot.cpp | 29 +-- src/bucket/BucketListSnapshot.h | 23 +- src/bucket/BucketManager.cpp | 32 +-- src/bucket/BucketManager.h | 5 +- src/bucket/BucketSnapshotManager.cpp | 178 +++++--------- src/bucket/BucketSnapshotManager.h | 73 ++---- src/bucket/BucketUtils.h | 5 - src/bucket/test/BucketIndexTests.cpp | 118 +++++---- src/bucket/test/BucketListTests.cpp | 18 +- src/bucket/test/BucketTestUtils.cpp | 8 +- src/bucket/test/BucketTestUtils.h | 4 +- src/herder/test/HerderTests.cpp | 4 - src/herder/test/UpgradesTests.cpp | 12 +- src/invariant/ArchivedStateConsistency.cpp | 18 +- src/invariant/ArchivedStateConsistency.h | 3 +- src/invariant/BucketListStateConsistency.cpp | 25 +- src/invariant/BucketListStateConsistency.h | 3 +- src/invariant/ConservationOfLumens.cpp | 34 ++- src/invariant/ConservationOfLumens.h | 3 +- src/invariant/Invariant.h | 6 +- src/invariant/InvariantManager.h | 13 +- src/invariant/InvariantManagerImpl.cpp | 22 +- src/invariant/InvariantManagerImpl.h | 11 +- .../test/ConservationOfLumensTests.cpp | 56 ++--- src/invariant/test/InvariantTests.cpp | 114 ++++----- src/ledger/InMemorySorobanState.cpp | 16 +- src/ledger/InMemorySorobanState.h | 6 +- src/ledger/LedgerManager.h | 7 +- src/ledger/LedgerManagerImpl.cpp | 131 +++++----- src/ledger/LedgerManagerImpl.h | 20 +- src/ledger/LedgerStateSnapshot.cpp | 231 ++++++++++++++++-- src/ledger/LedgerStateSnapshot.h | 153 +++++++++++- src/ledger/LedgerTxn.cpp | 36 +-- src/ledger/LedgerTxnImpl.h | 6 +- src/ledger/NetworkConfig.cpp | 7 - src/ledger/NetworkConfig.h | 2 - src/ledger/P23HotArchiveBug.cpp | 13 +- src/ledger/SharedModuleCacheCompiler.cpp | 14 +- src/ledger/SharedModuleCacheCompiler.h | 7 +- src/main/AppConnector.cpp | 40 +-- src/main/AppConnector.h | 20 +- src/main/ApplicationUtils.cpp | 13 +- src/main/QueryServer.cpp | 48 ++-- src/main/QueryServer.h | 9 +- src/overlay/OverlayManager.h | 3 +- src/overlay/OverlayManagerImpl.cpp | 7 +- src/overlay/OverlayManagerImpl.h | 5 +- src/overlay/Peer.cpp | 2 +- src/simulation/test/LoadGeneratorTests.cpp | 10 +- .../InvokeHostFunctionOpFrame.cpp | 14 +- src/transactions/ParallelApplyUtils.cpp | 30 +-- src/transactions/ParallelApplyUtils.h | 28 +-- src/transactions/RestoreFootprintOpFrame.cpp | 6 +- .../test/InvokeHostFunctionTests.cpp | 119 ++++----- src/transactions/test/ParallelApplyTest.cpp | 13 +- src/transactions/test/SorobanTxTestUtils.cpp | 13 +- 56 files changed, 932 insertions(+), 914 deletions(-) diff --git a/src/bucket/BucketListSnapshot.cpp b/src/bucket/BucketListSnapshot.cpp index 4172445641..0376a3b33a 100644 --- a/src/bucket/BucketListSnapshot.cpp +++ b/src/bucket/BucketListSnapshot.cpp @@ -63,10 +63,10 @@ SearchableBucketListSnapshot::SearchableBucketListSnapshot( std::shared_ptr const> data, std::map const>> historicalSnapshots, - LedgerHeader const& header) + uint32_t ledgerSeq) : mData(std::move(data)) , mHistoricalSnapshots(std::move(historicalSnapshots)) - , mHeader(header) + , mLedgerSeq(ledgerSeq) , mMetrics(metrics) , mBulkLoadMeter( metrics.NewMeter({BucketT::METRIC_STRING, "query", "loads"}, "query")) @@ -330,7 +330,7 @@ SearchableBucketListSnapshot::loadKeysInternal( return keys.empty() ? Loop::COMPLETE : Loop::INCOMPLETE; }; - if (!ledgerSeq || *ledgerSeq == mHeader.ledgerSeq) + if (!ledgerSeq || *ledgerSeq == mLedgerSeq) { loopAllBuckets(loadKeysLoop, *mData); } @@ -378,21 +378,6 @@ SearchableBucketListSnapshot::getBulkLoadTimer( return iter->second; } -template -uint32_t -SearchableBucketListSnapshot::getLedgerSeq() const -{ - return mHeader.ledgerSeq; -} - -template -LedgerHeader const& -SearchableBucketListSnapshot::getLedgerHeader() const -{ - releaseAssert(mData); - return mHeader; -} - template std::shared_ptr const> const& SearchableBucketListSnapshot::getSnapshotData() const @@ -418,9 +403,9 @@ SearchableLiveBucketListSnapshot::SearchableLiveBucketListSnapshot( std::map const>> historicalSnapshots, - LedgerHeader const& header) + uint32_t ledgerSeq) : SearchableBucketListSnapshot( - metrics, std::move(data), std::move(historicalSnapshots), header) + metrics, std::move(data), std::move(historicalSnapshots), ledgerSeq) { } @@ -850,9 +835,9 @@ SearchableHotArchiveBucketListSnapshot::SearchableHotArchiveBucketListSnapshot( std::map const>> historicalSnapshots, - LedgerHeader const& header) + uint32_t ledgerSeq) : SearchableBucketListSnapshot( - metrics, std::move(data), std::move(historicalSnapshots), header) + metrics, std::move(data), std::move(historicalSnapshots), ledgerSeq) { } diff --git a/src/bucket/BucketListSnapshot.h b/src/bucket/BucketListSnapshot.h index 092f11f9a6..237707f020 100644 --- a/src/bucket/BucketListSnapshot.h +++ b/src/bucket/BucketListSnapshot.h @@ -13,7 +13,6 @@ #include "util/UnorderedSet.h" #include "util/XDRStream.h" #include "xdr/Stellar-ledger-entries.h" -#include "xdr/Stellar-ledger.h" #include #include @@ -43,6 +42,8 @@ struct StateArchivalSettings; class EvictionStatistics; template class BucketListBase; template class BucketLevel; +class CompleteConstLedgerState; +class LedgerStateSnapshot; // BucketListSnapshotData holds the immutable bucket references that can be // safely shared across threads. It contains only bucket pointers and no @@ -84,10 +85,10 @@ template class SearchableBucketListSnapshot std::map const>> mHistoricalSnapshots; - // Ledger header associated with this snapshot. Stored separately from - // BucketListSnapshotData so that the snapshot data only contains bucket - // references. - LedgerHeader const mHeader; + // Ledger sequence number for this snapshot, used internally to route + // queries between current and historical data. Not exposed publicly; + // callers should get ledger metadata from CompleteConstLedgerState. + uint32_t const mLedgerSeq; // Per-snapshot mutable stream cache mutable UnorderedMap> @@ -153,7 +154,7 @@ template class SearchableBucketListSnapshot std::map const>> historicalSnapshots, - LedgerHeader const& header); + uint32_t ledgerSeq); public: virtual ~SearchableBucketListSnapshot() = default; @@ -172,9 +173,6 @@ template class SearchableBucketListSnapshot loadKeysFromLedger(std::set const& inKeys, uint32_t ledgerSeq) const; - uint32_t getLedgerSeq() const; - LedgerHeader const& getLedgerHeader() const; - // Access to underlying data (for copying/refreshing) std::shared_ptr const> const& getSnapshotData() const; @@ -194,7 +192,7 @@ class SearchableLiveBucketListSnapshot std::map const>> historicalSnapshots, - LedgerHeader const& header); + uint32_t ledgerSeq); Loop scanForEvictionInBucket( std::shared_ptr const& bucket, EvictionIterator& iter, @@ -226,6 +224,8 @@ class SearchableLiveBucketListSnapshot std::function callback) const; friend class BucketSnapshotManager; + friend class CompleteConstLedgerState; + friend class LedgerStateSnapshot; }; // Hot archive bucket list snapshot @@ -238,7 +238,7 @@ class SearchableHotArchiveBucketListSnapshot std::map const>> historicalSnapshots, - LedgerHeader const& header); + uint32_t ledgerSeq); public: std::vector @@ -250,6 +250,7 @@ class SearchableHotArchiveBucketListSnapshot std::function callback) const; friend class BucketSnapshotManager; + friend class LedgerStateSnapshot; }; extern template struct BucketListSnapshotData; diff --git a/src/bucket/BucketManager.cpp b/src/bucket/BucketManager.cpp index 99dc31ca37..a73926987f 100644 --- a/src/bucket/BucketManager.cpp +++ b/src/bucket/BucketManager.cpp @@ -18,6 +18,7 @@ #include "historywork/VerifyBucketWork.h" #include "invariant/InvariantManager.h" #include "ledger/LedgerManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "ledger/LedgerTypeUtils.h" #include "ledger/NetworkConfig.h" @@ -1160,29 +1161,28 @@ BucketManager::maybeSetIndex( } void -BucketManager::startBackgroundEvictionScan( - SearchableSnapshotConstPtr lclSnapshot, SorobanNetworkConfig const& cfg) +BucketManager::startBackgroundEvictionScan(LedgerStateSnapshot lclSnapshot, + SorobanNetworkConfig const& cfg) { releaseAssert(mSnapshotManager); releaseAssert(!mEvictionFuture.valid()); releaseAssert(mEvictionStatistics); // Start the eviction scan for then _next_ ledger - auto ledgerSeq = lclSnapshot->getLedgerSeq() + 1; - auto ledgerVers = lclSnapshot->getLedgerHeader().ledgerVersion; + auto ledgerSeq = lclSnapshot.getLedgerSeq() + 1; + auto ledgerVers = lclSnapshot.getLedgerHeader().ledgerVersion; auto const& sas = cfg.stateArchivalSettings(); using task_t = std::packaged_task()>; - // MSVC gotcha: searchableBL has to be shared_ptr because MSVC wants to - // copy this lambda, otherwise we could use unique_ptr. auto task = std::make_shared( - [lclSnapshot, iter = cfg.evictionIterator(), ledgerSeq, ledgerVers, sas, - &metrics = mBucketListEvictionMetrics, stats = mEvictionStatistics] { + [snap = std::move(lclSnapshot), iter = cfg.evictionIterator(), + ledgerSeq, ledgerVers, sas, &metrics = mBucketListEvictionMetrics, + stats = mEvictionStatistics]() mutable { auto timer = metrics.backgroundTime.TimeScope(); - return lclSnapshot->scanForEviction(ledgerSeq, metrics, iter, stats, - sas, ledgerVers); + return snap.scanForEviction(ledgerSeq, metrics, iter, stats, sas, + ledgerVers); }); mEvictionFuture = task->get_future(); @@ -1193,7 +1193,7 @@ BucketManager::startBackgroundEvictionScan( EvictedStateVectors BucketManager::resolveBackgroundEvictionScan( - SearchableSnapshotConstPtr lclSnapshot, AbstractLedgerTxn& ltx, + LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, LedgerKeySet const& modifiedKeys) { ZoneScoped; @@ -1203,7 +1203,7 @@ BucketManager::resolveBackgroundEvictionScan( auto ledgerSeq = ls.getLedgerHeader().current().ledgerSeq; auto ledgerVers = ls.getLedgerHeader().current().ledgerVersion; auto networkConfig = SorobanNetworkConfig::loadFromLedger(ls); - releaseAssert(ledgerSeq == lclSnapshot->getLedgerSeq() + 1); + releaseAssert(ledgerSeq == lclSnapshot.getLedgerSeq() + 1); if (!mEvictionFuture.valid()) { @@ -1212,7 +1212,9 @@ BucketManager::resolveBackgroundEvictionScan( // candidates; this function later validates them by re-checking the // Soroban config and reloading the latest TTLs. Any entry restored in // the same ledger will be rejected by eviction validation logic. - startBackgroundEvictionScan(lclSnapshot, networkConfig); + startBackgroundEvictionScan( + LedgerStateSnapshot(lclSnapshot), + networkConfig); } auto evictionCandidates = mEvictionFuture.get(); @@ -1222,7 +1224,9 @@ BucketManager::resolveBackgroundEvictionScan( if (!evictionCandidates->isValid(ledgerSeq, ledgerVers, networkConfig.stateArchivalSettings())) { - startBackgroundEvictionScan(lclSnapshot, networkConfig); + startBackgroundEvictionScan( + LedgerStateSnapshot(lclSnapshot), + networkConfig); evictionCandidates = mEvictionFuture.get(); } diff --git a/src/bucket/BucketManager.h b/src/bucket/BucketManager.h index 24da0e171c..517ef2d9d8 100644 --- a/src/bucket/BucketManager.h +++ b/src/bucket/BucketManager.h @@ -6,6 +6,7 @@ #include "bucket/BucketMergeMap.h" #include "history/HistoryArchive.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/NetworkConfig.h" #include "main/Config.h" #include "util/ThreadAnnotations.h" @@ -341,7 +342,7 @@ class BucketManager : NonMovableOrCopyable // Scans BucketList for non-live entries to evict starting at the entry // pointed to by EvictionIterator. Evicts until `maxEntriesToEvict` entries // have been evicted or maxEvictionScanSize bytes have been scanned. - void startBackgroundEvictionScan(SearchableSnapshotConstPtr lclSnapshot, + void startBackgroundEvictionScan(LedgerStateSnapshot lclSnapshot, SorobanNetworkConfig const& networkConfig); // Returns a pair of vectors representing entries evicted this ledger, where @@ -350,7 +351,7 @@ class BucketManager : NonMovableOrCopyable // ContractCode). Note that when an entry is archived, its TTL key will be // included in the deleted keys vector. EvictedStateVectors - resolveBackgroundEvictionScan(SearchableSnapshotConstPtr lclSnapshot, + resolveBackgroundEvictionScan(LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, LedgerKeySet const& modifiedKeys); diff --git a/src/bucket/BucketSnapshotManager.cpp b/src/bucket/BucketSnapshotManager.cpp index 92b8b495f6..f3f44a6510 100644 --- a/src/bucket/BucketSnapshotManager.cpp +++ b/src/bucket/BucketSnapshotManager.cpp @@ -6,9 +6,9 @@ #include "bucket/BucketListSnapshot.h" #include "bucket/HotArchiveBucketList.h" #include "bucket/LiveBucketList.h" +#include "ledger/LedgerStateSnapshot.h" #include "main/AppConnector.h" #include "util/GlobalChecks.h" -#include "util/MetricsRegistry.h" namespace stellar { @@ -29,129 +29,33 @@ BucketSnapshotManager::BucketSnapshotManager( releaseAssert(threadIsMain()); releaseAssert(mCurrLiveSnapshot); releaseAssert(mCurrHotArchiveSnapshot); -} - -SearchableSnapshotConstPtr -BucketSnapshotManager::copySearchableLiveBucketListSnapshot() const -{ - SharedLockShared guard(mSnapshotMutex); - // Can't use std::make_shared due to private constructor - return copySearchableLiveBucketListSnapshot(guard); -} - -SearchableHotArchiveSnapshotConstPtr -BucketSnapshotManager::copySearchableHotArchiveBucketListSnapshot() const -{ - SharedLockShared guard(mSnapshotMutex); - releaseAssert(mCurrHotArchiveSnapshot); - // Can't use std::make_shared due to private constructor - return copySearchableHotArchiveBucketListSnapshot(guard); -} - -SearchableSnapshotConstPtr -BucketSnapshotManager::copySearchableLiveBucketListSnapshot( - SharedLockShared const& guard) const -{ - // Can't use std::make_shared due to private constructor - return std::shared_ptr( - new SearchableLiveBucketListSnapshot( - mAppConnector.getMetrics(), mCurrLiveSnapshot, - mLiveHistoricalSnapshots, mCurrHeader)); -} - -SearchableSnapshotConstPtr -BucketSnapshotManager::copySearchableLiveBucketListSnapshot( - SearchableSnapshotConstPtr const& snapshot, MetricsRegistry& metrics) -{ - // Can't use std::make_shared due to private constructor - releaseAssert(snapshot); - return std::shared_ptr( - new SearchableLiveBucketListSnapshot( - metrics, snapshot->getSnapshotData(), - snapshot->getHistoricalSnapshots(), snapshot->getLedgerHeader())); -} - -SearchableHotArchiveSnapshotConstPtr -BucketSnapshotManager::copySearchableHotArchiveBucketListSnapshot( - SearchableHotArchiveSnapshotConstPtr const& snapshot, - MetricsRegistry& metrics) -{ - releaseAssert(snapshot); - // Can't use std::make_shared due to private constructor - return std::shared_ptr( - new SearchableHotArchiveBucketListSnapshot( - metrics, snapshot->getSnapshotData(), - snapshot->getHistoricalSnapshots(), snapshot->getLedgerHeader())); -} - -SearchableHotArchiveSnapshotConstPtr -BucketSnapshotManager::copySearchableHotArchiveBucketListSnapshot( - SharedLockShared const& guard) const -{ - releaseAssert(mCurrHotArchiveSnapshot); - // Can't use std::make_shared due to private constructor - return std::shared_ptr( - new SearchableHotArchiveBucketListSnapshot( - mAppConnector.getMetrics(), mCurrHotArchiveSnapshot, - mHotArchiveHistoricalSnapshots, mCurrHeader)); -} - -void -BucketSnapshotManager::maybeCopySearchableBucketListSnapshot( - SearchableSnapshotConstPtr& snapshot) -{ - // The canonical snapshot held by the BucketSnapshotManager is not being - // modified. Rather, a thread is checking it's copy against the canonical - // snapshot, so use a shared lock. - SharedLockShared guard(mSnapshotMutex); - if (!snapshot || snapshot->getLedgerSeq() < mCurrHeader.ledgerSeq) - { - snapshot = copySearchableLiveBucketListSnapshot(guard); - } -} -void -BucketSnapshotManager::maybeCopySearchableHotArchiveBucketListSnapshot( - SearchableHotArchiveSnapshotConstPtr& snapshot) -{ - // The canonical snapshot held by the BucketSnapshotManager is not being - // modified. Rather, a thread is checking it's copy against the canonical - // snapshot, so use a shared lock. - SharedLockShared guard(mSnapshotMutex); - if (!snapshot || snapshot->getLedgerSeq() < mCurrHeader.ledgerSeq) - { - snapshot = copySearchableHotArchiveBucketListSnapshot(guard); - } + // Initialize mCompleteState so that copyLedgerStateSnapshot() is valid + // even before the first ledger close calls setCompleteState(). + // TODO: make less sketchy + LedgerHeaderHistoryEntry lcl; + lcl.header = header; + HistoryArchiveState has; + has.currentLedger = header.ledgerSeq; + mCompleteState = std::shared_ptr( + new CompleteConstLedgerState( + mCurrLiveSnapshot, mLiveHistoricalSnapshots, + mCurrHotArchiveSnapshot, mHotArchiveHistoricalSnapshots, lcl, has)); } -void -BucketSnapshotManager::maybeCopyLiveAndHotArchiveSnapshots( - SearchableSnapshotConstPtr& liveSnapshot, - SearchableHotArchiveSnapshotConstPtr& hotArchiveSnapshot) +std::map const>> +BucketSnapshotManager::getLiveHistoricalSnapshots() const { - // The canonical snapshot held by the BucketSnapshotManager is not being - // modified. Rather, a thread is checking it's copy against the canonical - // snapshot, so use a shared lock. For consistency we hold the lock while - // updating both snapshots. SharedLockShared guard(mSnapshotMutex); - if (!liveSnapshot || liveSnapshot->getLedgerSeq() < mCurrHeader.ledgerSeq) - { - liveSnapshot = copySearchableLiveBucketListSnapshot(guard); - } - - if (!hotArchiveSnapshot || - hotArchiveSnapshot->getLedgerSeq() < mCurrHeader.ledgerSeq) - { - hotArchiveSnapshot = copySearchableHotArchiveBucketListSnapshot(guard); - } + return mLiveHistoricalSnapshots; } -std::pair -BucketSnapshotManager::copySearchableBucketListSnapshots() const +std::map const>> +BucketSnapshotManager::getHotArchiveHistoricalSnapshots() const { SharedLockShared guard(mSnapshotMutex); - return {copySearchableLiveBucketListSnapshot(guard), - copySearchableHotArchiveBucketListSnapshot(guard)}; + return mHotArchiveHistoricalSnapshots; } void @@ -194,5 +98,49 @@ BucketSnapshotManager::updateCurrentSnapshot( updateSnapshot(mCurrHotArchiveSnapshot, mHotArchiveHistoricalSnapshots, std::move(newHotArchiveSnapshot), mCurrHeader.ledgerSeq); mCurrHeader = header; + + // Rebuild mCompleteState from the updated raw data so that + // copyLedgerStateSnapshot() always returns fresh state. This uses the + // private constructor that skips Soroban config loading; the normal + // ledger-close path will follow up with setCompleteState() which + // provides the full state including Soroban config. + // TODO: make less sketchy + LedgerHeaderHistoryEntry lcl; + lcl.header = header; + HistoryArchiveState has; + has.currentLedger = header.ledgerSeq; + mCompleteState = std::shared_ptr( + new CompleteConstLedgerState( + mCurrLiveSnapshot, mLiveHistoricalSnapshots, + mCurrHotArchiveSnapshot, mHotArchiveHistoricalSnapshots, lcl, has)); +} + +void +BucketSnapshotManager::setCompleteState(CompleteConstLedgerStatePtr state) +{ + SharedLockExclusive guard(mSnapshotMutex); + mCompleteState = std::move(state); +} + +LedgerStateSnapshot +BucketSnapshotManager::copyLedgerStateSnapshot() const +{ + SharedLockShared guard(mSnapshotMutex); + releaseAssert(mCompleteState); + return LedgerStateSnapshot(mCompleteState, mAppConnector.getMetrics()); +} + +void +BucketSnapshotManager::maybeUpdateLedgerStateSnapshot( + LedgerStateSnapshot& snapshot) const +{ + SharedLockShared guard(mSnapshotMutex); + releaseAssert(mCompleteState); + if (snapshot.getLedgerSeq() != + mCompleteState->getLastClosedLedgerHeader().header.ledgerSeq) + { + snapshot = + LedgerStateSnapshot(mCompleteState, mAppConnector.getMetrics()); + } } } diff --git a/src/bucket/BucketSnapshotManager.h b/src/bucket/BucketSnapshotManager.h index caf79cab18..ed44cb33dd 100644 --- a/src/bucket/BucketSnapshotManager.h +++ b/src/bucket/BucketSnapshotManager.h @@ -7,18 +7,16 @@ #include "bucket/BucketListSnapshot.h" #include "bucket/HotArchiveBucket.h" #include "bucket/LiveBucket.h" +#include "ledger/LedgerStateSnapshot.h" #include "util/NonCopyable.h" #include "util/ThreadAnnotations.h" #include #include -#include namespace medida { -class Meter; class MetricsRegistry; -class Timer; } namespace stellar @@ -61,6 +59,11 @@ class BucketSnapshotManager : NonMovableOrCopyable uint32_t const mNumHistoricalSnapshots; + // The current complete ledger state, set by LedgerManager after each + // ledger close. Used by copyLedgerStateSnapshot() to construct + // LedgerStateSnapshot instances. + CompleteConstLedgerStatePtr mCompleteState GUARDED_BY(mSnapshotMutex){}; + public: // Called by main thread to update snapshots whenever the BucketList // is updated @@ -77,58 +80,28 @@ class BucketSnapshotManager : NonMovableOrCopyable LedgerHeader const& header, uint32_t numHistoricalLedgers); - // Copy the most recent snapshot for the live bucket list - SearchableSnapshotConstPtr copySearchableLiveBucketListSnapshot() const + // Store the complete ledger state for use by copyLedgerStateSnapshot(). + // Called by LedgerManager after each ledger close. + void setCompleteState(CompleteConstLedgerStatePtr state) LOCKS_EXCLUDED(mSnapshotMutex); - // Create a deep copy from an existing searchable snapshot - static SearchableSnapshotConstPtr copySearchableLiveBucketListSnapshot( - SearchableSnapshotConstPtr const& snapshot, MetricsRegistry& metrics); - - // Create a deep copy from an existing hot archive snapshot - static SearchableHotArchiveSnapshotConstPtr - copySearchableHotArchiveBucketListSnapshot( - SearchableHotArchiveSnapshotConstPtr const& snapshot, - MetricsRegistry& metrics); - - // Copy the most recent snapshot for the hot archive bucket list - SearchableHotArchiveSnapshotConstPtr - copySearchableHotArchiveBucketListSnapshot() const - LOCKS_EXCLUDED(mSnapshotMutex); + // Access raw snapshot data and historical snapshots. Used by + // CompleteConstLedgerState construction. + std::map const>> + getLiveHistoricalSnapshots() const LOCKS_EXCLUDED(mSnapshotMutex); + std::map const>> + getHotArchiveHistoricalSnapshots() const LOCKS_EXCLUDED(mSnapshotMutex); - // Copy the most recent snapshot for the live bucket list, while holding the - // lock - SearchableSnapshotConstPtr - copySearchableLiveBucketListSnapshot(SharedLockShared const& guard) const - REQUIRES_SHARED(mSnapshotMutex); - - // Copy the most recent snapshot for the hot archive bucket list, while - // holding the lock - SearchableHotArchiveSnapshotConstPtr - copySearchableHotArchiveBucketListSnapshot( - SharedLockShared const& guard) const REQUIRES_SHARED(mSnapshotMutex); - - // `maybeCopy` interface refreshes `snapshot` if a newer snapshot is - // available. It's a no-op otherwise. This is useful to avoid unnecessary - // copying. - void - maybeCopySearchableBucketListSnapshot(SearchableSnapshotConstPtr& snapshot) - LOCKS_EXCLUDED(mSnapshotMutex); - void maybeCopySearchableHotArchiveBucketListSnapshot( - SearchableHotArchiveSnapshotConstPtr& snapshot) + // Create a LedgerStateSnapshot containing both live and hot archive + // snapshots plus the current header. Thread-safe. + LedgerStateSnapshot copyLedgerStateSnapshot() const LOCKS_EXCLUDED(mSnapshotMutex); - // This function is the same as snapshot refreshers above, but guarantees - // that both snapshots are consistent with the same lcl. This is required - // when querying both snapshot types as part of the same query. - void maybeCopyLiveAndHotArchiveSnapshots( - SearchableSnapshotConstPtr& liveSnapshot, - SearchableHotArchiveSnapshotConstPtr& hotArchiveSnapshot) + // Refresh `snapshot` if its ledger seq differs from the current snapshot. + // No-op otherwise. + void maybeUpdateLedgerStateSnapshot(LedgerStateSnapshot& snapshot) const LOCKS_EXCLUDED(mSnapshotMutex); - - // Copy both live and hot archive snapshots atomically under a single lock. - // This guarantees both snapshots are from the same ledger. - std::pair - copySearchableBucketListSnapshots() const LOCKS_EXCLUDED(mSnapshotMutex); }; } diff --git a/src/bucket/BucketUtils.h b/src/bucket/BucketUtils.h index 16c404838d..4f8361215e 100644 --- a/src/bucket/BucketUtils.h +++ b/src/bucket/BucketUtils.h @@ -35,11 +35,6 @@ class SearchableHotArchiveBucketListSnapshot; std::is_same_v, \ "BucketT must be a Bucket type") -using SearchableSnapshotConstPtr = - std::shared_ptr; -using SearchableHotArchiveSnapshotConstPtr = - std::shared_ptr; - // A fine-grained merge-operation-counter structure for tracking various // events during merges. These are not medida counters because we do not // want or need to publish this level of granularity outside of testing, and diff --git a/src/bucket/test/BucketIndexTests.cpp b/src/bucket/test/BucketIndexTests.cpp index b506ed4759..b10f3af291 100644 --- a/src/bucket/test/BucketIndexTests.cpp +++ b/src/bucket/test/BucketIndexTests.cpp @@ -13,6 +13,7 @@ #include "bucket/LiveBucket.h" #include "bucket/LiveBucketList.h" #include "bucket/test/BucketTestUtils.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTypeUtils.h" #include "ledger/test/LedgerTestUtils.h" #include "main/Application.h" @@ -215,12 +216,11 @@ class BucketIndexTest } while (ledger < mApp->getConfig().QUERY_SNAPSHOT_LEDGERS + 2); ++ledger; - auto searchableBL = getBM() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + auto snap = + getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); auto lk = LedgerEntryKey(canonicalEntry); - auto currentLoadedEntry = searchableBL->load(lk); + auto currentLoadedEntry = snap.loadLiveEntry(lk); REQUIRE(currentLoadedEntry); // Note: The definition of "historical snapshot" ledger is that the @@ -231,7 +231,7 @@ class BucketIndexTest for (uint32_t currLedger = ledger; currLedger > 0; --currLedger) { - auto loadRes = searchableBL->loadKeysFromLedger({lk}, currLedger); + auto loadRes = snap.loadLiveKeysFromLedger({lk}, currLedger); // If we query an older snapshot, should return if (currLedger < ledger - mApp->getConfig().QUERY_SNAPSHOT_LEDGERS) @@ -413,9 +413,8 @@ class BucketIndexTest virtual void run(std::optional expectedHitRate = std::nullopt) { - auto searchableBL = getBM() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + auto snap = + getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); auto& hitMeter = getBM().getCacheHitMeter(); auto& missMeter = getBM().getCacheMissMeter(); @@ -483,7 +482,7 @@ class BucketIndexTest }; // Test bulk load lookup - auto loadResult = searchableBL->loadKeys(mKeysToSearch, "test"); + auto loadResult = snap.loadLiveKeys(mKeysToSearch, "test"); validateResults(mTestEntries, loadResult); if (expectedHitRate) @@ -517,7 +516,7 @@ class BucketIndexTest for (auto iter = mKeysToSearch.rbegin(); iter != mKeysToSearch.rend(); ++iter) { - auto entryPtr = searchableBL->load(*iter); + auto entryPtr = snap.loadLiveEntry(*iter); if (entryPtr) { loadResult.emplace_back(*entryPtr); @@ -532,7 +531,7 @@ class BucketIndexTest mKeysToSearch.size()); // Run bulk lookup again - auto loadResult2 = searchableBL->loadKeys(mKeysToSearch, "test"); + auto loadResult2 = snap.loadLiveKeys(mKeysToSearch, "test"); validateResults(mTestEntries, loadResult2); checkHitRate(expectedHitRate, startingHitCount, startingMissCount, @@ -544,9 +543,8 @@ class BucketIndexTest virtual void runPerf(size_t n) { - auto searchableBL = getBM() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + auto snap = + getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); for (size_t i = 0; i < n; ++i) { LedgerKeySet searchSubset; @@ -577,7 +575,7 @@ class BucketIndexTest searchSubset.insert(addKeys.begin(), addKeys.end()); } - auto blLoad = searchableBL->loadKeys(searchSubset, "test"); + auto blLoad = snap.loadLiveKeys(searchSubset, "test"); validateResults(testEntriesSubset, blLoad); } } @@ -585,9 +583,8 @@ class BucketIndexTest void testInvalidKeys() { - auto searchableBL = getBM() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + auto snap = + getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); // Load should return empty vector for keys not in bucket list auto keysNotInBL = @@ -597,12 +594,12 @@ class BucketIndexTest LedgerKeySet invalidKeys(keysNotInBL.begin(), keysNotInBL.end()); // Test bulk load - REQUIRE(searchableBL->loadKeys(invalidKeys, "test").size() == 0); + REQUIRE(snap.loadLiveKeys(invalidKeys, "test").size() == 0); // Test individual load for (auto const& key : invalidKeys) { - auto entryPtr = searchableBL->load(key); + auto entryPtr = snap.loadLiveEntry(key); REQUIRE(!entryPtr); } } @@ -756,12 +753,10 @@ class BucketIndexPoolShareTest : public BucketIndexTest virtual void run(std::optional expectedHitRate = std::nullopt) override { - auto searchableBL = getBM() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); - auto loadResult = - searchableBL->loadPoolShareTrustLinesByAccountAndAsset( - mAccountToSearch.accountID, mAssetToSearch); + auto snap = + getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); + auto loadResult = snap.loadPoolShareTrustLinesByAccountAndAsset( + mAccountToSearch.accountID, mAssetToSearch); validateResults(mTestEntries, loadResult); } }; @@ -1119,7 +1114,7 @@ TEST_CASE("soroban cache population", "[soroban][bucketindex]") auto snapshot = test.getBM() .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + .copyLedgerStateSnapshot(); // First, test that the cache is maintained correctly via `addBatch` REQUIRE(codeEntries.size() == @@ -1129,12 +1124,12 @@ TEST_CASE("soroban cache population", "[soroban][bucketindex]") auto inMemoryEntry = inMemorySorobanState.get(k); REQUIRE(inMemoryEntry); - auto liveEntry = snapshot->load(k); + auto liveEntry = snapshot.loadLiveEntry(k); REQUIRE(liveEntry); REQUIRE(*liveEntry == *inMemoryEntry); auto ttlKey = getTTLKey(k); - auto ttlEntry = snapshot->load(ttlKey); + auto ttlEntry = snapshot.loadLiveEntry(ttlKey); REQUIRE(ttlEntry); auto inMemoryTTL = inMemorySorobanState.get(ttlKey); @@ -1149,12 +1144,12 @@ TEST_CASE("soroban cache population", "[soroban][bucketindex]") auto inMemoryEntry = inMemorySorobanState.get(k); REQUIRE(inMemoryEntry); - auto liveEntry = snapshot->load(k); + auto liveEntry = snapshot.loadLiveEntry(k); REQUIRE(liveEntry); REQUIRE(*liveEntry == *inMemoryEntry); auto ttlKey = getTTLKey(k); - auto ttlEntry = snapshot->load(ttlKey); + auto ttlEntry = snapshot.loadLiveEntry(ttlKey); REQUIRE(ttlEntry); auto inMemoryTTL = inMemorySorobanState.get(ttlKey); @@ -1327,9 +1322,9 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") auto ledger = 1; // Use snapshot across ledger to test update behavior - auto searchableBL = app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); + auto snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); auto checkLoad = [&](LedgerKey const& k, @@ -1356,12 +1351,12 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") LedgerKeySet bulkLoadKeys; for (auto const& k : keysToSearch) { - auto entryPtr = searchableBL->load(k); + auto entryPtr = snap.loadArchiveEntry(k); checkLoad(k, entryPtr); bulkLoadKeys.emplace(k); } - auto bulkLoadResult = searchableBL->loadKeys(bulkLoadKeys); + auto bulkLoadResult = snap.loadArchiveKeys(bulkLoadKeys); for (auto entry : bulkLoadResult) { REQUIRE(entry.type() == HOT_ARCHIVE_ARCHIVED); @@ -1402,9 +1397,9 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") HotArchiveBucket::FIRST_PROTOCOL_SUPPORTING_PERSISTENT_EVICTION); addHotArchiveBatchAndUpdateSnapshot(*app, header, archivedEntries, restoredEntries); - app->getBucketManager() - .getBucketSnapshotManager() - .maybeCopySearchableHotArchiveBucketListSnapshot(searchableBL); + snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); checkResult(); // Add a few batches so that entries are no longer in the top bucket @@ -1412,9 +1407,9 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") { header.ledgerSeq += 1; addHotArchiveBatchAndUpdateSnapshot(*app, header, {}, {}); - app->getBucketManager() - .getBucketSnapshotManager() - .maybeCopySearchableHotArchiveBucketListSnapshot(searchableBL); + snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); } // Shadow entries via liveEntry @@ -1424,20 +1419,19 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") header.ledgerSeq += 1; addHotArchiveBatchAndUpdateSnapshot(*app, header, {}, {liveShadow1, liveShadow2}); - app->getBucketManager() - .getBucketSnapshotManager() - .maybeCopySearchableHotArchiveBucketListSnapshot(searchableBL); + snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); // Point load for (auto const& k : {liveShadow1, liveShadow2}) { - auto entryPtr = searchableBL->load(k); + auto entryPtr = snap.loadArchiveEntry(k); REQUIRE(!entryPtr); } // Bulk load - auto bulkLoadResult = - searchableBL->loadKeys({liveShadow1, liveShadow2}); + auto bulkLoadResult = snap.loadArchiveKeys({liveShadow1, liveShadow2}); REQUIRE(bulkLoadResult.size() == 0); // Shadow via archivedEntries @@ -1446,12 +1440,12 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") header.ledgerSeq += 1; addHotArchiveBatchAndUpdateSnapshot(*app, header, {archivedShadow}, {}); - app->getBucketManager() - .getBucketSnapshotManager() - .maybeCopySearchableHotArchiveBucketListSnapshot(searchableBL); + snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); // Point load - auto entryPtr = searchableBL->load(LedgerEntryKey(archivedShadow)); + auto entryPtr = snap.loadArchiveEntry(LedgerEntryKey(archivedShadow)); REQUIRE(entryPtr); REQUIRE(entryPtr->type() == HotArchiveBucketEntryType::HOT_ARCHIVE_ARCHIVED); @@ -1459,7 +1453,7 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") // Bulk load auto bulkLoadResult2 = - searchableBL->loadKeys({LedgerEntryKey(archivedShadow)}); + snap.loadArchiveKeys({LedgerEntryKey(archivedShadow)}); REQUIRE(bulkLoadResult2.size() == 1); REQUIRE(bulkLoadResult2[0].type() == HOT_ARCHIVE_ARCHIVED); REQUIRE(bulkLoadResult2[0].archivedEntry() == archivedShadow); @@ -1626,9 +1620,9 @@ TEST_CASE("getRangeForType bounds verification", "[bucket][bucketindex]") .getCurr(); verifyIndexBounds(bucket); - auto searchableBL = app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + auto snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); auto verifyScanForType = [&](LedgerEntryType type, @@ -1641,7 +1635,7 @@ TEST_CASE("getRangeForType bounds verification", "[bucket][bucketindex]") expectedEntries.emplace(LedgerEntryKey(entry), entry); } - searchableBL->scanForEntriesOfType( + snap.scanLiveEntriesOfType( type, [&](BucketEntry const& be) { auto lk = getBucketLedgerKey(be); REQUIRE(lk.type() == type); @@ -1662,11 +1656,11 @@ TEST_CASE("getRangeForType bounds verification", "[bucket][bucketindex]") verifyScanForType(TRUSTLINE, trustlineEntries); // Verify that we don't call the callback for non-existent types - searchableBL->scanForEntriesOfType(CONTRACT_CODE, - [&](BucketEntry const& be) { - REQUIRE(false); - return Loop::INCOMPLETE; - }); + snap.scanLiveEntriesOfType(CONTRACT_CODE, + [&](BucketEntry const& be) { + REQUIRE(false); + return Loop::INCOMPLETE; + }); } }; diff --git a/src/bucket/test/BucketListTests.cpp b/src/bucket/test/BucketListTests.cpp index 3ace5ad01a..05d4483121 100644 --- a/src/bucket/test/BucketListTests.cpp +++ b/src/bucket/test/BucketListTests.cpp @@ -13,12 +13,14 @@ #include "bucket/BucketInputIterator.h" #include "bucket/BucketManager.h" #include "bucket/BucketOutputIterator.h" +#include "bucket/BucketSnapshotManager.h" #include "bucket/HotArchiveBucket.h" #include "bucket/HotArchiveBucketList.h" #include "bucket/LiveBucket.h" #include "bucket/LiveBucketList.h" #include "bucket/test/BucketTestUtils.h" #include "crypto/Hex.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTypeUtils.h" #include "ledger/test/LedgerTestUtils.h" #include "lib/util/stdrandom.h" @@ -1213,15 +1215,14 @@ TEST_CASE_VERSIONS("eviction scan", "[bucketlist][archival][soroban]") auto checkArchivedBucketList = [&] { if (!tempOnly) { - auto archiveSnapshot = - bm.getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); + auto archiveSnap = + bm.getBucketSnapshotManager().copyLedgerStateSnapshot(); // Check that persisted entries have been inserted into // HotArchive for (auto const& k : persistentEntries) { - auto archivedEntry = archiveSnapshot->load(k); + auto archivedEntry = archiveSnap.loadArchiveEntry(k); REQUIRE(archivedEntry); auto seen = false; @@ -1237,14 +1238,14 @@ TEST_CASE_VERSIONS("eviction scan", "[bucketlist][archival][soroban]") // Make sure TTL keys are not archived auto ttl = getTTLKey(k); - auto archivedTTL = archiveSnapshot->load(ttl); + auto archivedTTL = archiveSnap.loadArchiveEntry(ttl); REQUIRE(!archivedTTL); } // Temp entries should not be archived for (auto const& k : tempEntries) { - auto archivedEntry = archiveSnapshot->load(k); + auto archivedEntry = archiveSnap.loadArchiveEntry(k); REQUIRE(!archivedEntry); } } @@ -1727,11 +1728,10 @@ TEST_CASE_VERSIONS("Searchable BucketListDB snapshots", "[bucketlist]") } closeLedger(*app); - auto searchableBL = bm.getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + auto blSnap = bm.getBucketSnapshotManager().copyLedgerStateSnapshot(); // Snapshot should automatically update with latest version - auto loadedEntry = searchableBL->load(LedgerEntryKey(entry)); + auto loadedEntry = blSnap.loadLiveEntry(LedgerEntryKey(entry)); REQUIRE((loadedEntry && *loadedEntry == entry)); } } diff --git a/src/bucket/test/BucketTestUtils.cpp b/src/bucket/test/BucketTestUtils.cpp index ae0d0f8e26..f1753c0604 100644 --- a/src/bucket/test/BucketTestUtils.cpp +++ b/src/bucket/test/BucketTestUtils.cpp @@ -5,6 +5,7 @@ #include "BucketTestUtils.h" #include "bucket/BucketInputIterator.h" #include "bucket/BucketManager.h" +#include "bucket/BucketSnapshotManager.h" #include "bucket/HotArchiveBucket.h" #include "bucket/LiveBucket.h" #include "crypto/Hex.h" @@ -175,9 +176,7 @@ template size_t countEntries(std::shared_ptr bucket); void LedgerManagerForBucketTests::finalizeLedgerTxnChanges( - SearchableSnapshotConstPtr lclSnapshot, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveSnapshot, - AbstractLedgerTxn& ltx, + LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, LedgerHeader lh, uint32_t initialLedgerVers) { @@ -326,8 +325,7 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges( else { LedgerManagerImpl::finalizeLedgerTxnChanges( - lclSnapshot, lclHotArchiveSnapshot, ltx, ledgerCloseMeta, lh, - initialLedgerVers); + lclSnapshot, ltx, ledgerCloseMeta, lh, initialLedgerVers); } } diff --git a/src/bucket/test/BucketTestUtils.h b/src/bucket/test/BucketTestUtils.h index 96b57f41f2..73552c0671 100644 --- a/src/bucket/test/BucketTestUtils.h +++ b/src/bucket/test/BucketTestUtils.h @@ -73,9 +73,7 @@ class LedgerManagerForBucketTests : public LedgerManagerImpl protected: void finalizeLedgerTxnChanges( - SearchableSnapshotConstPtr lclSnapshot, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveSnapshot, - AbstractLedgerTxn& ltx, + LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, LedgerHeader lh, uint32_t initialLedgerVers) override; diff --git a/src/herder/test/HerderTests.cpp b/src/herder/test/HerderTests.cpp index 71b2b8b86d..ad6debaa07 100644 --- a/src/herder/test/HerderTests.cpp +++ b/src/herder/test/HerderTests.cpp @@ -5054,8 +5054,6 @@ TEST_CASE("ledger state update flow with parallel apply", "[herder][parallel]") REQUIRE(lm.getLastClosedLedgerNum() == lcl); REQUIRE(lm.getLastClosedLedgerHAS().currentLedger == lastHeader.ledgerSeq); - REQUIRE(lm.getLastClosedSnapshot()->getLedgerHeader() == - lastHeader); // Apply state got committed, but has not yet been propagated to // read-only state @@ -5107,8 +5105,6 @@ TEST_CASE("ledger state update flow with parallel apply", "[herder][parallel]") auto readOnly = lm.getLastClosedLedgerHeader(); REQUIRE(readOnly.header.ledgerSeq == lcl + 1); REQUIRE(lm.getLastClosedLedgerNum() == lcl + 1); - REQUIRE(lm.getLastClosedSnapshot()->getLedgerHeader() == - readOnly.header); auto has = lm.getLastClosedLedgerHAS(); REQUIRE(has.currentLedger == readOnly.header.ledgerSeq); diff --git a/src/herder/test/UpgradesTests.cpp b/src/herder/test/UpgradesTests.cpp index 3d736cada2..07d6aa4c43 100644 --- a/src/herder/test/UpgradesTests.cpp +++ b/src/herder/test/UpgradesTests.cpp @@ -14,6 +14,7 @@ #include "herder/Upgrades.h" #include "history/HistoryArchiveManager.h" #include "history/test/HistoryTestsUtils.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "ledger/LedgerTxnEntry.h" #include "ledger/LedgerTxnHeader.h" @@ -4056,8 +4057,7 @@ TEST_CASE("p24 upgrade fixes corrupted hot archive entries", }; auto runUpgradeAndGetSnapshot = [&]() { executeUpgrade(*app, makeProtocolVersionUpgrade(fixedProtocolVersion)); - return app->getAppConnector() - .copySearchableHotArchiveBucketListSnapshot(); + return app->getAppConnector().copyLedgerStateSnapshot(); }; auto const& corruptedEntries = p23_hot_archive_bug::internal::P23_CORRUPTED_HOT_ARCHIVE_ENTRIES; @@ -4077,10 +4077,10 @@ TEST_CASE("p24 upgrade fixes corrupted hot archive entries", BucketTestUtils::addHotArchiveBatchAndUpdateSnapshot( *app, app->getLedgerManager().getLastClosedLedgerHeader().header, allCorruptedEntries, {}); - auto hotArchiveSnapshot = runUpgradeAndGetSnapshot(); + auto snap = runUpgradeAndGetSnapshot(); for (auto const& [key, expectedEntry] : allExpectedFixedByKey) { - auto actual = hotArchiveSnapshot->load(key); + auto actual = snap.loadArchiveEntry(key); REQUIRE(actual); REQUIRE(actual->archivedEntry() == expectedEntry); } @@ -4092,8 +4092,8 @@ TEST_CASE("p24 upgrade fixes corrupted hot archive entries", BucketTestUtils::addHotArchiveBatchAndUpdateSnapshot( *app, app->getLedgerManager().getLastClosedLedgerHeader().header, allCorruptedEntries, {}); - auto hotArchiveSnapshot = runUpgradeAndGetSnapshot(); - auto actual = hotArchiveSnapshot->load(removedKey); + auto snap = runUpgradeAndGetSnapshot(); + auto actual = snap.loadArchiveEntry(removedKey); REQUIRE(!actual); } } diff --git a/src/invariant/ArchivedStateConsistency.cpp b/src/invariant/ArchivedStateConsistency.cpp index 102988a1a7..808d3bee67 100644 --- a/src/invariant/ArchivedStateConsistency.cpp +++ b/src/invariant/ArchivedStateConsistency.cpp @@ -4,11 +4,9 @@ #include "invariant/ArchivedStateConsistency.h" #include "bucket/BucketListSnapshot.h" -#include "bucket/BucketSnapshotManager.h" -#include "bucket/HotArchiveBucket.h" #include "bucket/LedgerCmp.h" #include "invariant/InvariantManager.h" -#include "ledger/LedgerManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTypeUtils.h" #include "main/Application.h" #include "transactions/TransactionUtils.h" @@ -40,8 +38,7 @@ ArchivedStateConsistency::getName() const std::string ArchivedStateConsistency::checkOnLedgerCommit( - SearchableSnapshotConstPtr lclLiveState, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveState, + LedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -51,14 +48,15 @@ ArchivedStateConsistency::checkOnLedgerCommit( LogSlowExecution::Mode::AUTOMATIC_RAII, "took", std::chrono::milliseconds(1)); + auto ledgerSeq = lclSnapshot.getLedgerSeq() + 1; + auto ledgerVers = lclSnapshot.getLedgerHeader().ledgerVersion; + if (protocolVersionIsBefore( - lclLiveState->getLedgerHeader().ledgerVersion, + ledgerVers, LiveBucket::FIRST_PROTOCOL_SUPPORTING_PERSISTENT_EVICTION)) { return std::string{}; } - auto ledgerSeq = lclLiveState->getLedgerSeq() + 1; - auto ledgerVers = lclLiveState->getLedgerHeader().ledgerVersion; // Collect all keys to preload LedgerKeySet allKeys; @@ -95,13 +93,13 @@ ArchivedStateConsistency::checkOnLedgerCommit( // Preload from both live and archived state UnorderedMap preloadedLiveEntries; auto preloadedLiveVector = - lclLiveState->loadKeys(allKeys, "ArchivedStateConsistency"); + lclSnapshot.loadLiveKeys(allKeys, "ArchivedStateConsistency"); for (auto const& entry : preloadedLiveVector) { preloadedLiveEntries[LedgerEntryKey(entry)] = entry; } - auto preloadedArchivedVector = lclHotArchiveState->loadKeys(allKeys); + auto preloadedArchivedVector = lclSnapshot.loadArchiveKeys(allKeys); UnorderedMap preloadedArchivedEntries; for (auto const& entry : preloadedArchivedVector) { diff --git a/src/invariant/ArchivedStateConsistency.h b/src/invariant/ArchivedStateConsistency.h index 9d0e622794..13cf995319 100644 --- a/src/invariant/ArchivedStateConsistency.h +++ b/src/invariant/ArchivedStateConsistency.h @@ -34,8 +34,7 @@ class ArchivedStateConsistency : public Invariant virtual std::string getName() const override; virtual std::string checkOnLedgerCommit( - SearchableSnapshotConstPtr lclLiveState, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveState, + LedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, diff --git a/src/invariant/BucketListStateConsistency.cpp b/src/invariant/BucketListStateConsistency.cpp index 30dd33ec54..25178549f0 100644 --- a/src/invariant/BucketListStateConsistency.cpp +++ b/src/invariant/BucketListStateConsistency.cpp @@ -8,6 +8,7 @@ #include "invariant/Invariant.h" #include "invariant/InvariantManager.h" #include "ledger/InMemorySorobanState.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTypeUtils.h" #include "ledger/NetworkConfig.h" #include "main/Application.h" @@ -35,15 +36,14 @@ BucketListStateConsistency::BucketListStateConsistency() : Invariant(true) // 7. The cached total entry sizes match the sum of actual entry sizes std::string BucketListStateConsistency::checkSnapshot( - SearchableSnapshotConstPtr liveSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, + LedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) { LogSlowExecution logSlow("BucketListStateConsistency::checkSnapshot", LogSlowExecution::Mode::AUTOMATIC_RAII, "took", std::chrono::minutes(2)); - auto const& header = liveSnapshot->getLedgerHeader(); + auto const& header = snapshot.getLedgerHeader(); if (protocolVersionIsBefore(header.ledgerVersion, SOROBAN_PROTOCOL_VERSION)) { @@ -66,13 +66,14 @@ BucketListStateConsistency::checkSnapshot( std::string errorMsg; // Property 7: Track total entry sizes for validation - auto sorobanConfig = SorobanNetworkConfig::loadFromLedger(liveSnapshot); + LedgerSnapshot lsForConfig(snapshot); + auto sorobanConfig = SorobanNetworkConfig::loadFromLedger(lsForConfig); uint64_t expectedSorobanSize = 0; auto checkLiveEntry = [&seenLiveNonTTLKeys, &seenDeadKeys, &errorMsg, - &inMemorySnapshot, &hotArchiveSnapshot, - checkHotArchive, &isStopping, &expectedSorobanSize, - &header, &sorobanConfig](BucketEntry const& be) { + &inMemorySnapshot, &snapshot, checkHotArchive, + &isStopping, &expectedSorobanSize, &header, + &sorobanConfig](BucketEntry const& be) { if (isStopping()) { return Loop::COMPLETE; @@ -116,7 +117,7 @@ BucketListStateConsistency::checkSnapshot( } // Check property 5: live entry should not exist in hot archive - if (checkHotArchive && hotArchiveSnapshot->load(lk)) + if (checkHotArchive && snapshot.loadArchiveEntry(lk)) { errorMsg = fmt::format( FMT_STRING("BucketListStateConsistency invariant failed: " @@ -158,7 +159,7 @@ BucketListStateConsistency::checkSnapshot( }; // First check contract data entries. - liveSnapshot->scanForEntriesOfType(CONTRACT_DATA, checkLiveEntry); + snapshot.scanLiveEntriesOfType(CONTRACT_DATA, checkLiveEntry); // Note: All BucketList scans will exit early if isStopping() is true or if // there is an error, so we need to call shouldAbortInvariantScan after each @@ -175,7 +176,7 @@ BucketListStateConsistency::checkSnapshot( // keys since we will need them when checking TTL entries. seenDeadKeys.clear(); - liveSnapshot->scanForEntriesOfType(CONTRACT_CODE, checkLiveEntry); + snapshot.scanLiveEntriesOfType(CONTRACT_CODE, checkLiveEntry); if (shouldAbortInvariantScan(errorMsg, isStopping)) { return errorMsg; @@ -304,7 +305,7 @@ BucketListStateConsistency::checkSnapshot( }; seenDeadKeys.clear(); - liveSnapshot->scanForEntriesOfType(TTL, checkTTLEntry); + snapshot.scanLiveEntriesOfType(TTL, checkTTLEntry); if (shouldAbortInvariantScan(errorMsg, isStopping)) { return errorMsg; @@ -376,7 +377,7 @@ BucketListStateConsistency::checkSnapshot( return Loop::INCOMPLETE; }; - hotArchiveSnapshot->scanAllEntries(checkHotArchiveEntry); + snapshot.scanAllArchiveEntries(checkHotArchiveEntry); if (shouldAbortInvariantScan(errorMsg, isStopping)) { return errorMsg; diff --git a/src/invariant/BucketListStateConsistency.h b/src/invariant/BucketListStateConsistency.h index 6c555fe092..a79904318e 100644 --- a/src/invariant/BucketListStateConsistency.h +++ b/src/invariant/BucketListStateConsistency.h @@ -19,8 +19,7 @@ class BucketListStateConsistency : public Invariant virtual std::string getName() const override; virtual std::string - checkSnapshot(SearchableSnapshotConstPtr liveSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, + checkSnapshot(LedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) override; }; diff --git a/src/invariant/ConservationOfLumens.cpp b/src/invariant/ConservationOfLumens.cpp index 535a4532fb..339fe65d0d 100644 --- a/src/invariant/ConservationOfLumens.cpp +++ b/src/invariant/ConservationOfLumens.cpp @@ -6,6 +6,7 @@ #include "invariant/Invariant.h" #include "invariant/InvariantManager.h" #include "ledger/LedgerManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "main/Application.h" #include "transactions/TransactionUtils.h" @@ -222,11 +223,9 @@ processEntryIfNew(LedgerEntry const& entry, LedgerKey const& key, // Scan live bucket list for entries that can hold the native asset static void -scanLiveBuckets( - std::shared_ptr const& liveSnapshot, - Asset const& asset, AssetContractInfo const& assetContractInfo, - int64_t& sumBalance, std::string& errorMsg, - std::function const& isStopping) +scanLiveBuckets(LedgerStateSnapshot const& snapshot, Asset const& asset, + AssetContractInfo const& assetContractInfo, int64_t& sumBalance, + std::string& errorMsg, std::function const& isStopping) { // Scan all entry types that can hold the native asset for (auto let : xdr::xdr_traits::enum_values()) @@ -239,7 +238,7 @@ scanLiveBuckets( std::unordered_set countedKeys; - liveSnapshot->scanForEntriesOfType( + snapshot.scanLiveEntriesOfType( type, [&](BucketEntry const& be) -> Loop { if (isStopping()) { @@ -271,15 +270,13 @@ scanLiveBuckets( } static void -scanHotArchiveBuckets( - std::shared_ptr const& - hotArchiveSnapshot, - Asset const& asset, AssetContractInfo const& assetContractInfo, - int64_t& sumBalance, std::string& errorMsg, - std::function const& isStopping) +scanHotArchiveBuckets(LedgerStateSnapshot const& snapshot, Asset const& asset, + AssetContractInfo const& assetContractInfo, + int64_t& sumBalance, std::string& errorMsg, + std::function const& isStopping) { std::unordered_set countedKeys; - hotArchiveSnapshot->scanAllEntries([&](HotArchiveBucketEntry const& be) { + snapshot.scanAllArchiveEntries([&](HotArchiveBucketEntry const& be) { if (isStopping()) { return Loop::COMPLETE; @@ -312,8 +309,7 @@ scanHotArchiveBuckets( std::string ConservationOfLumens::checkSnapshot( - SearchableSnapshotConstPtr liveSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, + LedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) { @@ -321,7 +317,7 @@ ConservationOfLumens::checkSnapshot( LogSlowExecution::Mode::AUTOMATIC_RAII, "took", std::chrono::seconds(90)); - auto const& header = liveSnapshot->getLedgerHeader(); + auto const& header = snapshot.getLedgerHeader(); // This invariant can fail prior to v24 due to bugs if (protocolVersionIsBefore(header.ledgerVersion, ProtocolVersion::V_24)) @@ -346,7 +342,7 @@ ConservationOfLumens::checkSnapshot( // Scan the Live BucketList for native balances using loopAllBuckets - scanLiveBuckets(liveSnapshot, nativeAsset, mLumenContractInfo, sumBalance, + scanLiveBuckets(snapshot, nativeAsset, mLumenContractInfo, sumBalance, errorMsg, isStopping); if (shouldAbortInvariantScan(errorMsg, isStopping)) @@ -355,8 +351,8 @@ ConservationOfLumens::checkSnapshot( } // Scan the Hot Archive for native balances - scanHotArchiveBuckets(hotArchiveSnapshot, nativeAsset, mLumenContractInfo, - sumBalance, errorMsg, isStopping); + scanHotArchiveBuckets(snapshot, nativeAsset, mLumenContractInfo, sumBalance, + errorMsg, isStopping); if (shouldAbortInvariantScan(errorMsg, isStopping)) { diff --git a/src/invariant/ConservationOfLumens.h b/src/invariant/ConservationOfLumens.h index 66bd4685e1..b90e2f011c 100644 --- a/src/invariant/ConservationOfLumens.h +++ b/src/invariant/ConservationOfLumens.h @@ -34,8 +34,7 @@ class ConservationOfLumens : public Invariant std::vector const& events, AppConnector& app) override; virtual std::string - checkSnapshot(SearchableSnapshotConstPtr liveSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, + checkSnapshot(LedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) override; diff --git a/src/invariant/Invariant.h b/src/invariant/Invariant.h index 14a1ef0daa..f0d6bf62d1 100644 --- a/src/invariant/Invariant.h +++ b/src/invariant/Invariant.h @@ -74,8 +74,7 @@ class Invariant virtual std::string checkOnLedgerCommit( - SearchableSnapshotConstPtr lclLiveState, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveState, + LedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -85,8 +84,7 @@ class Invariant } virtual std::string - checkSnapshot(SearchableSnapshotConstPtr liveSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, + checkSnapshot(LedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) { diff --git a/src/invariant/InvariantManager.h b/src/invariant/InvariantManager.h index c19c1bc707..1167f238fd 100644 --- a/src/invariant/InvariantManager.h +++ b/src/invariant/InvariantManager.h @@ -16,6 +16,7 @@ class Application; class Bucket; class Invariant; class LedgerManager; +class LedgerStateSnapshot; struct EvictedStateVectors; struct LedgerTxnDelta; struct Operation; @@ -54,8 +55,7 @@ class InvariantManager AppConnector& app) = 0; virtual void checkOnLedgerCommit( - SearchableSnapshotConstPtr lclLiveState, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveState, + LedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -66,11 +66,10 @@ class InvariantManager // The invariant will periodically run on a background thread against the // given ledger state snapshot. These invariants will only run if // INVARIANT_EXTRA_CHECKS is enabled. - virtual void runStateSnapshotInvariant( - SearchableSnapshotConstPtr liveSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, - InMemorySorobanState const& inMemorySnapshot, - std::function isStopping) = 0; + virtual void + runStateSnapshotInvariant(LedgerStateSnapshot const& snapshot, + InMemorySorobanState const& inMemorySnapshot, + std::function isStopping) = 0; virtual void registerInvariant(std::shared_ptr invariant) = 0; diff --git a/src/invariant/InvariantManagerImpl.cpp b/src/invariant/InvariantManagerImpl.cpp index 41f397c7ff..b935b73847 100644 --- a/src/invariant/InvariantManagerImpl.cpp +++ b/src/invariant/InvariantManagerImpl.cpp @@ -15,6 +15,7 @@ #include "invariant/InvariantManagerImpl.h" #include "ledger/InMemorySorobanState.h" #include "ledger/LedgerManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "lib/util/finally.h" #include "main/Application.h" @@ -174,8 +175,7 @@ InvariantManagerImpl::checkOnOperationApply( void InvariantManagerImpl::checkOnLedgerCommit( - SearchableSnapshotConstPtr lclLiveState, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveState, + LedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -184,9 +184,8 @@ InvariantManagerImpl::checkOnLedgerCommit( for (auto invariant : mEnabled) { auto result = invariant->checkOnLedgerCommit( - lclLiveState, lclHotArchiveState, persitentEvictedFromLive, - tempAndTTLEvictedFromLive, restoredFromArchive, - restoredFromLiveState); + lclSnapshot, persitentEvictedFromLive, tempAndTTLEvictedFromLive, + restoredFromArchive, restoredFromLiveState); if (result.empty()) { continue; @@ -195,8 +194,7 @@ InvariantManagerImpl::checkOnLedgerCommit( auto message = fmt::format( FMT_STRING(R"(Invariant "{}" does not hold on ledger commit: {})"), invariant->getName(), result); - onInvariantFailure(invariant, message, - lclLiveState->getLedgerSeq() + 1); + onInvariantFailure(invariant, message, lclSnapshot.getLedgerSeq()); } } @@ -333,8 +331,7 @@ InvariantManagerImpl::handleInvariantFailure(bool isStrict, // required state, then call this function in a background thread. void InvariantManagerImpl::runStateSnapshotInvariant( - SearchableSnapshotConstPtr liveSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, + LedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) { @@ -346,12 +343,11 @@ InvariantManagerImpl::runStateSnapshotInvariant( { for (auto const& invariant : mEnabled) { - auto result = invariant->checkSnapshot( - liveSnapshot, hotArchiveSnapshot, inMemorySnapshot, isStopping); + auto result = invariant->checkSnapshot(snapshot, inMemorySnapshot, + isStopping); if (!result.empty()) { - auto ledgerSeq = liveSnapshot->getLedgerSeq(); - onInvariantFailure(invariant, result, ledgerSeq); + onInvariantFailure(invariant, result, snapshot.getLedgerSeq()); } } } diff --git a/src/invariant/InvariantManagerImpl.h b/src/invariant/InvariantManagerImpl.h index 55d9bd7e18..b4c60befae 100644 --- a/src/invariant/InvariantManagerImpl.h +++ b/src/invariant/InvariantManagerImpl.h @@ -62,8 +62,7 @@ class InvariantManagerImpl : public InvariantManager std::unordered_set const& shadowedKeys) override; virtual void checkOnLedgerCommit( - SearchableSnapshotConstPtr lclLiveState, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveState, + LedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -82,11 +81,9 @@ class InvariantManagerImpl : public InvariantManager bool shouldRunInvariantSnapshot() const override; void markStartOfInvariantSnapshot() override; - void runStateSnapshotInvariant( - SearchableSnapshotConstPtr liveSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, - InMemorySorobanState const& inMemorySnapshot, - std::function isStopping) override; + void runStateSnapshotInvariant(LedgerStateSnapshot const& snapshot, + InMemorySorobanState const& inMemorySnapshot, + std::function isStopping) override; #ifdef BUILD_TESTS void snapshotForFuzzer() override; diff --git a/src/invariant/test/ConservationOfLumensTests.cpp b/src/invariant/test/ConservationOfLumensTests.cpp index 93a7142158..2fcdd48a44 100644 --- a/src/invariant/test/ConservationOfLumensTests.cpp +++ b/src/invariant/test/ConservationOfLumensTests.cpp @@ -7,6 +7,7 @@ #include "invariant/InvariantDoesNotHold.h" #include "invariant/InvariantManager.h" #include "invariant/test/InvariantTestUtils.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "ledger/LedgerTxnHeader.h" #include "ledger/test/LedgerTestUtils.h" @@ -332,15 +333,12 @@ TEST_CASE( // Verify the snapshot invariant passes { - auto ledgerState = - app.getLedgerManager().getLastClosedLedgerStateForTesting(); + auto snap = app.getLedgerManager().getLastClosedSnapshot(); auto& inMemoryState = app.getLedgerManager().getInMemorySorobanStateForTesting(); REQUIRE_NOTHROW(app.getInvariantManager().runStateSnapshotInvariant( - ledgerState->getBucketSnapshot(), - ledgerState->getHotArchiveSnapshot(), inMemoryState, - []() { return false; })); + snap, inMemoryState, []() { return false; })); } // Now, manually modify totalCoins to be inconsistent. The invariant should @@ -354,18 +352,15 @@ TEST_CASE( closeLedger(test.getApp()); - auto ledgerState = - app.getLedgerManager().getLastClosedLedgerStateForTesting(); + auto snap = app.getLedgerManager().getLastClosedSnapshot(); auto& inMemoryState = app.getLedgerManager().getInMemorySorobanStateForTesting(); Asset native(ASSET_TYPE_NATIVE); auto lumenInfo = getAssetContractInfo(native, app.getNetworkID()); ConservationOfLumens invariant(lumenInfo); - auto result = - invariant.checkSnapshot(ledgerState->getBucketSnapshot(), - ledgerState->getHotArchiveSnapshot(), - inMemoryState, []() { return false; }); + auto result = invariant.checkSnapshot(snap, inMemoryState, + []() { return false; }); REQUIRE_FALSE(result.empty()); REQUIRE(result.find("Total native asset supply mismatch") != std::string::npos); @@ -422,15 +417,12 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", app->getInvariantManager().enableInvariant("ConservationOfLumens"); - auto ledgerState = - app->getLedgerManager().getLastClosedLedgerStateForTesting(); + auto snap = app->getLedgerManager().getLastClosedSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); REQUIRE_NOTHROW(app->getInvariantManager().runStateSnapshotInvariant( - ledgerState->getBucketSnapshot(), - ledgerState->getHotArchiveSnapshot(), inMemoryState, - []() { return false; })); + snap, inMemoryState, []() { return false; })); } SECTION("Invariant fails when bucket balance doesn't match totalCoins") @@ -458,18 +450,15 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", BucketTestUtils::closeLedger(*app); - auto ledgerState = - app->getLedgerManager().getLastClosedLedgerStateForTesting(); + auto snap = app->getLedgerManager().getLastClosedSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); Asset native(ASSET_TYPE_NATIVE); auto lumenInfo = getAssetContractInfo(native, app->getNetworkID()); ConservationOfLumens invariant(lumenInfo); - auto result = - invariant.checkSnapshot(ledgerState->getBucketSnapshot(), - ledgerState->getHotArchiveSnapshot(), - inMemoryState, []() { return false; }); + auto result = invariant.checkSnapshot(snap, inMemoryState, + []() { return false; }); REQUIRE_FALSE(result.empty()); REQUIRE(result.find("Total native asset supply mismatch") != std::string::npos); @@ -521,15 +510,12 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", app->getInvariantManager().enableInvariant("ConservationOfLumens"); - auto ledgerState = - app->getLedgerManager().getLastClosedLedgerStateForTesting(); + auto snap = app->getLedgerManager().getLastClosedSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); REQUIRE_NOTHROW(app->getInvariantManager().runStateSnapshotInvariant( - ledgerState->getBucketSnapshot(), - ledgerState->getHotArchiveSnapshot(), inMemoryState, - []() { return false; })); + snap, inMemoryState, []() { return false; })); } SECTION("Invariant detects corrupted native balance in hot archive") @@ -580,16 +566,13 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", BucketTestUtils::closeLedger(*app); { - auto ledgerState = - app->getLedgerManager().getLastClosedLedgerStateForTesting(); + auto snap = app->getLedgerManager().getLastClosedSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); REQUIRE_NOTHROW( app->getInvariantManager().runStateSnapshotInvariant( - ledgerState->getBucketSnapshot(), - ledgerState->getHotArchiveSnapshot(), inMemoryState, - []() { return false; })); + snap, inMemoryState, []() { return false; })); } // Corrupt the other live balance by adding 123 stroops to the balance @@ -603,18 +586,15 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", BucketTestUtils::closeLedger(*app); { - auto ledgerState = - app->getLedgerManager().getLastClosedLedgerStateForTesting(); + auto snap = app->getLedgerManager().getLastClosedSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); Asset native(ASSET_TYPE_NATIVE); auto lumenInfo = getAssetContractInfo(native, app->getNetworkID()); ConservationOfLumens invariant(lumenInfo); - auto result = - invariant.checkSnapshot(ledgerState->getBucketSnapshot(), - ledgerState->getHotArchiveSnapshot(), - inMemoryState, []() { return false; }); + auto result = invariant.checkSnapshot(snap, inMemoryState, + []() { return false; }); REQUIRE_FALSE(result.empty()); REQUIRE(result.find("Total native asset supply mismatch") != std::string::npos); diff --git a/src/invariant/test/InvariantTests.cpp b/src/invariant/test/InvariantTests.cpp index ff68e4366f..1344c50551 100644 --- a/src/invariant/test/InvariantTests.cpp +++ b/src/invariant/test/InvariantTests.cpp @@ -371,35 +371,29 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") } // Make sure the entries have not been evicted - auto liveBL = app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); - auto hotArchive = app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); - REQUIRE(liveBL->load(LedgerEntryKey(tempEntry))); - REQUIRE(liveBL->load(LedgerEntryKey(persistentEntry))); - REQUIRE(liveBL->load(getTTLKey(tempEntry))); - REQUIRE(liveBL->load(getTTLKey(persistentEntry))); - REQUIRE(!hotArchive->load(LedgerEntryKey(tempEntry))); - REQUIRE(!hotArchive->load(LedgerEntryKey(persistentEntry))); + auto snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); + REQUIRE(snap.loadLiveEntry(LedgerEntryKey(tempEntry))); + REQUIRE(snap.loadLiveEntry(LedgerEntryKey(persistentEntry))); + REQUIRE(snap.loadLiveEntry(getTTLKey(tempEntry))); + REQUIRE(snap.loadLiveEntry(getTTLKey(persistentEntry))); + REQUIRE(!snap.loadArchiveEntry(LedgerEntryKey(tempEntry))); + REQUIRE(!snap.loadArchiveEntry(LedgerEntryKey(persistentEntry))); SECTION("Entries properly evicted") { closeLedger(*app); - liveBL = app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); - hotArchive = app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); - REQUIRE(!liveBL->load(LedgerEntryKey(tempEntry))); - REQUIRE(!liveBL->load(LedgerEntryKey(persistentEntry))); - REQUIRE(!liveBL->load(getTTLKey(tempEntry))); - REQUIRE(!liveBL->load(getTTLKey(persistentEntry))); - REQUIRE(!hotArchive->load(LedgerEntryKey(tempEntry))); - REQUIRE(hotArchive->load(LedgerEntryKey(persistentEntry))); + snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); + REQUIRE(!snap.loadLiveEntry(LedgerEntryKey(tempEntry))); + REQUIRE(!snap.loadLiveEntry(LedgerEntryKey(persistentEntry))); + REQUIRE(!snap.loadLiveEntry(getTTLKey(tempEntry))); + REQUIRE(!snap.loadLiveEntry(getTTLKey(persistentEntry))); + REQUIRE(!snap.loadArchiveEntry(LedgerEntryKey(tempEntry))); + REQUIRE(snap.loadArchiveEntry(LedgerEntryKey(persistentEntry))); } SECTION("invariant check") @@ -410,20 +404,21 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") auto ledgerVersion = lm.getLastClosedLedgerHeader().header.ledgerVersion; - auto snapshot = app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + app->getBucketManager() + .getBucketSnapshotManager() + .maybeUpdateLedgerStateSnapshot(snap); + // Manually trigger eviction so we can test the invariant directly LedgerTxn ltx(app->getLedgerTxnRoot()); ltx.loadHeader().current().ledgerSeq++; auto evictedState = - app->getBucketManager().resolveBackgroundEvictionScan(snapshot, - ltx, {}); + app->getBucketManager().resolveBackgroundEvictionScan(snap, ltx, + {}); + + app->getBucketManager() + .getBucketSnapshotManager() + .maybeUpdateLedgerStateSnapshot(snap); - auto hotArchiveSnap = - app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); // Persistent entry REQUIRE(evictedState.archivedEntries.size() == 1); // Temp entry, temp TTL, persistent TTL @@ -446,7 +441,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -464,7 +459,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -479,7 +474,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -499,7 +494,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -514,7 +509,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -534,7 +529,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -553,8 +548,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") { REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, - evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -562,8 +556,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") { REQUIRE_NOTHROW( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, - evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap)); } } @@ -574,7 +567,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") evictedState.deletedKeys.push_back(getTTLKey(liveTempEntry)); REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -584,7 +577,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") // Valid eviction should always pass UnorderedMap emptyMap; REQUIRE_NOTHROW(app->getInvariantManager().checkOnLedgerCommit( - snapshot, hotArchiveSnap, evictedState.archivedEntries, + snap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap)); } } @@ -613,16 +606,10 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") {}, {}); closeLedger(*app); - auto getLiveSnapshot = [&]() { - return app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); - }; - - auto getHotArchiveSnapshot = [&]() { + auto makeSnap = [&]() { return app->getBucketManager() .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); + .copyLedgerStateSnapshot(); }; auto noopIsStopping = []() { return false; }; @@ -632,8 +619,7 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") SECTION("Valid state passes invariant") { auto result = invariant.checkSnapshot( - getLiveSnapshot(), getHotArchiveSnapshot(), - lm.getInMemorySorobanStateForTesting(), noopIsStopping); + makeSnap(), lm.getInMemorySorobanStateForTesting(), noopIsStopping); REQUIRE(result.empty()); } @@ -653,8 +639,7 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") } auto result = - invariant.checkSnapshot(getLiveSnapshot(), getHotArchiveSnapshot(), - modifiedState, noopIsStopping); + invariant.checkSnapshot(makeSnap(), modifiedState, noopIsStopping); REQUIRE(!result.empty()); }; @@ -701,8 +686,7 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") } auto result = - invariant.checkSnapshot(getLiveSnapshot(), getHotArchiveSnapshot(), - modifiedState, noopIsStopping); + invariant.checkSnapshot(makeSnap(), modifiedState, noopIsStopping); REQUIRE(!result.empty()); }; @@ -743,8 +727,7 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") } auto result = - invariant.checkSnapshot(getLiveSnapshot(), getHotArchiveSnapshot(), - modifiedState, noopIsStopping); + invariant.checkSnapshot(makeSnap(), modifiedState, noopIsStopping); REQUIRE(!result.empty()); }; @@ -774,8 +757,7 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") InternalContractDataMapEntry(entryCopy, wrongTTL)); auto result = - invariant.checkSnapshot(getLiveSnapshot(), getHotArchiveSnapshot(), - modifiedState, noopIsStopping); + invariant.checkSnapshot(makeSnap(), modifiedState, noopIsStopping); REQUIRE(!result.empty()); } @@ -790,8 +772,7 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") *app, lm.getLastClosedLedgerHeader().header, {phantomTTL}, {}, {}); auto result = invariant.checkSnapshot( - getLiveSnapshot(), getHotArchiveSnapshot(), - lm.getInMemorySorobanStateForTesting(), noopIsStopping); + makeSnap(), lm.getInMemorySorobanStateForTesting(), noopIsStopping); REQUIRE(!result.empty()); } @@ -803,8 +784,7 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") *app, lm.getLastClosedLedgerHeader().header, {dataEntry1}, {}); auto result = invariant.checkSnapshot( - getLiveSnapshot(), getHotArchiveSnapshot(), - lm.getInMemorySorobanStateForTesting(), noopIsStopping); + makeSnap(), lm.getInMemorySorobanStateForTesting(), noopIsStopping); REQUIRE(!result.empty()); } } diff --git a/src/ledger/InMemorySorobanState.cpp b/src/ledger/InMemorySorobanState.cpp index c7d1b40565..29eae79429 100644 --- a/src/ledger/InMemorySorobanState.cpp +++ b/src/ledger/InMemorySorobanState.cpp @@ -4,6 +4,7 @@ #include "ledger/InMemorySorobanState.h" #include "bucket/BucketListSnapshot.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTypeUtils.h" #include "ledger/SorobanMetrics.h" #include "util/GlobalChecks.h" @@ -444,15 +445,18 @@ InMemorySorobanState::getTTL(LedgerKey const& ledgerKey) const void InMemorySorobanState::initializeStateFromSnapshot( - SearchableSnapshotConstPtr snap, uint32_t ledgerVersion) + LedgerStateSnapshot const& snap) { releaseAssertOrThrow(mContractDataEntries.empty()); releaseAssertOrThrow(mContractCodeEntries.empty()); releaseAssertOrThrow(mPendingTTLs.empty()); + auto const& lclHeader = snap.getLedgerHeader(); + auto ledgerVersion = lclHeader.ledgerVersion; if (protocolVersionStartsFrom(ledgerVersion, SOROBAN_PROTOCOL_VERSION)) { - auto sorobanConfig = SorobanNetworkConfig::loadFromLedger(snap); + LedgerSnapshot ls(snap); + auto sorobanConfig = SorobanNetworkConfig::loadFromLedger(ls); // Check if entry is a DEADENTRY and add it to deletedKeys. Otherwise, // check if the entry is shadowed by a DEADENTRY. std::unordered_set deletedKeys; @@ -519,12 +523,12 @@ InMemorySorobanState::initializeStateFromSnapshot( return Loop::INCOMPLETE; }; - snap->scanForEntriesOfType(CONTRACT_DATA, contractDataHandler); - snap->scanForEntriesOfType(TTL, ttlHandler); - snap->scanForEntriesOfType(CONTRACT_CODE, contractCodeHandler); + snap.scanLiveEntriesOfType(CONTRACT_DATA, contractDataHandler); + snap.scanLiveEntriesOfType(TTL, ttlHandler); + snap.scanLiveEntriesOfType(CONTRACT_CODE, contractCodeHandler); } - mLastClosedLedgerSeq = snap->getLedgerSeq(); + mLastClosedLedgerSeq = lclHeader.ledgerSeq; checkUpdateInvariants(); } diff --git a/src/ledger/InMemorySorobanState.h b/src/ledger/InMemorySorobanState.h index 385e494841..a070138324 100644 --- a/src/ledger/InMemorySorobanState.h +++ b/src/ledger/InMemorySorobanState.h @@ -10,8 +10,6 @@ #include #include -#include "bucket/BucketSnapshotManager.h" -#include "invariant/InvariantManagerImpl.h" #include "ledger/LedgerTypeUtils.h" #include "util/types.h" #include "xdr/Stellar-ledger-entries.h" @@ -19,6 +17,7 @@ namespace stellar { +class LedgerStateSnapshot; class InvariantManagerImpl; class SorobanMetrics; @@ -440,8 +439,7 @@ class InMemorySorobanState // is reading state when these functions are called. // Initialize the map from a bucket list snapshot - void initializeStateFromSnapshot(SearchableSnapshotConstPtr snap, - uint32_t ledgerVersion); + void initializeStateFromSnapshot(LedgerStateSnapshot const& snap); // Update the map with entries from a ledger close. ledgerSeq must be // exactly mLastClosedLedgerSeq + 1. diff --git a/src/ledger/LedgerManager.h b/src/ledger/LedgerManager.h index dbc885008d..918155a3bf 100644 --- a/src/ledger/LedgerManager.h +++ b/src/ledger/LedgerManager.h @@ -7,6 +7,7 @@ #include "catchup/LedgerApplyManager.h" #include "history/HistoryManager.h" #include "ledger/LedgerCloseMetaFrame.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/NetworkConfig.h" #include "main/ApplicationImpl.h" #include "rust/RustBridge.h" @@ -229,8 +230,8 @@ class LedgerManager virtual LedgerHeaderHistoryEntry const& getLastClosedLedgerHeader() const = 0; - // Get bucketlist snapshot of LCL - virtual SearchableSnapshotConstPtr getLastClosedSnapshot() const = 0; + // Get a LedgerStateSnapshot of the last closed ledger + virtual LedgerStateSnapshot getLastClosedSnapshot() const = 0; // return the HAS that corresponds to the last closed ledger as persisted in // the database @@ -278,8 +279,6 @@ class LedgerManager getLastClosedLedgerCloseMeta() = 0; virtual void storeCurrentLedgerForTest(LedgerHeader const& header) = 0; virtual InMemorySorobanState const& getInMemorySorobanStateForTesting() = 0; - virtual CompleteConstLedgerStatePtr - getLastClosedLedgerStateForTesting() = 0; virtual void rebuildInMemorySorobanStateForTesting(uint32_t ledgerVersion) = 0; virtual ::rust::Box diff --git a/src/ledger/LedgerManagerImpl.cpp b/src/ledger/LedgerManagerImpl.cpp index 33800cd267..4235830bfa 100644 --- a/src/ledger/LedgerManagerImpl.cpp +++ b/src/ledger/LedgerManagerImpl.cpp @@ -335,7 +335,7 @@ LedgerManagerImpl::LedgerManagerImpl(Application& app) : mApp(app) , mApplyState(app) , mLastClosedLedgerState(std::make_shared( - nullptr, nullptr, LedgerHeaderHistoryEntry(), HistoryArchiveState())) + LedgerHeaderHistoryEntry(), HistoryArchiveState())) , mLastClose(mApp.getClock().now()) , mCatchupDuration( app.getMetrics().NewTimer({"ledger", "catchup", "duration"})) @@ -472,11 +472,11 @@ LedgerManagerImpl::startNewLedger(LedgerHeader const& genesisLedger) CLOG_INFO(Ledger, "Root account seed: {}", skey.getStrKeySeed().value); auto& appConnector = mApp.getAppConnector(); - auto output = sealLedgerTxnAndStoreInBucketsAndDB( - appConnector.copySearchableLiveBucketListSnapshot(), - appConnector.copySearchableHotArchiveBucketListSnapshot(), ltx, - /*ledgerCloseMeta*/ nullptr, - /*initialLedgerVers*/ 0); + auto snap = appConnector.copyLedgerStateSnapshot(); + auto output = + sealLedgerTxnAndStoreInBucketsAndDB(snap, ltx, + /*ledgerCloseMeta*/ nullptr, + /*initialLedgerVers*/ 0); advanceLastClosedLedgerState(output); ltx.commit(); @@ -589,13 +589,12 @@ LedgerManagerImpl::loadLastKnownLedgerInternal(bool skipBuildingFullState) // Prime module cache with LCL state, not apply-state. This is acceptable // here because we just started and there is no apply-state yet and no apply // thread to hold such state. - auto const& snapshot = mLastClosedLedgerState->getBucketSnapshot(); if (!skipBuildingFullState) { + auto snap = getLastClosedSnapshot(); mApplyState.compileAllContractsInLedger( - snapshot, latestLedgerHeader->ledgerVersion); - mApplyState.populateInMemorySorobanState( - snapshot, latestLedgerHeader->ledgerVersion); + snap, latestLedgerHeader->ledgerVersion); + mApplyState.populateInMemorySorobanState(snap); } if (!skipBuildingFullState) @@ -772,24 +771,16 @@ LedgerManagerImpl::maybeRunSnapshotInvariantFromLedgerState( auto ledgerSeq = ledgerState->getLastClosedLedgerHeader().header.ledgerSeq; inMemorySnapshotForInvariant->assertLastClosedLedger(ledgerSeq); - // Copy snapshots from ledgerState to ensure consistency with the - // in-memory Soroban state - auto liveSnapshotCopy = - BucketSnapshotManager::copySearchableLiveBucketListSnapshot( - ledgerState->getBucketSnapshot(), mApp.getMetrics()); - auto hotArchiveSnapshotCopy = - BucketSnapshotManager::copySearchableHotArchiveBucketListSnapshot( - ledgerState->getHotArchiveSnapshot(), mApp.getMetrics()); - releaseAssertOrThrow(liveSnapshotCopy->getLedgerSeq() == ledgerSeq); - releaseAssertOrThrow(hotArchiveSnapshotCopy->getLedgerSeq() == ledgerSeq); + // TODO: Instead of creating a pointer from fresh state, just make a copy + // from the other LedgerStateSnapshot, when it is safe to do so next commit + auto snapPtr = + std::make_shared(ledgerState, mApp.getMetrics()); // Note: No race condition acquiring app by reference, as all worker // threads are joined before application destruction. - auto cb = [liveSnapshot = liveSnapshotCopy, - hotArchiveSnapshot = hotArchiveSnapshotCopy, &app = mApp, - inMemorySnapshotForInvariant]() { + auto cb = [snapPtr, &app = mApp, inMemorySnapshotForInvariant]() { app.getInvariantManager().runStateSnapshotInvariant( - liveSnapshot, hotArchiveSnapshot, *inMemorySnapshotForInvariant, + *snapPtr, *inMemorySnapshotForInvariant, [&app]() { return app.isStopping(); }); }; @@ -868,19 +859,13 @@ LedgerManagerImpl::getInMemorySorobanStateForTesting() return mApplyState.getInMemorySorobanStateForTesting(); } -CompleteConstLedgerStatePtr -LedgerManagerImpl::getLastClosedLedgerStateForTesting() -{ - return mLastClosedLedgerState; -} - void LedgerManagerImpl::rebuildInMemorySorobanStateForTesting(uint32_t ledgerVersion) { mApplyState.resetToSetupPhase(); mApplyState.getInMemorySorobanStateForTesting().clearForTesting(); - mApplyState.populateInMemorySorobanState( - mLastClosedLedgerState->getBucketSnapshot(), ledgerVersion); + auto snap = getLastClosedSnapshot(); + mApplyState.populateInMemorySorobanState(snap); mApplyState.markEndOfSetupPhase(); } @@ -982,19 +967,19 @@ LedgerManagerImpl::ApplyState::finishPendingCompilation() void LedgerManagerImpl::ApplyState::compileAllContractsInLedger( - SearchableSnapshotConstPtr snap, uint32_t minLedgerVersion) + LedgerStateSnapshot const& snap, uint32_t minLedgerVersion) { assertSetupPhase(); - startCompilingAllContracts(snap, minLedgerVersion); + startCompilingAllContracts(LedgerStateSnapshot(snap), minLedgerVersion); finishPendingCompilation(); } void LedgerManagerImpl::ApplyState::populateInMemorySorobanState( - SearchableSnapshotConstPtr snap, uint32_t ledgerVersion) + LedgerStateSnapshot const& snap) { assertSetupPhase(); - mInMemorySorobanState.initializeStateFromSnapshot(snap, ledgerVersion); + mInMemorySorobanState.initializeStateFromSnapshot(snap); } void @@ -1052,7 +1037,7 @@ LedgerManagerImpl::ApplyState::assertSetupPhase() const void LedgerManagerImpl::ApplyState::startCompilingAllContracts( - SearchableSnapshotConstPtr snap, uint32_t minLedgerVersion) + LedgerStateSnapshot snap, uint32_t minLedgerVersion) { threadInvariant(); // Always stop a previous compilation before starting a new one. Can only @@ -1067,7 +1052,7 @@ LedgerManagerImpl::ApplyState::startCompilingAllContracts( } } mCompiler = std::make_unique( - snap, mAppConnector.getMetrics(), mNumCompilationThreads, versions); + std::move(snap), mNumCompilationThreads, versions); mCompiler->start(); } @@ -1081,7 +1066,7 @@ LedgerManagerImpl::ApplyState::assertWritablePhase() const void LedgerManagerImpl::ApplyState::maybeRebuildModuleCache( - SearchableSnapshotConstPtr snap, uint32_t minLedgerVersion) + LedgerStateSnapshot const& snap, uint32_t minLedgerVersion) { assertCommittingPhase(); @@ -1120,7 +1105,8 @@ LedgerManagerImpl::ApplyState::maybeRebuildModuleCache( // linearTerm is in 1/128ths in the cost model, to reduce rounding error. uint64_t scale = 128; - auto sorobanConfig = SorobanNetworkConfig::loadFromLedger(snap); + LedgerSnapshot lsForConfig(snap); + auto sorobanConfig = SorobanNetworkConfig::loadFromLedger(lsForConfig); auto const& memParams = sorobanConfig.memCostParams(); if (memParams.size() > (size_t)stellar::VmInstantiation) { @@ -1140,7 +1126,8 @@ LedgerManagerImpl::ApplyState::maybeRebuildModuleCache( "Rebuilding module cache: worst-case estimate {} " "model-bytes consumed of {} limit", bytesConsumed, limit); - startCompilingAllContracts(snap, minLedgerVersion); + startCompilingAllContracts(LedgerStateSnapshot(snap), + minLedgerVersion); break; } } @@ -1694,10 +1681,9 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData, auto ledgerSeq = ltx.loadHeader().current().ledgerSeq; auto& appConnector = mApp.getAppConnector(); + auto lclSnap = appConnector.copyLedgerStateSnapshot(); auto appliedLedgerState = sealLedgerTxnAndStoreInBucketsAndDB( - appConnector.copySearchableLiveBucketListSnapshot(), - appConnector.copySearchableHotArchiveBucketListSnapshot(), ltx, - ledgerCloseMeta, initialLedgerVers); + lclSnap, ltx, ledgerCloseMeta, initialLedgerVers); // NB: from now on, the ledger state may not change, but LCL still hasn't // advanced properly. Hence when requesting the ledger state data (such as @@ -1800,14 +1786,13 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData, .header.ledgerVersion, SOROBAN_PROTOCOL_VERSION)) { - // Copy the snapshot directly from `appliedLedgerState`, which holds - // the latest committed state, to avoid relying on + // Construct a LedgerStateSnapshot from `appliedLedgerState`, which + // holds the latest committed state, to avoid relying on // BucketSnapshotManager. - auto latestSnapshot = - BucketSnapshotManager::copySearchableLiveBucketListSnapshot( - appliedLedgerState->getBucketSnapshot(), mApp.getMetrics()); + // TODO: Replease this with a copy once safe to do so + LedgerStateSnapshot snap(appliedLedgerState, mApp.getMetrics()); mApp.getBucketManager().startBackgroundEvictionScan( - latestSnapshot, appliedLedgerState->getSorobanConfig()); + std::move(snap), appliedLedgerState->getSorobanConfig()); } // At this point, we've committed all changes to the Apply State for this @@ -1890,9 +1875,9 @@ LedgerManagerImpl::setLastClosedLedger( // bucket state, there's no tx-apply state to snapshot, in this one // case we will prime the tx-apply-state's soroban module cache using // a snapshot _from_ the LCL state. - auto const& snapshot = mLastClosedLedgerState->getBucketSnapshot(); - mApplyState.compileAllContractsInLedger(snapshot, ledgerVersion); - mApplyState.populateInMemorySorobanState(snapshot, ledgerVersion); + auto snap = getLastClosedSnapshot(); + mApplyState.compileAllContractsInLedger(snap, ledgerVersion); + mApplyState.populateInMemorySorobanState(snap); } mApplyState.markEndOfSetupPhase(); } @@ -2048,12 +2033,12 @@ LedgerManagerImpl::maybeResetLedgerCloseMetaDebugStream(uint32_t ledgerSeq) } } -SearchableSnapshotConstPtr +LedgerStateSnapshot LedgerManagerImpl::getLastClosedSnapshot() const { releaseAssert(threadIsMain()); releaseAssert(mLastClosedLedgerState); - return mLastClosedLedgerState->getBucketSnapshot(); + return LedgerStateSnapshot(mLastClosedLedgerState, mApp.getMetrics()); } void @@ -2089,11 +2074,13 @@ LedgerManagerImpl::advanceBucketListSnapshotAndMakeLedgerState( bm.getBucketSnapshotManager().updateCurrentSnapshot( bm.getLiveBucketList(), bm.getHotArchiveBucketList(), header); - return std::make_shared( - bm.getBucketSnapshotManager().copySearchableLiveBucketListSnapshot(), - bm.getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(), - lcl, has); + auto state = std::make_shared( + bm.getLiveBucketList(), bm.getHotArchiveBucketList(), + bm.getBucketSnapshotManager().getLiveHistoricalSnapshots(), + bm.getBucketSnapshotManager().getHotArchiveHistoricalSnapshots(), lcl, + has, mApp.getMetrics()); + bm.getBucketSnapshotManager().setCompleteState(state); + return state; } } @@ -2318,8 +2305,6 @@ LedgerManagerImpl::applySorobanStageClustersInParallel( std::vector>> threadFutures; - auto liveSnapshot = app.copySearchableLiveBucketListSnapshot(); - DeactivateScopeGuard globalStateDeactivateGuard(globalState); for (size_t i = 0; i < stage.numClusters(); ++i) @@ -2827,9 +2812,7 @@ LedgerManagerImpl::storePersistentStateAndLedgerHeaderInDB( // NB: This is a separate method so a testing subclass can override it. void LedgerManagerImpl::finalizeLedgerTxnChanges( - SearchableSnapshotConstPtr lclSnapshot, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveSnapshot, - AbstractLedgerTxn& ltx, + LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, LedgerHeader lh, uint32_t initialLedgerVers) { @@ -2868,9 +2851,9 @@ LedgerManagerImpl::finalizeLedgerTxnChanges( } mApp.getInvariantManager().checkOnLedgerCommit( - lclSnapshot, lclHotArchiveSnapshot, - evictedState.archivedEntries, evictedState.deletedKeys, - restoredHotArchiveKeyMap, ltx.getRestoredLiveBucketListKeys()); + lclSnapshot, evictedState.archivedEntries, + evictedState.deletedKeys, restoredHotArchiveKeyMap, + ltx.getRestoredLiveBucketListKeys()); bool isP24UpgradeLedger = protocolVersionIsBefore(initialLedgerVers, @@ -2936,9 +2919,7 @@ LedgerManagerImpl::finalizeLedgerTxnChanges( CompleteConstLedgerStatePtr LedgerManagerImpl::sealLedgerTxnAndStoreInBucketsAndDB( - SearchableSnapshotConstPtr lclSnapshot, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveSnapshot, - AbstractLedgerTxn& ltx, + LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, uint32_t initialLedgerVers) { @@ -2974,8 +2955,8 @@ LedgerManagerImpl::sealLedgerTxnAndStoreInBucketsAndDB( // protocol version prior to the upgrade. Due to this, we must check the // initial protocol version of ledger instead of the ledger version of // the current ltx header, which may have been modified via an upgrade. - finalizeLedgerTxnChanges(lclSnapshot, lclHotArchiveSnapshot, ltx, - ledgerCloseMeta, ledgerHeader, initialLedgerVers); + finalizeLedgerTxnChanges(lclSnapshot, ltx, ledgerCloseMeta, ledgerHeader, + initialLedgerVers); CompleteConstLedgerStatePtr res; ltx.unsealHeader([this, &res](LedgerHeader& lh) { @@ -2989,8 +2970,8 @@ LedgerManagerImpl::sealLedgerTxnAndStoreInBucketsAndDB( if (protocolVersionStartsFrom( initialLedgerVers, REUSABLE_SOROBAN_MODULE_CACHE_PROTOCOL_VERSION)) { - mApplyState.maybeRebuildModuleCache(res->getBucketSnapshot(), - initialLedgerVers); + LedgerStateSnapshot snap(res, mApp.getMetrics()); + mApplyState.maybeRebuildModuleCache(snap, initialLedgerVers); } return res; diff --git a/src/ledger/LedgerManagerImpl.h b/src/ledger/LedgerManagerImpl.h index faa7ba5ffb..edbf2a1503 100644 --- a/src/ledger/LedgerManagerImpl.h +++ b/src/ledger/LedgerManagerImpl.h @@ -184,7 +184,7 @@ class LedgerManagerImpl : public LedgerManager // provided snapshot, for ledger protocols starting at minLedgerVersion // and running through to Config::CURRENT_LEDGER_PROTOCOL_VERSION (to // enable upgrades). - void startCompilingAllContracts(SearchableSnapshotConstPtr snap, + void startCompilingAllContracts(LedgerStateSnapshot snap, uint32_t minLedgerVersion); // Checks if ApplyState can currently be modified. For functions that @@ -236,13 +236,13 @@ class LedgerManagerImpl : public LedgerManager // Equivalent to calling `startCompilingAllContracts` followed by // `finishPendingCompilation`. - void compileAllContractsInLedger(SearchableSnapshotConstPtr snap, + void compileAllContractsInLedger(LedgerStateSnapshot const& snap, uint32_t minLedgerVersion); // Estimates the size of the arena underlying the module cache's shared // wasmi engine, from metrics, and rebuilds if it has likely built up a // lot of dead space inside of it. - void maybeRebuildModuleCache(SearchableSnapshotConstPtr snap, + void maybeRebuildModuleCache(LedgerStateSnapshot const& snap, uint32_t minLedgerVersion); // Evicts a single contract from the module cache, if it is present. @@ -258,8 +258,7 @@ class LedgerManagerImpl : public LedgerManager // Populates all live Soroban state into the cache from the provided // snapshot. - void populateInMemorySorobanState(SearchableSnapshotConstPtr snap, - uint32_t ledgerVersion); + void populateInMemorySorobanState(LedgerStateSnapshot const& snap); void handleUpgradeAffectingSorobanInMemoryStateSize( AbstractLedgerTxn& upgradeLtx); @@ -382,9 +381,7 @@ class LedgerManagerImpl : public LedgerManager // On the ledger in which a protocol upgrade from vN to vN + 1 occurs, // initialLedgerVers must be vN. CompleteConstLedgerStatePtr sealLedgerTxnAndStoreInBucketsAndDB( - SearchableSnapshotConstPtr lclSnapshot, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveSnapshot, - AbstractLedgerTxn& ltx, + LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, uint32_t initialLedgerVers); @@ -452,9 +449,7 @@ class LedgerManagerImpl : public LedgerManager // NB: LedgerHeader is a copy here to prevent footguns in case ltx // invalidates any header references virtual void finalizeLedgerTxnChanges( - SearchableSnapshotConstPtr lclSnapshot, - SearchableHotArchiveSnapshotConstPtr lclHotArchiveSnapshot, - AbstractLedgerTxn& ltx, + LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, LedgerHeader lh, uint32_t initialLedgerVers); @@ -514,7 +509,6 @@ class LedgerManagerImpl : public LedgerManager void storeCurrentLedgerForTest(LedgerHeader const& header) override; std::function mAdvanceLedgerStateAndPublishOverride; InMemorySorobanState const& getInMemorySorobanStateForTesting() override; - CompleteConstLedgerStatePtr getLastClosedLedgerStateForTesting() override; ::rust::Box getModuleCacheForTesting() override; void rebuildInMemorySorobanStateForTesting(uint32_t ledgerVersion) override; @@ -560,7 +554,7 @@ class LedgerManagerImpl : public LedgerManager void maybeResetLedgerCloseMetaDebugStream(uint32_t ledgerSeq); SorobanMetrics& getSorobanMetrics() override; - SearchableSnapshotConstPtr getLastClosedSnapshot() const override; + LedgerStateSnapshot getLastClosedSnapshot() const override; virtual bool isApplying() const override { diff --git a/src/ledger/LedgerStateSnapshot.cpp b/src/ledger/LedgerStateSnapshot.cpp index c1a78b9ef2..13a61f2ab8 100644 --- a/src/ledger/LedgerStateSnapshot.cpp +++ b/src/ledger/LedgerStateSnapshot.cpp @@ -5,7 +5,6 @@ #include "ledger/LedgerStateSnapshot.h" #include "bucket/BucketManager.h" #include "bucket/BucketSnapshotManager.h" -#include "ledger/LedgerManager.h" #include "ledger/LedgerTxn.h" #include "main/Application.h" #include "transactions/TransactionFrame.h" @@ -168,10 +167,10 @@ LedgerTxnReadOnly::executeWithMaybeInnerSnapshot( return f(lsg); } -BucketSnapshotState::BucketSnapshotState(SearchableSnapshotConstPtr snapshot) - : mSnapshot(snapshot) +BucketSnapshotState::BucketSnapshotState(LedgerStateSnapshot const& snap) + : mSnapshot(snap) , mLedgerHeader(LedgerHeaderWrapper( - std::make_shared(mSnapshot->getLedgerHeader()))) + std::make_shared(snap.getLedgerHeader()))) { } @@ -188,7 +187,7 @@ BucketSnapshotState::getLedgerHeader() const LedgerEntryWrapper BucketSnapshotState::getAccount(AccountID const& account) const { - return LedgerEntryWrapper(mSnapshot->load(accountKey(account))); + return LedgerEntryWrapper(mSnapshot.loadLiveEntry(accountKey(account))); } LedgerEntryWrapper @@ -209,7 +208,7 @@ BucketSnapshotState::getAccount(LedgerHeaderWrapper const& header, LedgerEntryWrapper BucketSnapshotState::load(LedgerKey const& key) const { - return LedgerEntryWrapper(mSnapshot->load(key)); + return LedgerEntryWrapper(mSnapshot.loadLiveEntry(key)); } void @@ -240,12 +239,16 @@ LedgerSnapshot::LedgerSnapshot(Application& app) } else #endif - mGetter = std::make_unique( - app.getLedgerManager().getLastClosedSnapshot()); + { + auto snap = app.getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); + mGetter = std::make_unique(snap); + } } -LedgerSnapshot::LedgerSnapshot(SearchableSnapshotConstPtr snapshot) - : mGetter(std::make_unique(snapshot)) +LedgerSnapshot::LedgerSnapshot(LedgerStateSnapshot const& snap) + : mGetter(std::make_unique(snap)) { } @@ -279,25 +282,54 @@ CompleteConstLedgerState::checkInvariant() const { releaseAssert(mLastClosedHistoryArchiveState.currentLedger == mLastClosedLedgerHeader.header.ledgerSeq); + // Initial placeholder state (ledgerSeq 0) may have null bucket data + // TODO: Get rid of this since it's kinda sketchy if (mLastClosedLedgerHeader.header.ledgerSeq > 0) { - releaseAssert(mBucketSnapshot->getLedgerHeader() == - mLastClosedLedgerHeader.header); + releaseAssert(mLiveBucketData); + releaseAssert(mHotArchiveBucketData); } } CompleteConstLedgerState::CompleteConstLedgerState( - SearchableSnapshotConstPtr searchableSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, + BucketListBase const& liveBL, + BucketListBase const& hotArchiveBL, + std::map const>> + liveHistorical, + std::map const>> + hotArchiveHistorical, LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, - HistoryArchiveState const& lastClosedHistoryArchiveState) - : mBucketSnapshot(searchableSnapshot) - , mHotArchiveSnapshot(hotArchiveSnapshot) + HistoryArchiveState const& lastClosedHistoryArchiveState, + MetricsRegistry& metrics) + : mLiveBucketData( + std::make_shared>(liveBL)) + , mLiveHistoricalSnapshots(std::move(liveHistorical)) + , mHotArchiveBucketData( + std::make_shared>( + hotArchiveBL)) + , mHotArchiveHistoricalSnapshots(std::move(hotArchiveHistorical)) , mSorobanConfig( protocolVersionStartsFrom(lastClosedLedgerHeader.header.ledgerVersion, SOROBAN_PROTOCOL_VERSION) - ? std::make_optional( - SorobanNetworkConfig::loadFromLedger(searchableSnapshot)) + ? std::make_optional([&]() { + // Create a temporary CompleteConstLedgerState (without + // Soroban config) to serve as the canonical state for the + // LedgerStateSnapshot used during config loading. + // TODO: Sketchy, please remove + auto tempState = + std::shared_ptr( + new CompleteConstLedgerState( + mLiveBucketData, mLiveHistoricalSnapshots, + mHotArchiveBucketData, + mHotArchiveHistoricalSnapshots, + lastClosedLedgerHeader, + lastClosedHistoryArchiveState)); + LedgerStateSnapshot tempSnap(tempState, metrics); + LedgerSnapshot ls(tempSnap); + return SorobanNetworkConfig::loadFromLedger(ls); + }()) : std::nullopt) , mLastClosedLedgerHeader(lastClosedLedgerHeader) , mLastClosedHistoryArchiveState(lastClosedHistoryArchiveState) @@ -305,16 +337,41 @@ CompleteConstLedgerState::CompleteConstLedgerState( checkInvariant(); } -SearchableSnapshotConstPtr -CompleteConstLedgerState::getBucketSnapshot() const +CompleteConstLedgerState::CompleteConstLedgerState( + LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, + HistoryArchiveState const& lastClosedHistoryArchiveState) + : mLiveBucketData(nullptr) + , mLiveHistoricalSnapshots() + , mHotArchiveBucketData(nullptr) + , mHotArchiveHistoricalSnapshots() + , mSorobanConfig(std::nullopt) + , mLastClosedLedgerHeader(lastClosedLedgerHeader) + , mLastClosedHistoryArchiveState(lastClosedHistoryArchiveState) { - return mBucketSnapshot; + checkInvariant(); } -SearchableHotArchiveSnapshotConstPtr -CompleteConstLedgerState::getHotArchiveSnapshot() const +CompleteConstLedgerState::CompleteConstLedgerState( + std::shared_ptr const> liveBucketData, + std::map const>> + liveHistorical, + std::shared_ptr const> + hotArchiveBucketData, + std::map const>> + hotArchiveHistorical, + LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, + HistoryArchiveState const& lastClosedHistoryArchiveState) + : mLiveBucketData(std::move(liveBucketData)) + , mLiveHistoricalSnapshots(std::move(liveHistorical)) + , mHotArchiveBucketData(std::move(hotArchiveBucketData)) + , mHotArchiveHistoricalSnapshots(std::move(hotArchiveHistorical)) + , mSorobanConfig(std::nullopt) + , mLastClosedLedgerHeader(lastClosedLedgerHeader) + , mLastClosedHistoryArchiveState(lastClosedHistoryArchiveState) { - return mHotArchiveSnapshot; + checkInvariant(); } SorobanNetworkConfig const& @@ -341,4 +398,128 @@ CompleteConstLedgerState::getLastClosedHistoryArchiveState() const return mLastClosedHistoryArchiveState; } +LedgerStateSnapshot::LedgerStateSnapshot(CompleteConstLedgerStatePtr state, + MetricsRegistry& metrics) + : mState(state) + , mLiveSnapshot(std::shared_ptr( + new SearchableLiveBucketListSnapshot( + metrics, state->mLiveBucketData, state->mLiveHistoricalSnapshots, + state->mLastClosedLedgerHeader.header.ledgerSeq))) + , mHotArchiveSnapshot( + std::shared_ptr( + new SearchableHotArchiveBucketListSnapshot( + metrics, state->mHotArchiveBucketData, + state->mHotArchiveHistoricalSnapshots, + state->mLastClosedLedgerHeader.header.ledgerSeq))) + , mMetrics(metrics) +{ +} + +CompleteConstLedgerState const& +LedgerStateSnapshot::getState() const +{ + releaseAssert(mState); + return *mState; +} + +LedgerHeader const& +LedgerStateSnapshot::getLedgerHeader() const +{ + return mState->getLastClosedLedgerHeader().header; +} + +uint32_t +LedgerStateSnapshot::getLedgerSeq() const +{ + return mState->getLastClosedLedgerHeader().header.ledgerSeq; +} + +// === Live BucketList wrapper methods === + +std::shared_ptr +LedgerStateSnapshot::loadLiveEntry(LedgerKey const& k) const +{ + return mLiveSnapshot->load(k); +} + +std::vector +LedgerStateSnapshot::loadLiveKeys( + std::set const& inKeys, + std::string const& label) const +{ + return mLiveSnapshot->loadKeys(inKeys, label); +} + +std::optional> +LedgerStateSnapshot::loadLiveKeysFromLedger( + std::set const& inKeys, + uint32_t ledgerSeq) const +{ + return mLiveSnapshot->loadKeysFromLedger(inKeys, ledgerSeq); +} + +std::vector +LedgerStateSnapshot::loadPoolShareTrustLinesByAccountAndAsset( + AccountID const& accountID, Asset const& asset) const +{ + return mLiveSnapshot->loadPoolShareTrustLinesByAccountAndAsset(accountID, + asset); +} + +std::vector +LedgerStateSnapshot::loadInflationWinners(size_t maxWinners, + int64_t minBalance) const +{ + return mLiveSnapshot->loadInflationWinners(maxWinners, minBalance); +} + +std::unique_ptr +LedgerStateSnapshot::scanForEviction(uint32_t ledgerSeq, + EvictionMetrics& metrics, + EvictionIterator iter, + std::shared_ptr stats, + StateArchivalSettings const& sas, + uint32_t ledgerVers) const +{ + return mLiveSnapshot->scanForEviction(ledgerSeq, metrics, std::move(iter), + std::move(stats), sas, ledgerVers); +} + +void +LedgerStateSnapshot::scanLiveEntriesOfType( + LedgerEntryType type, + std::function callback) const +{ + mLiveSnapshot->scanForEntriesOfType(type, std::move(callback)); +} + +// === Hot Archive BucketList wrapper methods === + +std::shared_ptr +LedgerStateSnapshot::loadArchiveEntry(LedgerKey const& k) const +{ + return mHotArchiveSnapshot->load(k); +} + +std::vector +LedgerStateSnapshot::loadArchiveKeys( + std::set const& inKeys) const +{ + return mHotArchiveSnapshot->loadKeys(inKeys); +} + +std::optional> +LedgerStateSnapshot::loadArchiveKeysFromLedger( + std::set const& inKeys, + uint32_t ledgerSeq) const +{ + return mHotArchiveSnapshot->loadKeysFromLedger(inKeys, ledgerSeq); +} + +void +LedgerStateSnapshot::scanAllArchiveEntries( + std::function callback) const +{ + mHotArchiveSnapshot->scanAllEntries(std::move(callback)); +} } diff --git a/src/ledger/LedgerStateSnapshot.h b/src/ledger/LedgerStateSnapshot.h index cbbc5fb6ec..0978515505 100644 --- a/src/ledger/LedgerStateSnapshot.h +++ b/src/ledger/LedgerStateSnapshot.h @@ -5,11 +5,11 @@ #pragma once #include "bucket/BucketListSnapshot.h" -#include "bucket/BucketSnapshotManager.h" #include "history/HistoryArchive.h" #include "ledger/LedgerTxn.h" #include "ledger/NetworkConfig.h" #include "util/NonCopyable.h" +#include #include namespace stellar @@ -19,6 +19,12 @@ class Application; class TransactionFrame; class LedgerSnapshot; class CompleteConstLedgerState; +class EvictionStatistics; +struct EvictionMetrics; +struct EvictionResultCandidates; +struct InflationWinner; +struct StateArchivalSettings; +template class BucketListBase; // NB: we can't use unique_ptr here, because this object gets passed to a // lambda, and std::function requires its callable to be copyable (C++23 fixes @@ -112,17 +118,90 @@ class LedgerTxnReadOnly : public AbstractLedgerStateSnapshot std::function f) const override; }; +// A copyable value type that provides searchable access to a +// CompleteConstLedgerState. Each instance maintains its own file stream cache +// for bucket I/O. Multiple LedgerStateSnapshot instances can safely wrap the +// same CompleteConstLedgerState. +class LedgerStateSnapshot +{ + std::shared_ptr mState; + std::shared_ptr mLiveSnapshot; + std::shared_ptr + mHotArchiveSnapshot; + std::reference_wrapper mMetrics; + + public: + // Construct from CompleteConstLedgerState + explicit LedgerStateSnapshot(CompleteConstLedgerStatePtr state, + MetricsRegistry& metrics); + + // Default copy/move + // TODO: This is currently NOT thread sade, since we just do pointer copies + // for the state snapshots. Next commit will turn the snapshots into value + // types with correct copy semantics. + LedgerStateSnapshot(LedgerStateSnapshot const&) = default; + LedgerStateSnapshot& operator=(LedgerStateSnapshot const&) = default; + LedgerStateSnapshot(LedgerStateSnapshot&&) = default; + LedgerStateSnapshot& operator=(LedgerStateSnapshot&&) = default; + + CompleteConstLedgerState const& getState() const; + LedgerHeader const& getLedgerHeader() const; + uint32_t getLedgerSeq() const; + + // === Live BucketList methods === + std::shared_ptr loadLiveEntry(LedgerKey const& k) const; + std::vector + loadLiveKeys(std::set const& inKeys, + std::string const& label) const; + std::optional> + loadLiveKeysFromLedger(std::set const& inKeys, + uint32_t ledgerSeq) const; + std::vector + loadPoolShareTrustLinesByAccountAndAsset(AccountID const& accountID, + Asset const& asset) const; + std::vector loadInflationWinners(size_t maxWinners, + int64_t minBalance) const; + std::unique_ptr scanForEviction( + uint32_t ledgerSeq, EvictionMetrics& metrics, EvictionIterator iter, + std::shared_ptr stats, + StateArchivalSettings const& sas, uint32_t ledgerVers) const; + void scanLiveEntriesOfType( + LedgerEntryType type, + std::function callback) const; + + // === Hot Archive BucketList methods === + std::shared_ptr + loadArchiveEntry(LedgerKey const& k) const; + std::vector + loadArchiveKeys(std::set const& inKeys) const; + std::optional> loadArchiveKeysFromLedger( + std::set const& inKeys, + uint32_t ledgerSeq) const; + void scanAllArchiveEntries( + std::function callback) const; + + private: + friend class CompleteConstLedgerState; + + // TODO: Remove + friend class BucketSnapshotState; + friend class LedgerSnapshot; + friend class LedgerManagerImpl; +}; + // A concrete implementation of read-only BucketList snapshot wrapper class BucketSnapshotState : public AbstractLedgerStateSnapshot { - SearchableSnapshotConstPtr const mSnapshot; - // Store a copy of the header from mSnapshot. This is needed for - // validation flow where for certain validation scenarios the header needs - // to be modified + LedgerStateSnapshot mSnapshot; + // Store a copy of the header. This is needed for validation flow where + // for certain validation scenarios the header needs to be modified. LedgerHeaderWrapper mLedgerHeader; + // TODO: Remove + friend class LedgerSnapshot; + public: - BucketSnapshotState(SearchableSnapshotConstPtr snapshot); + explicit BucketSnapshotState(LedgerStateSnapshot const& snap); ~BucketSnapshotState() override; LedgerHeaderWrapper getLedgerHeader() const override; @@ -152,7 +231,7 @@ class LedgerSnapshot : public NonMovableOrCopyable public: LedgerSnapshot(AbstractLedgerTxn& ltx); LedgerSnapshot(Application& app); - explicit LedgerSnapshot(SearchableSnapshotConstPtr snapshot); + explicit LedgerSnapshot(LedgerStateSnapshot const& snap); LedgerHeaderWrapper getLedgerHeader() const; LedgerEntryWrapper getAccount(AccountID const& account) const; LedgerEntryWrapper @@ -195,23 +274,71 @@ class LedgerSnapshot : public NonMovableOrCopyable class CompleteConstLedgerState : public NonMovableOrCopyable { private: - SearchableSnapshotConstPtr const mBucketSnapshot; - SearchableHotArchiveSnapshotConstPtr const mHotArchiveSnapshot; + // Raw immutable bucket data for the live and hot archive bucket lists + std::shared_ptr const> const + mLiveBucketData; + std::map const>> const + mLiveHistoricalSnapshots; + std::shared_ptr const> const + mHotArchiveBucketData; + std::map< + uint32_t, + std::shared_ptr const>> const + mHotArchiveHistoricalSnapshots; + std::optional const mSorobanConfig; LedgerHeaderHistoryEntry const mLastClosedLedgerHeader; HistoryArchiveState const mLastClosedHistoryArchiveState; void checkInvariant() const; + // Private constructor: creates a state without Soroban config, used as a + // temporary during the main constructor's config loading to break the + // circular dependency. + CompleteConstLedgerState( + std::shared_ptr const> liveBucketData, + std::map const>> + liveHistorical, + std::shared_ptr const> + hotArchiveBucketData, + std::map const>> + hotArchiveHistorical, + LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, + HistoryArchiveState const& lastClosedHistoryArchiveState); + + // LedgerStateSnapshot constructs searchable snapshots from our raw data + // TODO: Remove + friend class LedgerStateSnapshot; + // BucketSnapshotManager uses the private constructor to rebuild + // mCompleteState in updateCurrentSnapshot without loading Soroban config. + friend class BucketSnapshotManager; + public: + // Construct from raw bucket lists. Creates BucketListSnapshotData + // internally. Historical snapshots and metrics are needed for + // SorobanNetworkConfig loading. + CompleteConstLedgerState( + BucketListBase const& liveBL, + BucketListBase const& hotArchiveBL, + std::map const>> + liveHistorical, + std::map const>> + hotArchiveHistorical, + LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, + HistoryArchiveState const& lastClosedHistoryArchiveState, + MetricsRegistry& metrics); + + // Initial empty state constructor (ledger 0, no bucket data) + // TODO: Make less sketchy CompleteConstLedgerState( - SearchableSnapshotConstPtr searchableSnapshot, - SearchableHotArchiveSnapshotConstPtr hotArchiveSnapshot, LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, HistoryArchiveState const& lastClosedHistoryArchiveState); - SearchableSnapshotConstPtr getBucketSnapshot() const; - SearchableHotArchiveSnapshotConstPtr getHotArchiveSnapshot() const; SorobanNetworkConfig const& getSorobanConfig() const; bool hasSorobanConfig() const; LedgerHeaderHistoryEntry const& getLastClosedLedgerHeader() const; diff --git a/src/ledger/LedgerTxn.cpp b/src/ledger/LedgerTxn.cpp index 5523cd2c5d..b5f81feb2f 100644 --- a/src/ledger/LedgerTxn.cpp +++ b/src/ledger/LedgerTxn.cpp @@ -5,6 +5,7 @@ #include "ledger/LedgerTxn.h" #include "bucket/BucketListSnapshot.h" #include "bucket/BucketManager.h" +#include "bucket/BucketSnapshotManager.h" #include "crypto/KeyUtils.h" #include "database/Database.h" #include "ledger/InMemorySorobanState.h" @@ -2962,8 +2963,8 @@ LedgerTxnRoot::Impl::commitChild(EntryIterator iter, mPrefetchHits = 0; mPrefetchMisses = 0; - // std::shared_ptr<...>::reset does not throw - mSearchableBucketListSnapshot.reset(); + // std::optional<...>::reset does not throw + mLedgerStateSnapshot.reset(); mThreadInvariant.clearActiveThread(); } @@ -3070,8 +3071,8 @@ LedgerTxnRoot::Impl::prefetch(UnorderedSet const& keys) { insertIfNotLoaded(keysToSearch, key); } - auto blLoad = getSearchableLiveBucketListSnapshot().loadKeys(keysToSearch, - "prefetch"); + auto blLoad = + getLedgerStateSnapshot().loadLiveKeys(keysToSearch, "prefetch"); cacheResult(populateLoadedEntries(keysToSearch, blLoad)); return total; @@ -3342,18 +3343,17 @@ LedgerTxnRoot::Impl::areEntriesMissingInCacheForOffer(OfferEntry const& oe) return false; } -SearchableLiveBucketListSnapshot const& -LedgerTxnRoot::Impl::getSearchableLiveBucketListSnapshot() const +LedgerStateSnapshot const& +LedgerTxnRoot::Impl::getLedgerStateSnapshot() const { - if (!mSearchableBucketListSnapshot) + if (!mLedgerStateSnapshot) { - mSearchableBucketListSnapshot = - mApp.getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + mLedgerStateSnapshot = mApp.getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); } - return *mSearchableBucketListSnapshot; + return *mLedgerStateSnapshot; } std::shared_ptr @@ -3489,8 +3489,8 @@ LedgerTxnRoot::Impl::getPoolShareTrustLinesByAccountAndAsset( try { trustLines = - getSearchableLiveBucketListSnapshot() - .loadPoolShareTrustLinesByAccountAndAsset(account, asset); + getLedgerStateSnapshot().loadPoolShareTrustLinesByAccountAndAsset( + account, asset); } catch (std::exception& e) { @@ -3540,8 +3540,8 @@ LedgerTxnRoot::Impl::getInflationWinners(size_t maxWinners, int64_t minVotes) { try { - return getSearchableLiveBucketListSnapshot().loadInflationWinners( - maxWinners, minVotes); + return getLedgerStateSnapshot().loadInflationWinners(maxWinners, + minVotes); } catch (std::exception& e) { @@ -3626,7 +3626,7 @@ LedgerTxnRoot::Impl::getNewestVersion(InternalLedgerKey const& gkey) const } else { - entry = getSearchableLiveBucketListSnapshot().load(key); + entry = getLedgerStateSnapshot().loadLiveEntry(key); } } catch (std::exception& e) @@ -3695,7 +3695,7 @@ LedgerTxnRoot::Impl::rollbackChild() noexcept mChild = nullptr; mPrefetchHits = 0; mPrefetchMisses = 0; - mSearchableBucketListSnapshot.reset(); + mLedgerStateSnapshot.reset(); mThreadInvariant.clearActiveThread(); } diff --git a/src/ledger/LedgerTxnImpl.h b/src/ledger/LedgerTxnImpl.h index 7e9c9b1e3d..4013775156 100644 --- a/src/ledger/LedgerTxnImpl.h +++ b/src/ledger/LedgerTxnImpl.h @@ -6,6 +6,7 @@ #include "bucket/BucketSnapshotManager.h" #include "database/Database.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "util/RandomEvictionCache.h" #include "util/UnorderedSet.h" @@ -620,7 +621,7 @@ class LedgerTxnRoot::Impl mutable BestOffers mBestOffers; mutable uint64_t mPrefetchHits{0}; mutable uint64_t mPrefetchMisses{0}; - mutable SearchableSnapshotConstPtr mSearchableBucketListSnapshot; + mutable std::optional mLedgerStateSnapshot; size_t mBulkLoadBatchSize; std::unique_ptr mTransaction; @@ -689,8 +690,7 @@ class LedgerTxnRoot::Impl bool areEntriesMissingInCacheForOffer(OfferEntry const& oe); - SearchableLiveBucketListSnapshot const& - getSearchableLiveBucketListSnapshot() const; + LedgerStateSnapshot const& getLedgerStateSnapshot() const; public: // Constructor has the strong exception safety guarantee diff --git a/src/ledger/NetworkConfig.cpp b/src/ledger/NetworkConfig.cpp index f127ea2dc2..1f8500497c 100644 --- a/src/ledger/NetworkConfig.cpp +++ b/src/ledger/NetworkConfig.cpp @@ -1695,13 +1695,6 @@ SorobanNetworkConfig::loadFromLedger(LedgerSnapshot const& ls) return config; } -SorobanNetworkConfig -SorobanNetworkConfig::loadFromLedger(SearchableSnapshotConstPtr snapshot) -{ - LedgerSnapshot ls(snapshot); - return SorobanNetworkConfig::loadFromLedger(ls); -} - SorobanNetworkConfig SorobanNetworkConfig::loadFromLedger(AbstractLedgerTxn& ltx) { diff --git a/src/ledger/NetworkConfig.h b/src/ledger/NetworkConfig.h index 0999d57311..cd561396d6 100644 --- a/src/ledger/NetworkConfig.h +++ b/src/ledger/NetworkConfig.h @@ -262,8 +262,6 @@ class SorobanNetworkConfig public: // Static factory function to create a SorobanNetworkConfig from ledger static SorobanNetworkConfig loadFromLedger(LedgerSnapshot const& ls); - static SorobanNetworkConfig - loadFromLedger(SearchableSnapshotConstPtr snapshot); static SorobanNetworkConfig loadFromLedger(AbstractLedgerTxn& ltx); #ifdef BUILD_TESTS diff --git a/src/ledger/P23HotArchiveBug.cpp b/src/ledger/P23HotArchiveBug.cpp index 6a6d66f09a..de312a29ac 100644 --- a/src/ledger/P23HotArchiveBug.cpp +++ b/src/ledger/P23HotArchiveBug.cpp @@ -49,8 +49,7 @@ addHotArchiveBatchWithP23HotArchiveFix( auto updatedArchivedEntries = archivedEntries; updatedArchivedEntries.reserve(updatedArchivedEntries.size() + P23_CORRUPTED_HOT_ARCHIVE_ENTRIES_COUNT); - auto const& hotArchiveSnapshot = - app.getAppConnector().copySearchableHotArchiveBucketListSnapshot(); + auto snap = app.getAppConnector().copyLedgerStateSnapshot(); for (size_t i = 0; i < P23_CORRUPTED_HOT_ARCHIVE_ENTRIES_COUNT; ++i) { LedgerEntry corruptedEntry = @@ -68,7 +67,7 @@ addHotArchiveBatchWithP23HotArchiveFix( // Hot Archive that match our expectations for the corrupted entries. // Ensure that the entry exists in Hot Archive. - auto hotArchiveEntry = hotArchiveSnapshot->load(corruptedEntryKey); + auto hotArchiveEntry = snap.loadArchiveEntry(corruptedEntryKey); if (!hotArchiveEntry) { CLOG_WARNING( @@ -351,9 +350,9 @@ Protocol23CorruptionDataVerifier::verifyArchivalOfCorruptedEntry( // This database can load the actual, correct version of a // given ledger key. This tells us the value that should // have been evicted. - auto liveDatabase = app.getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + auto snap = app.getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); // This is the set of all keys incorrectly evicted for this // ledger @@ -368,7 +367,7 @@ Protocol23CorruptionDataVerifier::verifyArchivalOfCorruptedEntry( { // Load the correct value from the live database. auto evictedLedgerKey = LedgerEntryKey(evictedEntry); - auto databaseEntry = liveDatabase->load(evictedLedgerKey); + auto databaseEntry = snap.loadLiveEntry(evictedLedgerKey); releaseAssert(databaseEntry != nullptr); // If there was a corruption diff --git a/src/ledger/SharedModuleCacheCompiler.cpp b/src/ledger/SharedModuleCacheCompiler.cpp index 3158c31459..3b483d99c0 100644 --- a/src/ledger/SharedModuleCacheCompiler.cpp +++ b/src/ledger/SharedModuleCacheCompiler.cpp @@ -3,7 +3,6 @@ // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 #include "ledger/SharedModuleCacheCompiler.h" -#include "bucket/BucketListSnapshot.h" #include "crypto/Hex.h" #include "crypto/SHA.h" #include "rust/RustBridge.h" @@ -19,14 +18,13 @@ namespace stellar size_t const SharedModuleCacheCompiler::BUFFERED_WASM_CAPACITY = 100 * 1024 * 1024; -// The snapshot is copied here to ensure the background loading thread has its -// own instance since snapshots themselves aren't thread safe. +// The snapshot is passed by copy here to ensure the background loading thread +// has its own instance since snapshots themselves aren't thread safe. SharedModuleCacheCompiler::SharedModuleCacheCompiler( - SearchableSnapshotConstPtr snap, MetricsRegistry& metrics, - size_t numThreads, std::vector const& ledgerVersions) + LedgerStateSnapshot snap, size_t numThreads, + std::vector const& ledgerVersions) : mModuleCache(rust_bridge::new_module_cache()) - , mSnap(BucketSnapshotManager::copySearchableLiveBucketListSnapshot( - snap, metrics)) + , mSnap(std::move(snap)) , mNumThreads(numThreads) , mLedgerVersions(ledgerVersions) , mStarted(std::chrono::steady_clock::now()) @@ -150,7 +148,7 @@ SharedModuleCacheCompiler::start() std::unordered_set seenContracts; size_t liveContracts{0}; // Note: this access is safe since we only have a single loading thread. - this->mSnap->scanForEntriesOfType( + this->mSnap.scanLiveEntriesOfType( CONTRACT_CODE, [&](BucketEntry const& entry) { Hash h; switch (entry.type()) diff --git a/src/ledger/SharedModuleCacheCompiler.h b/src/ledger/SharedModuleCacheCompiler.h index 40e7d71804..3ca8c69882 100644 --- a/src/ledger/SharedModuleCacheCompiler.h +++ b/src/ledger/SharedModuleCacheCompiler.h @@ -4,7 +4,7 @@ #pragma once -#include "bucket/BucketSnapshotManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "rust/RustBridge.h" #include "util/NonCopyable.h" #include "xdrpp/types.h" @@ -25,7 +25,7 @@ namespace stellar class SharedModuleCacheCompiler : NonMovableOrCopyable { ::rust::Box mModuleCache; - stellar::SearchableSnapshotConstPtr mSnap; + LedgerStateSnapshot mSnap; std::deque> mWasms; size_t const mNumThreads; @@ -55,8 +55,7 @@ class SharedModuleCacheCompiler : NonMovableOrCopyable bool popAndCompileWasm(size_t thread, std::unique_lock& lock); public: - SharedModuleCacheCompiler(SearchableSnapshotConstPtr snap, - MetricsRegistry& metrics, size_t numThreads, + SharedModuleCacheCompiler(LedgerStateSnapshot snap, size_t numThreads, std::vector const& ledgerVersions); ~SharedModuleCacheCompiler(); void start(); diff --git a/src/main/AppConnector.cpp b/src/main/AppConnector.cpp index 249277e85f..0911d5c532 100644 --- a/src/main/AppConnector.cpp +++ b/src/main/AppConnector.cpp @@ -1,5 +1,6 @@ #include "main/AppConnector.h" #include "bucket/BucketManager.h" +#include "bucket/BucketSnapshotManager.h" #include "herder/Herder.h" #include "invariant/InvariantManager.h" #include "ledger/LedgerManager.h" @@ -169,50 +170,23 @@ AppConnector::threadIsType(Application::ThreadType type) const return mApp.threadIsType(type); } -SearchableHotArchiveSnapshotConstPtr -AppConnector::copySearchableHotArchiveBucketListSnapshot() +LedgerStateSnapshot +AppConnector::copyLedgerStateSnapshot() { return mApp.getBucketManager() .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); -} - -SearchableSnapshotConstPtr -AppConnector::copySearchableLiveBucketListSnapshot() -{ - return mApp.getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); -} - -void -AppConnector::maybeCopySearchableBucketListSnapshot( - SearchableSnapshotConstPtr& snapshot) -{ - mApp.getBucketManager() - .getBucketSnapshotManager() - .maybeCopySearchableBucketListSnapshot(snapshot); + .copyLedgerStateSnapshot(); } void -AppConnector::maybeCopyLiveAndHotArchiveSnapshots( - SearchableSnapshotConstPtr& liveSnapshot, - SearchableHotArchiveSnapshotConstPtr& hotArchiveSnapshot) +AppConnector::maybeUpdateLedgerStateSnapshot(LedgerStateSnapshot& snapshot) { mApp.getBucketManager() .getBucketSnapshotManager() - .maybeCopyLiveAndHotArchiveSnapshots(liveSnapshot, hotArchiveSnapshot); -} - -std::pair -AppConnector::copySearchableBucketListSnapshots() -{ - return mApp.getBucketManager() - .getBucketSnapshotManager() - .copySearchableBucketListSnapshots(); + .maybeUpdateLedgerStateSnapshot(snapshot); } -SearchableSnapshotConstPtr& +LedgerStateSnapshot& AppConnector::getOverlayThreadSnapshot() { return mApp.getOverlayManager().getOverlayThreadSnapshot(); diff --git a/src/main/AppConnector.h b/src/main/AppConnector.h index 7f90a13daf..1d6ecd8d2d 100644 --- a/src/main/AppConnector.h +++ b/src/main/AppConnector.h @@ -5,6 +5,7 @@ #pragma once #include "bucket/BucketUtils.h" +#include "ledger/LedgerStateSnapshot.h" #include "main/Application.h" #include "main/Config.h" #include "rust/RustBridge.h" @@ -72,25 +73,12 @@ class AppConnector bool isStopping() const; - SearchableHotArchiveSnapshotConstPtr - copySearchableHotArchiveBucketListSnapshot(); - - SearchableSnapshotConstPtr copySearchableLiveBucketListSnapshot(); - - // Refreshes `snapshot` if a newer snapshot is available. No-op otherwise. - void - maybeCopySearchableBucketListSnapshot(SearchableSnapshotConstPtr& snapshot); - - void maybeCopyLiveAndHotArchiveSnapshots( - SearchableSnapshotConstPtr& liveSnapshot, - SearchableHotArchiveSnapshotConstPtr& hotArchiveSnapshot); - - std::pair - copySearchableBucketListSnapshots(); + LedgerStateSnapshot copyLedgerStateSnapshot(); + void maybeUpdateLedgerStateSnapshot(LedgerStateSnapshot& snapshot); // Get a snapshot of ledger state for use by the overlay thread only. Must // only be called from the overlay thread. - SearchableSnapshotConstPtr& getOverlayThreadSnapshot(); + LedgerStateSnapshot& getOverlayThreadSnapshot(); // Protocol 23 data corruption bug data verifier. This typically is null, // unless a path to a CSV file containing the corruption data was provided diff --git a/src/main/ApplicationUtils.cpp b/src/main/ApplicationUtils.cpp index 81bb553abc..4398b3a880 100644 --- a/src/main/ApplicationUtils.cpp +++ b/src/main/ApplicationUtils.cpp @@ -485,8 +485,6 @@ getHotArchiveListBalanceForAsset(Application& app, AssetContractInfo const& assetContractInfo, int64_t& runningBalance) { - auto& bm = app.getBucketManager(); - std::map archived = app.getBucketManager().loadCompleteHotArchiveState(has); for (auto const& [_, entry] : archived) @@ -757,8 +755,7 @@ dumpLedger(Config cfg, std::string const& outputFile, auto& lm = app->getLedgerManager(); lm.partiallyLoadLastKnownLedgerForUtils(); - auto liveSnapshot = - app->getAppConnector().copySearchableLiveBucketListSnapshot(); + auto liveSnapshot = app->getAppConnector().copyLedgerStateSnapshot(); auto ttlGetter = [&liveSnapshot, includeAllStates, dumpHotArchive](LedgerKey const& key) -> uint32_t { if (includeAllStates || dumpHotArchive) @@ -767,7 +764,7 @@ dumpLedger(Config cfg, std::string const& outputFile, "TTL is undefined when `--include-all-states` or " "`--hot-archive` flag is set."); } - auto entry = liveSnapshot->load(key); + auto entry = liveSnapshot.loadLiveEntry(key); if (!entry) { throw std::runtime_error("No TTL entry found for key: " + @@ -899,10 +896,10 @@ dumpWasmBlob(Config cfg, std::string const& hash, std::string const& dir) }; auto snap = app->getBucketManager() .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + .copyLedgerStateSnapshot(); if (hash == "ALL") { - snap->scanForEntriesOfType( + snap.scanLiveEntriesOfType( CONTRACT_CODE, [&](BucketEntry const& entry) { if (entry.type() == INITENTRY || entry.type() == LIVEENTRY) { @@ -918,7 +915,7 @@ dumpWasmBlob(Config cfg, std::string const& hash, std::string const& dir) LedgerKey key; key.type(LedgerEntryType::CONTRACT_CODE); key.contractCode().hash = hexToBin256(hash); - auto entry = snap->load(key); + auto entry = snap.loadLiveEntry(key); if (entry && entry->data.type() == LedgerEntryType::CONTRACT_CODE) { auto const& codeEntry = entry->data.contractCode(); diff --git a/src/main/QueryServer.cpp b/src/main/QueryServer.cpp index ae5f3fee87..ccd825a7bc 100644 --- a/src/main/QueryServer.cpp +++ b/src/main/QueryServer.cpp @@ -78,11 +78,8 @@ QueryServer::QueryServer(std::string const& address, unsigned short port, #ifdef BUILD_TESTS if (useMainThreadForTesting) { - auto [live, hotArchive] = - mAppConnector.copySearchableBucketListSnapshots(); - mBucketListSnapshots[std::this_thread::get_id()] = std::move(live); - mHotArchiveBucketListSnapshots[std::this_thread::get_id()] = - std::move(hotArchive); + mSnapshots.emplace(std::this_thread::get_id(), + mAppConnector.copyLedgerStateSnapshot()); } else #endif @@ -90,10 +87,8 @@ QueryServer::QueryServer(std::string const& address, unsigned short port, auto workerPids = mServer.start(); for (auto pid : workerPids) { - auto [live, hotArchive] = - mAppConnector.copySearchableBucketListSnapshots(); - mBucketListSnapshots[pid] = std::move(live); - mHotArchiveBucketListSnapshots[pid] = std::move(hotArchive); + mSnapshots.emplace(pid, + mAppConnector.copyLedgerStateSnapshot()); } } } @@ -167,10 +162,6 @@ QueryServer::getLedgerEntryRaw(std::string const& params, std::string const& body, std::string& retStr) { ZoneScoped; - - auto& snapshotPtr = mBucketListSnapshots.at(std::this_thread::get_id()); - mAppConnector.maybeCopySearchableBucketListSnapshot(snapshotPtr); - Json::Value root; std::map> paramMap; @@ -181,7 +172,8 @@ QueryServer::getLedgerEntryRaw(std::string const& params, if (!keys.empty()) { - auto& bl = *snapshotPtr; + auto& snapshot = mSnapshots.at(std::this_thread::get_id()); + mAppConnector.maybeUpdateLedgerStateSnapshot(snapshot); LedgerKeySet orderedKeys; for (auto const& key : keys) @@ -199,7 +191,7 @@ QueryServer::getLedgerEntryRaw(std::string const& params, root["ledgerSeq"] = *snapshotLedger; auto loadedKeysOp = - bl.loadKeysFromLedger(orderedKeys, *snapshotLedger); + snapshot.loadLiveKeysFromLedger(orderedKeys, *snapshotLedger); // Return 404 if ledgerSeq not found if (!loadedKeysOp) @@ -213,8 +205,8 @@ QueryServer::getLedgerEntryRaw(std::string const& params, // Otherwise default to current ledger else { - loadedKeys = bl.loadKeys(orderedKeys, "query"); - root["ledgerSeq"] = bl.getLedgerSeq(); + loadedKeys = snapshot.loadLiveKeys(orderedKeys, "query"); + root["ledgerSeq"] = snapshot.getLedgerSeq(); } for (auto const& le : loadedKeys) @@ -247,13 +239,6 @@ QueryServer::getLedgerEntry(std::string const& params, std::string const& body, std::string& retStr) { ZoneScoped; - - auto& liveBl = mBucketListSnapshots.at(std::this_thread::get_id()); - auto& hotArchiveBl = - mHotArchiveBucketListSnapshots.at(std::this_thread::get_id()); - - mAppConnector.maybeCopyLiveAndHotArchiveSnapshots(liveBl, hotArchiveBl); - Json::Value root; std::map> paramMap; @@ -269,6 +254,8 @@ QueryServer::getLedgerEntry(std::string const& params, std::string const& body, return false; } + auto& snapshot = mSnapshots.at(std::this_thread::get_id()); + mAppConnector.maybeUpdateLedgerStateSnapshot(snapshot); LedgerKeySet keysToSearch; // Keep track of keys in their original order for response ordering @@ -297,10 +284,11 @@ QueryServer::getLedgerEntry(std::string const& params, std::string const& body, std::vector liveEntries; std::vector archivedEntries; uint32_t ledgerSeq = - snapshotLedger ? *snapshotLedger : liveBl->getLedgerSeq(); + snapshotLedger ? *snapshotLedger : snapshot.getLedgerSeq(); root["ledgerSeq"] = ledgerSeq; - auto liveEntriesOp = liveBl->loadKeysFromLedger(keysToSearch, ledgerSeq); + auto liveEntriesOp = + snapshot.loadLiveKeysFromLedger(keysToSearch, ledgerSeq); // Return 404 if ledgerSeq not found if (!liveEntriesOp) @@ -329,8 +317,8 @@ QueryServer::getLedgerEntry(std::string const& params, std::string const& body, // Only query archive for soroban keys we didn't find in the live bucketList if (!hotArchiveKeysToSearch.empty()) { - auto archivedEntriesOp = - hotArchiveBl->loadKeysFromLedger(hotArchiveKeysToSearch, ledgerSeq); + auto archivedEntriesOp = snapshot.loadArchiveKeysFromLedger( + hotArchiveKeysToSearch, ledgerSeq); if (!archivedEntriesOp) { retStr = "Ledger not found\n"; @@ -354,8 +342,8 @@ QueryServer::getLedgerEntry(std::string const& params, std::string const& body, { // We haven't updated the live snapshot so we know the have a snapshot // available for ledgerSeq - ttlEntries = - std::move(liveBl->loadKeysFromLedger(ttlKeys, ledgerSeq).value()); + ttlEntries = std::move( + snapshot.loadLiveKeysFromLedger(ttlKeys, ledgerSeq).value()); } std::unordered_map ttlMap; diff --git a/src/main/QueryServer.h b/src/main/QueryServer.h index e00ca7ba68..b7e81a8104 100644 --- a/src/main/QueryServer.h +++ b/src/main/QueryServer.h @@ -7,8 +7,11 @@ #include "lib/httpthreaded/server.hpp" #include "bucket/BucketSnapshotManager.h" +#include "ledger/LedgerStateSnapshot.h" #include #include +#include +#include #include #include #include @@ -26,11 +29,7 @@ class QueryServer httpThreaded::server::server mServer; - std::unordered_map - mBucketListSnapshots; - - std::unordered_map - mHotArchiveBucketListSnapshots; + std::unordered_map mSnapshots; AppConnector& mAppConnector; diff --git a/src/overlay/OverlayManager.h b/src/overlay/OverlayManager.h index bd2f453799..551c2133d1 100644 --- a/src/overlay/OverlayManager.h +++ b/src/overlay/OverlayManager.h @@ -5,6 +5,7 @@ #pragma once #include "crypto/BLAKE2.h" +#include "ledger/LedgerStateSnapshot.h" #include "overlay/Peer.h" /** @@ -214,6 +215,6 @@ class OverlayManager // Get a snapshot of ledger state for use by the overlay thread only. Caller // is responsible for updating the snapshot as needed. - virtual SearchableSnapshotConstPtr& getOverlayThreadSnapshot() = 0; + virtual LedgerStateSnapshot& getOverlayThreadSnapshot() = 0; }; } diff --git a/src/overlay/OverlayManagerImpl.cpp b/src/overlay/OverlayManagerImpl.cpp index 3cf558029e..85b63f3205 100644 --- a/src/overlay/OverlayManagerImpl.cpp +++ b/src/overlay/OverlayManagerImpl.cpp @@ -4,6 +4,7 @@ #include "overlay/OverlayManagerImpl.h" #include "bucket/BucketManager.h" +#include "bucket/BucketSnapshotManager.h" #include "crypto/Hex.h" #include "crypto/SecretKey.h" #include "crypto/ShortHash.h" @@ -1432,7 +1433,7 @@ OverlayManagerImpl::recordMessageMetric(StellarMessage const& stellarMsg, } } -SearchableSnapshotConstPtr& +LedgerStateSnapshot& OverlayManagerImpl::getOverlayThreadSnapshot() { releaseAssert(mApp.threadIsType(Application::ThreadType::OVERLAY)); @@ -1441,9 +1442,9 @@ OverlayManagerImpl::getOverlayThreadSnapshot() // Create a new snapshot mOverlayThreadSnapshot = mApp.getBucketManager() .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + .copyLedgerStateSnapshot(); } - return mOverlayThreadSnapshot; + return *mOverlayThreadSnapshot; } } diff --git a/src/overlay/OverlayManagerImpl.h b/src/overlay/OverlayManagerImpl.h index 3a62ddd45e..c4b41c0ab8 100644 --- a/src/overlay/OverlayManagerImpl.h +++ b/src/overlay/OverlayManagerImpl.h @@ -9,6 +9,7 @@ #include "PeerDoor.h" #include "PeerManager.h" #include "herder/TxSetFrame.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "overlay/Floodgate.h" #include "overlay/OverlayManager.h" @@ -167,7 +168,7 @@ class OverlayManagerImpl : public OverlayManager void recordMessageMetric(StellarMessage const& stellarMsg, Peer::pointer peer) override; - SearchableSnapshotConstPtr& getOverlayThreadSnapshot() override; + LedgerStateSnapshot& getOverlayThreadSnapshot() override; private: struct ResolvedPeers @@ -185,7 +186,7 @@ class OverlayManagerImpl : public OverlayManager mScheduledMessages; // Snapshot of ledger state for use ONLY by the overlay thread - SearchableSnapshotConstPtr mOverlayThreadSnapshot; + std::optional mOverlayThreadSnapshot; void triggerPeerResolution(); std::pair, bool> diff --git a/src/overlay/Peer.cpp b/src/overlay/Peer.cpp index e6d16ee3c3..95f9a07b30 100644 --- a/src/overlay/Peer.cpp +++ b/src/overlay/Peer.cpp @@ -67,7 +67,7 @@ populateSignatureCache(AppConnector& app, TransactionFrameBaseConstPtr tx) app.threadIsType(Application::ThreadType::OVERLAY)); auto& snapshot = app.getOverlayThreadSnapshot(); - app.maybeCopySearchableBucketListSnapshot(snapshot); + app.maybeUpdateLedgerStateSnapshot(snapshot); LedgerSnapshot ledgerSnapshot(snapshot); // Use ledgerSnapshot to check all transactions in `tx`. We use a lambda to diff --git a/src/simulation/test/LoadGeneratorTests.cpp b/src/simulation/test/LoadGeneratorTests.cpp index 148bcfd693..e0dca494e0 100644 --- a/src/simulation/test/LoadGeneratorTests.cpp +++ b/src/simulation/test/LoadGeneratorTests.cpp @@ -3,9 +3,11 @@ // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 #include "bucket/BucketManager.h" +#include "bucket/BucketSnapshotManager.h" #include "crypto/SHA.h" #include "crypto/SecretKey.h" #include "ledger/LedgerManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "main/Config.h" #include "scp/QuorumSetUtils.h" #include "simulation/ApplyLoad.h" @@ -954,16 +956,16 @@ TEST_CASE("apply load", "[loadgen][applyload][acceptance]") expectedArchivedEntries - 1}; std::set sampleKeys; - auto hotArchive = app->getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); + auto snap = app->getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); for (auto idx : sampleIndices) { sampleKeys.insert(ApplyLoad::getKeyForArchivedEntry(idx)); } - auto sampleEntries = hotArchive->loadKeys(sampleKeys); + auto sampleEntries = snap.loadArchiveKeys(sampleKeys); REQUIRE(sampleEntries.size() == sampleKeys.size()); al.execute(); diff --git a/src/transactions/InvokeHostFunctionOpFrame.cpp b/src/transactions/InvokeHostFunctionOpFrame.cpp index 62ed961c39..a16a9ac9f1 100644 --- a/src/transactions/InvokeHostFunctionOpFrame.cpp +++ b/src/transactions/InvokeHostFunctionOpFrame.cpp @@ -271,7 +271,8 @@ class InvokeHostFunctionApplyHelper : virtual LedgerAccessHelper rust::Vec mTtlEntryCxxBufs; rust::Vec mAutoRestoredRwEntryIndices; HostFunctionMetrics mMetrics; - SearchableHotArchiveSnapshotConstPtr mHotArchive; + // Used for hot archive access only + LedgerStateSnapshot mStateSnapshot; rust::Box const& mModuleCache; DiagnosticEventManager& mDiagnosticEvents; @@ -285,7 +286,7 @@ class InvokeHostFunctionApplyHelper : virtual LedgerAccessHelper std::optional& refundableFeeTracker, OperationMetaBuilder& opMeta, InvokeHostFunctionOpFrame const& opFrame, SorobanNetworkConfig const& sorobanConfig, - SearchableHotArchiveSnapshotConstPtr hotArchive, + LedgerStateSnapshot stateSnapshot, rust::Box const& moduleCache) : mApp(app) , mRes(res) @@ -298,7 +299,7 @@ class InvokeHostFunctionApplyHelper : virtual LedgerAccessHelper , mAppConfig(app.getConfig()) , mMetrics(app.getSorobanMetrics(), app.getConfig().DISABLE_SOROBAN_METRICS_FOR_TESTING) - , mHotArchive(hotArchive) + , mStateSnapshot(std::move(stateSnapshot)) , mModuleCache(moduleCache) , mDiagnosticEvents(mOpMeta.getDiagnosticEventManager()) { @@ -425,8 +426,7 @@ class InvokeHostFunctionApplyHelper : virtual LedgerAccessHelper continue; } - releaseAssertOrThrow(mHotArchive); - auto archiveEntry = mHotArchive->load(lk); + auto archiveEntry = mStateSnapshot.loadArchiveEntry(lk); if (archiveEntry) { releaseAssertOrThrow( @@ -984,7 +984,7 @@ class InvokeHostFunctionPreV23ApplyHelper : InvokeHostFunctionApplyHelper(app, sorobanBasePrngSeed, res, refundableFeeTracker, opMeta, opFrame, sorobanConfig, - nullptr, // No hot archive before p23 + app.copyLedgerStateSnapshot(), moduleCache) , PreV23LedgerAccessHelper(ltx) { @@ -1170,7 +1170,7 @@ class InvokeHostFunctionParallelApplyHelper : InvokeHostFunctionApplyHelper( app, sorobanBasePrngSeed, res, refundableFeeTracker, opMeta, opFrame, threadState.getSorobanConfig(), - threadState.getHotArchiveSnapshot(), threadState.getModuleCache()) + threadState.getSnapshot(), threadState.getModuleCache()) , ParallelLedgerAccessHelper(threadState, ledgerInfo) { ZoneScoped; diff --git a/src/transactions/ParallelApplyUtils.cpp b/src/transactions/ParallelApplyUtils.cpp index 9c26466bd5..d230f13364 100644 --- a/src/transactions/ParallelApplyUtils.cpp +++ b/src/transactions/ParallelApplyUtils.cpp @@ -302,11 +302,11 @@ GlobalParallelApplyLedgerState::GlobalParallelApplyLedgerState( InMemorySorobanState const& inMemoryState, SorobanNetworkConfig const& sorobanConfig) : LedgerEntryScope(ScopeIdT(0, ltx.getHeader().ledgerSeq)) - , mHotArchiveSnapshot(app.copySearchableHotArchiveBucketListSnapshot()) - , mLiveSnapshot(app.copySearchableLiveBucketListSnapshot()) + , mSnapshot(app.copyLedgerStateSnapshot()) , mInMemorySorobanState(inMemoryState) , mSorobanConfig(sorobanConfig) { + releaseAssertOrThrow(ltx.getHeader().ledgerSeq == getSnapshotLedgerSeq() + 1); @@ -445,11 +445,7 @@ GlobalParallelApplyLedgerState::commitChangesToLedgerTxn( uint32_t GlobalParallelApplyLedgerState::getSnapshotLedgerSeq() const { - releaseAssertOrThrow(mLiveSnapshot->getLedgerSeq() == - mHotArchiveSnapshot->getLedgerSeq()); - releaseAssertOrThrow(mLiveSnapshot->getLedgerSeq() == - mInMemorySorobanState.getLedgerSeq()); - return mLiveSnapshot->getLedgerSeq(); + return mInMemorySorobanState.getLedgerSeq(); } GlobalParallelApplyEntryMap const& @@ -599,11 +595,7 @@ ThreadParallelApplyLedgerState::ThreadParallelApplyLedgerState( AppConnector& app, GlobalParallelApplyLedgerState const& global, Cluster const& cluster, size_t clusterIdx) : LedgerEntryScope(ScopeIdT(clusterIdx, global.mScopeID.mLedger)) - // TODO: find a way to clone these from parent rather than asking the - // snapshot manager again. That might have changed! NB taking a shared - // pointer copy is not safe, the snapshot objects are not threadsafe. - , mHotArchiveSnapshot(app.copySearchableHotArchiveBucketListSnapshot()) - , mLiveSnapshot(app.copySearchableLiveBucketListSnapshot()) + , mSnapshot(global.mSnapshot) , mInMemorySorobanState(global.mInMemorySorobanState) , mSorobanConfig(global.mSorobanConfig) , mModuleCache(app.getModuleCache()) @@ -720,7 +712,7 @@ ThreadParallelApplyLedgerState::getLiveEntryOpt(LedgerKey const& key) const } else { - res = mLiveSnapshot->load(key); + res = mSnapshot.loadLiveEntry(key); } return scopeAdoptEntryOpt(res ? std::make_optional(*res) : std::nullopt); @@ -846,11 +838,7 @@ ThreadParallelApplyLedgerState::entryWasRestored(LedgerKey const& key) const uint32_t ThreadParallelApplyLedgerState::getSnapshotLedgerSeq() const { - releaseAssertOrThrow(mLiveSnapshot->getLedgerSeq() == - mHotArchiveSnapshot->getLedgerSeq()); - releaseAssertOrThrow(mLiveSnapshot->getLedgerSeq() == - mInMemorySorobanState.getLedgerSeq()); - return mLiveSnapshot->getLedgerSeq(); + return mInMemorySorobanState.getLedgerSeq(); } SorobanNetworkConfig const& @@ -859,10 +847,10 @@ ThreadParallelApplyLedgerState::getSorobanConfig() const return mSorobanConfig; } -SearchableHotArchiveSnapshotConstPtr const& -ThreadParallelApplyLedgerState::getHotArchiveSnapshot() const +LedgerStateSnapshot const& +ThreadParallelApplyLedgerState::getSnapshot() const { - return mHotArchiveSnapshot; + return mSnapshot; } rust::Box const& diff --git a/src/transactions/ParallelApplyUtils.h b/src/transactions/ParallelApplyUtils.h index 2f009dafd6..8f0259ed16 100644 --- a/src/transactions/ParallelApplyUtils.h +++ b/src/transactions/ParallelApplyUtils.h @@ -6,6 +6,7 @@ #include "ledger/InMemorySorobanState.h" #include "ledger/LedgerEntryScope.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "ledger/LedgerTypeUtils.h" #include "transactions/ParallelApplyStage.h" @@ -70,9 +71,9 @@ class ParallelLedgerInfo class ThreadParallelApplyLedgerState : public LedgerEntryScope { - // Copies of snapshots from the global state. - SearchableHotArchiveSnapshotConstPtr mHotArchiveSnapshot; - SearchableSnapshotConstPtr mLiveSnapshot; + // Copy of the ledger state snapshot from the global state, with fresh + // file caches for thread safety. + LedgerStateSnapshot mSnapshot; // Reference to the live in-memory Soroban state. For Soroban entries // (CONTRACT_DATA, CONTRACT_CODE, TTL), query this in-memory state instead @@ -173,7 +174,7 @@ class ThreadParallelApplyLedgerState SorobanNetworkConfig const& getSorobanConfig() const; - SearchableHotArchiveSnapshotConstPtr const& getHotArchiveSnapshot() const; + LedgerStateSnapshot const& getSnapshot() const; rust::Box const& getModuleCache() const; }; @@ -181,20 +182,11 @@ class ThreadParallelApplyLedgerState class GlobalParallelApplyLedgerState : public LedgerEntryScope { - // Contains the hot archive state from the start of the ledger close. If a - // key is in here, it is "evicted". An invariant is that if a key is in here - // it is _not_ in the live snapshot. - SearchableHotArchiveSnapshotConstPtr mHotArchiveSnapshot; - - // Contains the live soroban state from the start of the ledger close. If a - // key is in here, it is either "archived" or "live", depending on its TTL. - // Classic entries are always live, soroban entries always have an - // associated TTL entry and if the TTL is in the past the entry is - // "archived", otherwise "live". An invariant is that if a key is in here it - // is _not_ in the hot archive snapshot. - // Note that only classic entries should be queried from mLiveSnapshot. For - // Soroban entries query mInMemorySorobanState instead. - SearchableSnapshotConstPtr mLiveSnapshot; + // Contains the full ledger state snapshot from the start of the ledger + // close, providing access to both the live bucket list and the hot archive + // bucket list. Note that this does not reflect changes from the classic + // apply phase, but is a snapshot of the start of the ledger. + LedgerStateSnapshot mSnapshot; // Contains an exact one-to-one in-memory mapping of the live snapshot for // CONTRACT_DATA, CONTRACT_CODE, and TTL entries. For these entry types, diff --git a/src/transactions/RestoreFootprintOpFrame.cpp b/src/transactions/RestoreFootprintOpFrame.cpp index 547d138268..315cfdb8ed 100644 --- a/src/transactions/RestoreFootprintOpFrame.cpp +++ b/src/transactions/RestoreFootprintOpFrame.cpp @@ -288,7 +288,7 @@ class RestoreFootprintParallelApplyHelper : virtual public RestoreFootprintApplyHelper, virtual public ParallelLedgerAccessHelper { - SearchableHotArchiveSnapshotConstPtr mHotArchive; + LedgerStateSnapshot mSnapshot; public: RestoreFootprintParallelApplyHelper( @@ -299,14 +299,14 @@ class RestoreFootprintParallelApplyHelper : RestoreFootprintApplyHelper(app, res, refundableFeeTracker, opMeta, opFrame, threadState.getSorobanConfig()) , ParallelLedgerAccessHelper(threadState, ledgerInfo) - , mHotArchive(app.copySearchableHotArchiveBucketListSnapshot()) + , mSnapshot(app.copyLedgerStateSnapshot()) { } std::optional getHotArchiveEntry(LedgerKey const& key) override { - auto ptr = mHotArchive->load(key); + auto ptr = mSnapshot.loadArchiveEntry(key); if (ptr) { return ptr->archivedEntry(); diff --git a/src/transactions/test/InvokeHostFunctionTests.cpp b/src/transactions/test/InvokeHostFunctionTests.cpp index 8dcb76abcf..22c0ca537b 100644 --- a/src/transactions/test/InvokeHostFunctionTests.cpp +++ b/src/transactions/test/InvokeHostFunctionTests.cpp @@ -19,6 +19,7 @@ #include "crypto/SecretKey.h" #include "herder/Herder.h" #include "ledger/LedgerManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "ledger/LedgerTypeUtils.h" #include "ledger/test/LedgerTestUtils.h" @@ -4648,15 +4649,15 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]") auto lk = client.getContract().getDataKey( makeSymbolSCVal("key"), ContractDataDurability::PERSISTENT); - auto hotArchive = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); + auto snap = test.getApp() + .getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); if (evict) { - REQUIRE(hotArchive->load(lk)); - REQUIRE(!hotArchive->load(getTTLKey(lk))); + REQUIRE(snap.loadArchiveEntry(lk)); + REQUIRE(!snap.loadArchiveEntry(getTTLKey(lk))); { LedgerTxn ltx(test.getApp().getLedgerTxnRoot()); REQUIRE(!ltx.load(lk)); @@ -4665,8 +4666,8 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]") } else { - REQUIRE(!hotArchive->load(lk)); - REQUIRE(!hotArchive->load(getTTLKey(lk))); + REQUIRE(!snap.loadArchiveEntry(lk)); + REQUIRE(!snap.loadArchiveEntry(getTTLKey(lk))); { LedgerTxn ltx(test.getApp().getLedgerTxnRoot()); REQUIRE(ltx.load(lk)); @@ -4729,14 +4730,13 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]") client.get("key", ContractDataDurability::PERSISTENT, 123); - test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .maybeCopySearchableHotArchiveBucketListSnapshot( - hotArchive); + auto restoredSnap = test.getApp() + .getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); // Restored entries are deleted from Hot Archive - REQUIRE(!hotArchive->load(lk)); + REQUIRE(!restoredSnap.loadArchiveEntry(lk)); }; SECTION("restore op") @@ -7385,11 +7385,11 @@ TEST_CASE("multiple version of same key in a single eviction scan", closeLedgerOn(test.getApp(), ledgerSeq, 2, 1, 2016); } - auto hotArchive = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); - REQUIRE(hotArchive->load(lk)); + auto evictSnap = test.getApp() + .getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); + REQUIRE(evictSnap.loadArchiveEntry(lk)); }; evictEntry(); @@ -7400,11 +7400,11 @@ TEST_CASE("multiple version of same key in a single eviction scan", // levels of the BucketList. test.invokeRestoreOp({lk}, 20166); - auto bl = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); - auto loadRes = bl->load(lk); + auto restoreSnap = test.getApp() + .getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); + auto loadRes = restoreSnap.loadLiveEntry(lk); REQUIRE(loadRes); // Evict the entry again. If we "double evict" we'll throw during ledger @@ -7468,13 +7468,10 @@ TEST_CASE_VERSIONS("do not evict outdated keys", "[archival][soroban]") // Check the eviction results. // Entry should be archived and not in the live BucketList - auto liveBL = snapshotManager.copySearchableLiveBucketListSnapshot(); - REQUIRE(!liveBL->load(lk)); + auto evictionSnap = snapshotManager.copyLedgerStateSnapshot(); + REQUIRE(!evictionSnap.loadLiveEntry(lk)); - auto hotArchive = - snapshotManager.copySearchableHotArchiveBucketListSnapshot(); - - auto hotLoad = hotArchive->load(lk); + auto hotLoad = evictionSnap.loadArchiveEntry(lk); REQUIRE(hotLoad); REQUIRE(hotLoad->type() == HOT_ARCHIVE_ARCHIVED); @@ -7495,14 +7492,12 @@ TEST_CASE_VERSIONS("do not evict outdated keys", "[archival][soroban]") // Restore entry and make sure the correct value is restored test.invokeRestoreOp({lk}, 20166); - hotArchive = - snapshotManager.copySearchableHotArchiveBucketListSnapshot(); - auto hotLoadAfterRestore = hotArchive->load(lk); + auto restoreSnap = snapshotManager.copyLedgerStateSnapshot(); + auto hotLoadAfterRestore = restoreSnap.loadArchiveEntry(lk); REQUIRE(!hotLoadAfterRestore); // Check that restored value matches what was archived - liveBL = snapshotManager.copySearchableLiveBucketListSnapshot(); - auto liveLoadAfterRestore = liveBL->load(lk); + auto liveLoadAfterRestore = restoreSnap.loadLiveEntry(lk); REQUIRE(liveLoadAfterRestore); if (protocolVersionIsBefore(test.getLedgerVersion(), @@ -7579,30 +7574,23 @@ TEST_CASE("disable eviction scan", "[archival][soroban]") REQUIRE(initialIterator == test.getNetworkCfg().evictionIterator()); } - auto liveBL = snapshotManager.copySearchableLiveBucketListSnapshot(); - auto hotArchive = - snapshotManager.copySearchableHotArchiveBucketListSnapshot(); - + auto snap = snapshotManager.copyLedgerStateSnapshot(); auto assertTemp = [&](bool isLive) { - liveBL = snapshotManager.copySearchableLiveBucketListSnapshot(); - hotArchive = - snapshotManager.copySearchableHotArchiveBucketListSnapshot(); - auto tempLiveLoad = liveBL->load(temporaryKey); + snap = snapshotManager.copyLedgerStateSnapshot(); + auto tempLiveLoad = snap.loadLiveEntry(temporaryKey); REQUIRE(static_cast(tempLiveLoad) == isLive); // Temp entries are never archived - REQUIRE(!hotArchive->load(temporaryKey)); + REQUIRE(!snap.loadArchiveEntry(temporaryKey)); }; auto assertPersistent = [&](bool isLive) { - liveBL = snapshotManager.copySearchableLiveBucketListSnapshot(); - hotArchive = - snapshotManager.copySearchableHotArchiveBucketListSnapshot(); + snap = snapshotManager.copyLedgerStateSnapshot(); - auto persistentLiveLoad = liveBL->load(persistentKey); + auto persistentLiveLoad = snap.loadLiveEntry(persistentKey); REQUIRE(static_cast(persistentLiveLoad) == isLive); - auto hotArchiveLoad = hotArchive->load(persistentKey); + auto hotArchiveLoad = snap.loadArchiveEntry(persistentKey); REQUIRE(static_cast(hotArchiveLoad) != isLive); }; @@ -7626,10 +7614,9 @@ TEST_CASE("disable eviction scan", "[archival][soroban]") initialIterator = iteratorAfterUpgrade; // Check that exactly one entry has been evicted - liveBL = snapshotManager.copySearchableLiveBucketListSnapshot(); - hotArchive = snapshotManager.copySearchableHotArchiveBucketListSnapshot(); + snap = snapshotManager.copyLedgerStateSnapshot(); - auto persistentLiveLoad = liveBL->load(persistentKey); + auto persistentLiveLoad = snap.loadLiveEntry(persistentKey); if (persistentLiveLoad) { // If perstent entry is live, assert that the temp entry is evicted. @@ -10023,17 +10010,13 @@ TEST_CASE("autorestore from another contract", "[tx][soroban][archival]") REQUIRE(client2.get("key2", ContractDataDurability::PERSISTENT, std::nullopt) == INVOKE_HOST_FUNCTION_ENTRY_ARCHIVED); - auto hotArchiveSnapshot = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); - auto liveSnapshot = test.getApp() + auto archivedSnap = test.getApp() .getBucketManager() .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); + .copyLedgerStateSnapshot(); - REQUIRE(hotArchiveSnapshot->loadKeys({lk1, lk2}).size() == 2); - REQUIRE(liveSnapshot->loadKeys({lk1, lk2}, "load").size() == 0); + REQUIRE(archivedSnap.loadArchiveKeys({lk1, lk2}).size() == 2); + REQUIRE(archivedSnap.loadLiveKeys({lk1, lk2}, "load").size() == 0); // Now, invoke contract2, but also autorestore state from contract1. auto keysToRestore = client2.getContract().getKeys(); @@ -10058,17 +10041,13 @@ TEST_CASE("autorestore from another contract", "[tx][soroban][archival]") /*addContractKeys=*/false); REQUIRE(invocation.withExactNonRefundableResourceFee().invoke()); - liveSnapshot = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copySearchableLiveBucketListSnapshot(); - hotArchiveSnapshot = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); + auto restoredSnap = test.getApp() + .getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); - REQUIRE(liveSnapshot->loadKeys({lk1, lk2}, "load").size() == 2); - REQUIRE(hotArchiveSnapshot->loadKeys({lk1, lk2}).size() == 0); + REQUIRE(restoredSnap.loadLiveKeys({lk1, lk2}, "load").size() == 2); + REQUIRE(restoredSnap.loadArchiveKeys({lk1, lk2}).size() == 0); // Verify that the correct values were restored REQUIRE(client1.get("key1", ContractDataDurability::PERSISTENT, 111) == diff --git a/src/transactions/test/ParallelApplyTest.cpp b/src/transactions/test/ParallelApplyTest.cpp index d0614962b7..b0f220fdb7 100644 --- a/src/transactions/test/ParallelApplyTest.cpp +++ b/src/transactions/test/ParallelApplyTest.cpp @@ -11,6 +11,8 @@ #include #include "bucket/BucketManager.h" +#include "bucket/BucketSnapshotManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "main/Application.h" #include "test/TestUtils.h" #include "test/TxTests.h" @@ -1121,11 +1123,10 @@ applyTestTransactions(TestConfig const& testConfig, uint32_t protocolVersion, std::optional>>> finalEntries; LedgerSnapshot ls(test.getApp()); - auto hotArchive = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); - releaseAssert(hotArchive); + auto archiveSnap = test.getApp() + .getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); for (auto const& k : allKeys) { std::optional liveEntry; @@ -1145,7 +1146,7 @@ applyTestTransactions(TestConfig const& testConfig, uint32_t protocolVersion, } else if (isSorobanEntry(k)) { - if (auto e = hotArchive->load(k)) + if (auto e = archiveSnap.loadArchiveEntry(k)) { archivedEntry = e->archivedEntry(); } diff --git a/src/transactions/test/SorobanTxTestUtils.cpp b/src/transactions/test/SorobanTxTestUtils.cpp index 885d3b0f0e..aa6f66ea68 100644 --- a/src/transactions/test/SorobanTxTestUtils.cpp +++ b/src/transactions/test/SorobanTxTestUtils.cpp @@ -4,6 +4,8 @@ #include "SorobanTxTestUtils.h" #include "bucket/BucketManager.h" +#include "bucket/BucketSnapshotManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTypeUtils.h" #include "rust/RustBridge.h" #include "test/Catch2.h" @@ -1443,12 +1445,11 @@ SorobanTest::getEntryExpirationStatus(LedgerKey const& key) } return ExpirationStatus::LIVE; } - auto hotArchive = getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copySearchableHotArchiveBucketListSnapshot(); - releaseAssert(hotArchive); - if (hotArchive->load(key) != nullptr) + auto snap = getApp() + .getBucketManager() + .getBucketSnapshotManager() + .copyLedgerStateSnapshot(); + if (snap.loadArchiveEntry(key) != nullptr) { return ExpirationStatus::HOT_ARCHIVE; } From 003890c8fa8d90aebf365d78fcf6a182cbdffd87 Mon Sep 17 00:00:00 2001 From: Garand Tyson Date: Tue, 10 Feb 2026 17:10:34 -0800 Subject: [PATCH 3/5] Make LedgerStateSnapshot threadsafe --- src/bucket/BucketListSnapshot.cpp | 41 +++++++++++++++++-- src/bucket/BucketListSnapshot.h | 25 +++++++++--- src/bucket/BucketManager.cpp | 5 +-- src/ledger/LedgerManagerImpl.cpp | 13 +++--- src/ledger/LedgerStateSnapshot.cpp | 42 +++++++++----------- src/ledger/LedgerStateSnapshot.h | 14 +------ src/main/QueryServer.h | 2 - src/overlay/OverlayManagerImpl.cpp | 1 - src/transactions/ParallelApplyUtils.cpp | 3 +- src/transactions/RestoreFootprintOpFrame.cpp | 2 +- 10 files changed, 87 insertions(+), 61 deletions(-) diff --git a/src/bucket/BucketListSnapshot.cpp b/src/bucket/BucketListSnapshot.cpp index 0376a3b33a..99a44a8dfc 100644 --- a/src/bucket/BucketListSnapshot.cpp +++ b/src/bucket/BucketListSnapshot.cpp @@ -81,6 +81,39 @@ SearchableBucketListSnapshot::SearchableBucketListSnapshot( } } +template +SearchableBucketListSnapshot::SearchableBucketListSnapshot( + SearchableBucketListSnapshot const& other) + : mData(other.mData) + , mHistoricalSnapshots(other.mHistoricalSnapshots) + , mLedgerSeq(other.mLedgerSeq) + // mStreams intentionally left empty — each copy gets its own stream cache + , mMetrics(other.mMetrics) + , mPointTimers(other.mPointTimers) + , mBulkTimers(other.mBulkTimers) + , mBulkLoadMeter(other.mBulkLoadMeter) +{ +} + +template +SearchableBucketListSnapshot& +SearchableBucketListSnapshot::operator=( + SearchableBucketListSnapshot const& other) +{ + if (this != &other) + { + mData = other.mData; + mHistoricalSnapshots = other.mHistoricalSnapshots; + mLedgerSeq = other.mLedgerSeq; + mStreams.clear(); + mMetrics = other.mMetrics; + mPointTimers = other.mPointTimers; + mBulkTimers = other.mBulkTimers; + mBulkLoadMeter = other.mBulkLoadMeter; + } + return *this; +} + // File streams are fairly expensive to create, so they are lazily created and // stored in mStreams. template @@ -286,7 +319,7 @@ SearchableBucketListSnapshot::load(LedgerKey const& k) const auto timerIter = mPointTimers.find(k.type()); releaseAssert(timerIter != mPointTimers.end()); - auto timer = timerIter->second.TimeScope(); + auto timer = timerIter->second.get().TimeScope(); std::shared_ptr result{}; @@ -364,18 +397,18 @@ SearchableBucketListSnapshot::getBulkLoadTimer( { if (numEntries != 0) { - mBulkLoadMeter.Mark(numEntries); + mBulkLoadMeter.get().Mark(numEntries); } auto iter = mBulkTimers.find(label); if (iter == mBulkTimers.end()) { auto& metric = - mMetrics.NewTimer({BucketT::METRIC_STRING, "bulk", label}); + mMetrics.get().NewTimer({BucketT::METRIC_STRING, "bulk", label}); iter = mBulkTimers.emplace(label, metric).first; } - return iter->second; + return iter->second.get(); } template diff --git a/src/bucket/BucketListSnapshot.h b/src/bucket/BucketListSnapshot.h index 237707f020..fcc4e4ba3a 100644 --- a/src/bucket/BucketListSnapshot.h +++ b/src/bucket/BucketListSnapshot.h @@ -88,22 +88,24 @@ template class SearchableBucketListSnapshot // Ledger sequence number for this snapshot, used internally to route // queries between current and historical data. Not exposed publicly; // callers should get ledger metadata from CompleteConstLedgerState. - uint32_t const mLedgerSeq; + uint32_t mLedgerSeq; // Per-snapshot mutable stream cache mutable UnorderedMap> mStreams; - MetricsRegistry& mMetrics; + std::reference_wrapper mMetrics; // Tracks load times for each LedgerEntryType. We use // SimpleTimer since medida Timer overhead is too expensive for point loads. - UnorderedMap mPointTimers; + UnorderedMap> + mPointTimers; // Bulk load timers take significantly longer, so the timer overhead is // comparatively negligible. - mutable UnorderedMap mBulkTimers; - medida::Meter& mBulkLoadMeter; + mutable UnorderedMap> + mBulkTimers; + std::reference_wrapper mBulkLoadMeter; // Returns (lazily-constructed) file stream for bucket file. Note // this might be in some random position left over from a previous read -- @@ -157,6 +159,13 @@ template class SearchableBucketListSnapshot uint32_t ledgerSeq); public: + // Copy: copies all state except mStreams, which is reset to empty. + // Each copy gets its own file stream cache, making copies safe to use + // from different threads. + SearchableBucketListSnapshot(SearchableBucketListSnapshot const& other); + SearchableBucketListSnapshot& + operator=(SearchableBucketListSnapshot const& other); + virtual ~SearchableBucketListSnapshot() = default; // Point load, returns nullptr if not found @@ -201,6 +210,9 @@ class SearchableLiveBucketListSnapshot UnorderedSet& keysInEvictableEntries) const; public: + SearchableLiveBucketListSnapshot( + SearchableLiveBucketListSnapshot const&) = default; + std::vector loadKeys(std::set const& inKeys, std::string const& label) const; @@ -241,6 +253,9 @@ class SearchableHotArchiveBucketListSnapshot uint32_t ledgerSeq); public: + SearchableHotArchiveBucketListSnapshot( + SearchableHotArchiveBucketListSnapshot const&) = default; + std::vector loadKeys(std::set const& inKeys) const; diff --git a/src/bucket/BucketManager.cpp b/src/bucket/BucketManager.cpp index a73926987f..c44576b8ab 100644 --- a/src/bucket/BucketManager.cpp +++ b/src/bucket/BucketManager.cpp @@ -17,7 +17,6 @@ #include "history/HistoryManager.h" #include "historywork/VerifyBucketWork.h" #include "invariant/InvariantManager.h" -#include "ledger/LedgerManager.h" #include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "ledger/LedgerTypeUtils.h" @@ -34,8 +33,9 @@ #include "util/MetricsRegistry.h" #include "util/ProtocolVersion.h" #include "util/TmpDir.h" -#include "util/UnorderedMap.h" #include "util/types.h" +#include "work/WorkScheduler.h" +#include "work/WorkSequence.h" #include "xdr/Stellar-ledger.h" #include #include @@ -50,7 +50,6 @@ #include "medida/counter.h" #include "medida/meter.h" #include "medida/timer.h" -#include "work/WorkScheduler.h" #include "xdrpp/printer.h" #include diff --git a/src/ledger/LedgerManagerImpl.cpp b/src/ledger/LedgerManagerImpl.cpp index 4235830bfa..073bc744f1 100644 --- a/src/ledger/LedgerManagerImpl.cpp +++ b/src/ledger/LedgerManagerImpl.cpp @@ -771,16 +771,14 @@ LedgerManagerImpl::maybeRunSnapshotInvariantFromLedgerState( auto ledgerSeq = ledgerState->getLastClosedLedgerHeader().header.ledgerSeq; inMemorySnapshotForInvariant->assertLastClosedLedger(ledgerSeq); - // TODO: Instead of creating a pointer from fresh state, just make a copy - // from the other LedgerStateSnapshot, when it is safe to do so next commit - auto snapPtr = - std::make_shared(ledgerState, mApp.getMetrics()); - // Note: No race condition acquiring app by reference, as all worker // threads are joined before application destruction. - auto cb = [snapPtr, &app = mApp, inMemorySnapshotForInvariant]() { + // Make sure we make a new snapshot copy since invariant will run on another + // thread. + auto cb = [snap = LedgerStateSnapshot(ledgerState, mApp.getMetrics()), + &app = mApp, inMemorySnapshotForInvariant]() { app.getInvariantManager().runStateSnapshotInvariant( - *snapPtr, *inMemorySnapshotForInvariant, + std::move(snap), *inMemorySnapshotForInvariant, [&app]() { return app.isStopping(); }); }; @@ -1789,7 +1787,6 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData, // Construct a LedgerStateSnapshot from `appliedLedgerState`, which // holds the latest committed state, to avoid relying on // BucketSnapshotManager. - // TODO: Replease this with a copy once safe to do so LedgerStateSnapshot snap(appliedLedgerState, mApp.getMetrics()); mApp.getBucketManager().startBackgroundEvictionScan( std::move(snap), appliedLedgerState->getSorobanConfig()); diff --git a/src/ledger/LedgerStateSnapshot.cpp b/src/ledger/LedgerStateSnapshot.cpp index 13a61f2ab8..e87ccce95e 100644 --- a/src/ledger/LedgerStateSnapshot.cpp +++ b/src/ledger/LedgerStateSnapshot.cpp @@ -401,16 +401,12 @@ CompleteConstLedgerState::getLastClosedHistoryArchiveState() const LedgerStateSnapshot::LedgerStateSnapshot(CompleteConstLedgerStatePtr state, MetricsRegistry& metrics) : mState(state) - , mLiveSnapshot(std::shared_ptr( - new SearchableLiveBucketListSnapshot( - metrics, state->mLiveBucketData, state->mLiveHistoricalSnapshots, - state->mLastClosedLedgerHeader.header.ledgerSeq))) - , mHotArchiveSnapshot( - std::shared_ptr( - new SearchableHotArchiveBucketListSnapshot( - metrics, state->mHotArchiveBucketData, - state->mHotArchiveHistoricalSnapshots, - state->mLastClosedLedgerHeader.header.ledgerSeq))) + , mLiveSnapshot(metrics, state->mLiveBucketData, + state->mLiveHistoricalSnapshots, + state->mLastClosedLedgerHeader.header.ledgerSeq) + , mHotArchiveSnapshot(metrics, state->mHotArchiveBucketData, + state->mHotArchiveHistoricalSnapshots, + state->mLastClosedLedgerHeader.header.ledgerSeq) , mMetrics(metrics) { } @@ -439,7 +435,7 @@ LedgerStateSnapshot::getLedgerSeq() const std::shared_ptr LedgerStateSnapshot::loadLiveEntry(LedgerKey const& k) const { - return mLiveSnapshot->load(k); + return mLiveSnapshot.load(k); } std::vector @@ -447,7 +443,7 @@ LedgerStateSnapshot::loadLiveKeys( std::set const& inKeys, std::string const& label) const { - return mLiveSnapshot->loadKeys(inKeys, label); + return mLiveSnapshot.loadKeys(inKeys, label); } std::optional> @@ -455,22 +451,22 @@ LedgerStateSnapshot::loadLiveKeysFromLedger( std::set const& inKeys, uint32_t ledgerSeq) const { - return mLiveSnapshot->loadKeysFromLedger(inKeys, ledgerSeq); + return mLiveSnapshot.loadKeysFromLedger(inKeys, ledgerSeq); } std::vector LedgerStateSnapshot::loadPoolShareTrustLinesByAccountAndAsset( AccountID const& accountID, Asset const& asset) const { - return mLiveSnapshot->loadPoolShareTrustLinesByAccountAndAsset(accountID, - asset); + return mLiveSnapshot.loadPoolShareTrustLinesByAccountAndAsset(accountID, + asset); } std::vector LedgerStateSnapshot::loadInflationWinners(size_t maxWinners, int64_t minBalance) const { - return mLiveSnapshot->loadInflationWinners(maxWinners, minBalance); + return mLiveSnapshot.loadInflationWinners(maxWinners, minBalance); } std::unique_ptr @@ -481,8 +477,8 @@ LedgerStateSnapshot::scanForEviction(uint32_t ledgerSeq, StateArchivalSettings const& sas, uint32_t ledgerVers) const { - return mLiveSnapshot->scanForEviction(ledgerSeq, metrics, std::move(iter), - std::move(stats), sas, ledgerVers); + return mLiveSnapshot.scanForEviction(ledgerSeq, metrics, std::move(iter), + std::move(stats), sas, ledgerVers); } void @@ -490,7 +486,7 @@ LedgerStateSnapshot::scanLiveEntriesOfType( LedgerEntryType type, std::function callback) const { - mLiveSnapshot->scanForEntriesOfType(type, std::move(callback)); + mLiveSnapshot.scanForEntriesOfType(type, std::move(callback)); } // === Hot Archive BucketList wrapper methods === @@ -498,14 +494,14 @@ LedgerStateSnapshot::scanLiveEntriesOfType( std::shared_ptr LedgerStateSnapshot::loadArchiveEntry(LedgerKey const& k) const { - return mHotArchiveSnapshot->load(k); + return mHotArchiveSnapshot.load(k); } std::vector LedgerStateSnapshot::loadArchiveKeys( std::set const& inKeys) const { - return mHotArchiveSnapshot->loadKeys(inKeys); + return mHotArchiveSnapshot.loadKeys(inKeys); } std::optional> @@ -513,13 +509,13 @@ LedgerStateSnapshot::loadArchiveKeysFromLedger( std::set const& inKeys, uint32_t ledgerSeq) const { - return mHotArchiveSnapshot->loadKeysFromLedger(inKeys, ledgerSeq); + return mHotArchiveSnapshot.loadKeysFromLedger(inKeys, ledgerSeq); } void LedgerStateSnapshot::scanAllArchiveEntries( std::function callback) const { - mHotArchiveSnapshot->scanAllEntries(std::move(callback)); + mHotArchiveSnapshot.scanAllEntries(std::move(callback)); } } diff --git a/src/ledger/LedgerStateSnapshot.h b/src/ledger/LedgerStateSnapshot.h index 0978515505..5dcb333962 100644 --- a/src/ledger/LedgerStateSnapshot.h +++ b/src/ledger/LedgerStateSnapshot.h @@ -125,9 +125,8 @@ class LedgerTxnReadOnly : public AbstractLedgerStateSnapshot class LedgerStateSnapshot { std::shared_ptr mState; - std::shared_ptr mLiveSnapshot; - std::shared_ptr - mHotArchiveSnapshot; + SearchableLiveBucketListSnapshot mLiveSnapshot; + SearchableHotArchiveBucketListSnapshot mHotArchiveSnapshot; std::reference_wrapper mMetrics; public: @@ -135,15 +134,6 @@ class LedgerStateSnapshot explicit LedgerStateSnapshot(CompleteConstLedgerStatePtr state, MetricsRegistry& metrics); - // Default copy/move - // TODO: This is currently NOT thread sade, since we just do pointer copies - // for the state snapshots. Next commit will turn the snapshots into value - // types with correct copy semantics. - LedgerStateSnapshot(LedgerStateSnapshot const&) = default; - LedgerStateSnapshot& operator=(LedgerStateSnapshot const&) = default; - LedgerStateSnapshot(LedgerStateSnapshot&&) = default; - LedgerStateSnapshot& operator=(LedgerStateSnapshot&&) = default; - CompleteConstLedgerState const& getState() const; LedgerHeader const& getLedgerHeader() const; uint32_t getLedgerSeq() const; diff --git a/src/main/QueryServer.h b/src/main/QueryServer.h index b7e81a8104..dd1466733d 100644 --- a/src/main/QueryServer.h +++ b/src/main/QueryServer.h @@ -10,8 +10,6 @@ #include "ledger/LedgerStateSnapshot.h" #include #include -#include -#include #include #include #include diff --git a/src/overlay/OverlayManagerImpl.cpp b/src/overlay/OverlayManagerImpl.cpp index 85b63f3205..d79d9bf9fa 100644 --- a/src/overlay/OverlayManagerImpl.cpp +++ b/src/overlay/OverlayManagerImpl.cpp @@ -10,7 +10,6 @@ #include "crypto/ShortHash.h" #include "database/Database.h" #include "herder/Herder.h" -#include "ledger/LedgerManager.h" #include "lib/util/finally.h" #include "lib/util/stdrandom.h" #include "main/Application.h" diff --git a/src/transactions/ParallelApplyUtils.cpp b/src/transactions/ParallelApplyUtils.cpp index d230f13364..46e7c49f47 100644 --- a/src/transactions/ParallelApplyUtils.cpp +++ b/src/transactions/ParallelApplyUtils.cpp @@ -306,9 +306,8 @@ GlobalParallelApplyLedgerState::GlobalParallelApplyLedgerState( , mInMemorySorobanState(inMemoryState) , mSorobanConfig(sorobanConfig) { - releaseAssertOrThrow(ltx.getHeader().ledgerSeq == - getSnapshotLedgerSeq() + 1); + mSnapshot.getLedgerSeq() + 1); // From now on, we will be using globalState, liveSnapshots, and the // hotArchive to collect all entries. Before we continue though, we need to diff --git a/src/transactions/RestoreFootprintOpFrame.cpp b/src/transactions/RestoreFootprintOpFrame.cpp index 315cfdb8ed..a6c549b360 100644 --- a/src/transactions/RestoreFootprintOpFrame.cpp +++ b/src/transactions/RestoreFootprintOpFrame.cpp @@ -299,7 +299,7 @@ class RestoreFootprintParallelApplyHelper : RestoreFootprintApplyHelper(app, res, refundableFeeTracker, opMeta, opFrame, threadState.getSorobanConfig()) , ParallelLedgerAccessHelper(threadState, ledgerInfo) - , mSnapshot(app.copyLedgerStateSnapshot()) + , mSnapshot(threadState.getSnapshot()) { } From 6bdc178f32c08c9511c011d76dbb157265b1ecb8 Mon Sep 17 00:00:00 2001 From: Garand Tyson Date: Fri, 13 Feb 2026 18:36:17 -0800 Subject: [PATCH 4/5] Remove BucketSnapshotManager and manage snapshot from LedgerManager --- Builds/VisualStudio/stellar-core.vcxproj | 2 - .../VisualStudio/stellar-core.vcxproj.filters | 6 - src/bucket/BucketListSnapshot.h | 8 +- src/bucket/BucketManager.cpp | 25 +- src/bucket/BucketManager.h | 8 +- src/bucket/BucketSnapshotManager.cpp | 146 --------- src/bucket/BucketSnapshotManager.h | 107 ------ src/bucket/test/BucketIndexTests.cpp | 47 +-- src/bucket/test/BucketListTests.cpp | 10 +- src/bucket/test/BucketTestUtils.cpp | 42 ++- src/bucket/test/BucketTestUtils.h | 4 +- src/herder/HerderImpl.cpp | 3 - src/invariant/ArchivedStateConsistency.cpp | 2 +- src/invariant/ArchivedStateConsistency.h | 2 +- src/invariant/BucketListStateConsistency.cpp | 2 +- src/invariant/BucketListStateConsistency.h | 2 +- src/invariant/ConservationOfLumens.cpp | 7 +- src/invariant/ConservationOfLumens.h | 2 +- src/invariant/Invariant.h | 5 +- src/invariant/InvariantManager.h | 5 +- src/invariant/InvariantManagerImpl.cpp | 10 +- src/invariant/InvariantManagerImpl.h | 5 +- .../test/ConservationOfLumensTests.cpp | 15 +- src/invariant/test/InvariantTests.cpp | 48 ++- src/ledger/InMemorySorobanState.cpp | 2 +- src/ledger/InMemorySorobanState.h | 4 +- src/ledger/LedgerManager.h | 18 +- src/ledger/LedgerManagerImpl.cpp | 305 ++++++++++++------ src/ledger/LedgerManagerImpl.h | 81 +++-- src/ledger/LedgerStateSnapshot.cpp | 185 ++++++----- src/ledger/LedgerStateSnapshot.h | 112 +++---- src/ledger/LedgerTxn.cpp | 8 +- src/ledger/LedgerTxnImpl.h | 6 +- src/ledger/P23HotArchiveBug.cpp | 6 +- src/ledger/SharedModuleCacheCompiler.cpp | 2 +- src/ledger/SharedModuleCacheCompiler.h | 4 +- src/main/AppConnector.cpp | 15 +- src/main/AppConnector.h | 1 + src/main/ApplicationUtils.cpp | 6 +- src/main/CommandHandler.cpp | 2 - src/main/QueryServer.h | 1 - src/main/test/QueryServerTests.cpp | 1 - src/overlay/OverlayManagerImpl.cpp | 6 +- src/simulation/ApplyLoad.cpp | 5 - src/simulation/test/LoadGeneratorTests.cpp | 6 +- .../InvokeHostFunctionOpFrame.cpp | 13 +- src/transactions/ParallelApplyUtils.cpp | 12 +- src/transactions/ParallelApplyUtils.h | 10 +- src/transactions/RestoreFootprintOpFrame.cpp | 2 +- .../test/InvokeHostFunctionTests.cpp | 44 +-- src/transactions/test/ParallelApplyTest.cpp | 8 +- src/transactions/test/SorobanTxTestUtils.cpp | 7 +- 52 files changed, 593 insertions(+), 792 deletions(-) delete mode 100644 src/bucket/BucketSnapshotManager.cpp delete mode 100644 src/bucket/BucketSnapshotManager.h diff --git a/Builds/VisualStudio/stellar-core.vcxproj b/Builds/VisualStudio/stellar-core.vcxproj index 0aa028f9d2..933a344b6f 100644 --- a/Builds/VisualStudio/stellar-core.vcxproj +++ b/Builds/VisualStudio/stellar-core.vcxproj @@ -464,7 +464,6 @@ exit /b 0 - @@ -941,7 +940,6 @@ exit /b 0 - diff --git a/Builds/VisualStudio/stellar-core.vcxproj.filters b/Builds/VisualStudio/stellar-core.vcxproj.filters index 7782662115..5be5dae624 100644 --- a/Builds/VisualStudio/stellar-core.vcxproj.filters +++ b/Builds/VisualStudio/stellar-core.vcxproj.filters @@ -1278,9 +1278,6 @@ bucket - - bucket - overlay\tests @@ -2376,9 +2373,6 @@ main - - bucket - overlay diff --git a/src/bucket/BucketListSnapshot.h b/src/bucket/BucketListSnapshot.h index fcc4e4ba3a..371932456a 100644 --- a/src/bucket/BucketListSnapshot.h +++ b/src/bucket/BucketListSnapshot.h @@ -33,7 +33,6 @@ class Timer; namespace stellar { -class BucketSnapshotManager; struct EvictionMetrics; struct EvictionResultCandidates; struct EvictionResultEntry; @@ -210,8 +209,8 @@ class SearchableLiveBucketListSnapshot UnorderedSet& keysInEvictableEntries) const; public: - SearchableLiveBucketListSnapshot( - SearchableLiveBucketListSnapshot const&) = default; + SearchableLiveBucketListSnapshot(SearchableLiveBucketListSnapshot const&) = + default; std::vector loadKeys(std::set const& inKeys, @@ -235,7 +234,7 @@ class SearchableLiveBucketListSnapshot LedgerEntryType type, std::function callback) const; - friend class BucketSnapshotManager; + friend class BucketSnapshotState; friend class CompleteConstLedgerState; friend class LedgerStateSnapshot; }; @@ -264,7 +263,6 @@ class SearchableHotArchiveBucketListSnapshot void scanAllEntries( std::function callback) const; - friend class BucketSnapshotManager; friend class LedgerStateSnapshot; }; diff --git a/src/bucket/BucketManager.cpp b/src/bucket/BucketManager.cpp index c44576b8ab..f190dbabfb 100644 --- a/src/bucket/BucketManager.cpp +++ b/src/bucket/BucketManager.cpp @@ -6,7 +6,6 @@ #include "bucket/BucketInputIterator.h" #include "bucket/BucketManager.h" #include "bucket/BucketOutputIterator.h" -#include "bucket/BucketSnapshotManager.h" #include "bucket/BucketUtils.h" #include "bucket/HotArchiveBucket.h" #include "bucket/HotArchiveBucketList.h" @@ -103,9 +102,6 @@ BucketManager::initialize() mLiveBucketList = std::make_unique(); mHotArchiveBucketList = std::make_unique(); - mSnapshotManager = std::make_unique( - mAppConnector, *mLiveBucketList, *mHotArchiveBucketList, LedgerHeader(), - mConfig.QUERY_SNAPSHOT_LEDGERS); // Create persistent publish directories // Note: HISTORY_FILE_TYPE_BUCKET is already tracked by BucketList in @@ -136,7 +132,6 @@ BucketManager::BucketManager(AppConnector& appConnector) : mAppConnector(appConnector) , mLiveBucketList(nullptr) , mHotArchiveBucketList(nullptr) - , mSnapshotManager(nullptr) , mTmpDirManager(nullptr) , mWorkDir(nullptr) , mLockedBucketDir(nullptr) @@ -318,13 +313,6 @@ BucketManager::getHotArchiveBucketList() return *mHotArchiveBucketList; } -BucketSnapshotManager& -BucketManager::getBucketSnapshotManager() const -{ - releaseAssert(mSnapshotManager); - return *mSnapshotManager; -} - medida::Timer& BucketManager::getMergeTimer() { @@ -1160,10 +1148,9 @@ BucketManager::maybeSetIndex( } void -BucketManager::startBackgroundEvictionScan(LedgerStateSnapshot lclSnapshot, +BucketManager::startBackgroundEvictionScan(ApplyLedgerStateSnapshot lclSnapshot, SorobanNetworkConfig const& cfg) { - releaseAssert(mSnapshotManager); releaseAssert(!mEvictionFuture.valid()); releaseAssert(mEvictionStatistics); @@ -1192,7 +1179,7 @@ BucketManager::startBackgroundEvictionScan(LedgerStateSnapshot lclSnapshot, EvictedStateVectors BucketManager::resolveBackgroundEvictionScan( - LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, + ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, LedgerKeySet const& modifiedKeys) { ZoneScoped; @@ -1211,9 +1198,7 @@ BucketManager::resolveBackgroundEvictionScan( // candidates; this function later validates them by re-checking the // Soroban config and reloading the latest TTLs. Any entry restored in // the same ledger will be rejected by eviction validation logic. - startBackgroundEvictionScan( - LedgerStateSnapshot(lclSnapshot), - networkConfig); + startBackgroundEvictionScan(lclSnapshot, networkConfig); } auto evictionCandidates = mEvictionFuture.get(); @@ -1223,9 +1208,7 @@ BucketManager::resolveBackgroundEvictionScan( if (!evictionCandidates->isValid(ledgerSeq, ledgerVers, networkConfig.stateArchivalSettings())) { - startBackgroundEvictionScan( - LedgerStateSnapshot(lclSnapshot), - networkConfig); + startBackgroundEvictionScan(lclSnapshot, networkConfig); evictionCandidates = mEvictionFuture.get(); } diff --git a/src/bucket/BucketManager.h b/src/bucket/BucketManager.h index 517ef2d9d8..2a768db71a 100644 --- a/src/bucket/BucketManager.h +++ b/src/bucket/BucketManager.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -40,7 +39,6 @@ class AppConnector; class Bucket; class LiveBucketList; class HotArchiveBucketList; -class BucketSnapshotManager; class SearchableLiveBucketListSnapshot; struct BucketEntryCounters; enum class LedgerEntryTypeAndDurability : uint32_t; @@ -84,7 +82,6 @@ class BucketManager : NonMovableOrCopyable AppConnector& mAppConnector; std::unique_ptr mLiveBucketList; std::unique_ptr mHotArchiveBucketList; - std::unique_ptr mSnapshotManager; std::unique_ptr mTmpDirManager; std::unique_ptr mWorkDir; BucketMapT mSharedLiveBuckets; @@ -234,7 +231,6 @@ class BucketManager : NonMovableOrCopyable std::string const& getBucketDir() const; LiveBucketList& getLiveBucketList(); HotArchiveBucketList& getHotArchiveBucketList(); - BucketSnapshotManager& getBucketSnapshotManager() const; bool renameBucketDirFile(std::filesystem::path const& src, std::filesystem::path const& dst); @@ -342,7 +338,7 @@ class BucketManager : NonMovableOrCopyable // Scans BucketList for non-live entries to evict starting at the entry // pointed to by EvictionIterator. Evicts until `maxEntriesToEvict` entries // have been evicted or maxEvictionScanSize bytes have been scanned. - void startBackgroundEvictionScan(LedgerStateSnapshot lclSnapshot, + void startBackgroundEvictionScan(ApplyLedgerStateSnapshot lclSnapshot, SorobanNetworkConfig const& networkConfig); // Returns a pair of vectors representing entries evicted this ledger, where @@ -351,7 +347,7 @@ class BucketManager : NonMovableOrCopyable // ContractCode). Note that when an entry is archived, its TTL key will be // included in the deleted keys vector. EvictedStateVectors - resolveBackgroundEvictionScan(LedgerStateSnapshot const& lclSnapshot, + resolveBackgroundEvictionScan(ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, LedgerKeySet const& modifiedKeys); diff --git a/src/bucket/BucketSnapshotManager.cpp b/src/bucket/BucketSnapshotManager.cpp deleted file mode 100644 index f3f44a6510..0000000000 --- a/src/bucket/BucketSnapshotManager.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2024 Stellar Development Foundation and contributors. Licensed -// under the Apache License, Version 2.0. See the COPYING file at the root -// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 - -#include "bucket/BucketSnapshotManager.h" -#include "bucket/BucketListSnapshot.h" -#include "bucket/HotArchiveBucketList.h" -#include "bucket/LiveBucketList.h" -#include "ledger/LedgerStateSnapshot.h" -#include "main/AppConnector.h" -#include "util/GlobalChecks.h" - -namespace stellar -{ - -BucketSnapshotManager::BucketSnapshotManager( - AppConnector& app, LiveBucketList const& liveBL, - HotArchiveBucketList const& hotArchiveBL, LedgerHeader const& header, - uint32_t numHistoricalLedgers) - : mAppConnector(app) - , mCurrLiveSnapshot( - std::make_shared>(liveBL)) - , mCurrHotArchiveSnapshot( - std::make_shared>( - hotArchiveBL)) - , mCurrHeader(header) - , mNumHistoricalSnapshots(numHistoricalLedgers) -{ - releaseAssert(threadIsMain()); - releaseAssert(mCurrLiveSnapshot); - releaseAssert(mCurrHotArchiveSnapshot); - - // Initialize mCompleteState so that copyLedgerStateSnapshot() is valid - // even before the first ledger close calls setCompleteState(). - // TODO: make less sketchy - LedgerHeaderHistoryEntry lcl; - lcl.header = header; - HistoryArchiveState has; - has.currentLedger = header.ledgerSeq; - mCompleteState = std::shared_ptr( - new CompleteConstLedgerState( - mCurrLiveSnapshot, mLiveHistoricalSnapshots, - mCurrHotArchiveSnapshot, mHotArchiveHistoricalSnapshots, lcl, has)); -} - -std::map const>> -BucketSnapshotManager::getLiveHistoricalSnapshots() const -{ - SharedLockShared guard(mSnapshotMutex); - return mLiveHistoricalSnapshots; -} - -std::map const>> -BucketSnapshotManager::getHotArchiveHistoricalSnapshots() const -{ - SharedLockShared guard(mSnapshotMutex); - return mHotArchiveHistoricalSnapshots; -} - -void -BucketSnapshotManager::updateCurrentSnapshot( - LiveBucketList const& liveBL, HotArchiveBucketList const& hotArchiveBL, - LedgerHeader const& header) -{ - auto updateSnapshot = [numHistoricalSnapshots = mNumHistoricalSnapshots]( - auto& currentSnapshot, auto& historicalSnapshots, - auto newSnapshot, uint32_t currLedgerSeq) { - releaseAssert(newSnapshot); - - // First update historical snapshots - if (numHistoricalSnapshots != 0 && currentSnapshot) - { - // If historical snapshots are full, delete the oldest one - if (historicalSnapshots.size() == numHistoricalSnapshots) - { - historicalSnapshots.erase(historicalSnapshots.begin()); - } - - historicalSnapshots.emplace(currLedgerSeq, - std::move(currentSnapshot)); - } - - currentSnapshot = std::move(newSnapshot); - }; - - auto newLiveSnapshot = - std::make_shared>(liveBL); - auto newHotArchiveSnapshot = - std::make_shared>( - hotArchiveBL); - - // Updating canonical snapshots requires exclusive write access - SharedLockExclusive guard(mSnapshotMutex); - releaseAssert(header.ledgerSeq >= mCurrHeader.ledgerSeq); - updateSnapshot(mCurrLiveSnapshot, mLiveHistoricalSnapshots, - std::move(newLiveSnapshot), mCurrHeader.ledgerSeq); - updateSnapshot(mCurrHotArchiveSnapshot, mHotArchiveHistoricalSnapshots, - std::move(newHotArchiveSnapshot), mCurrHeader.ledgerSeq); - mCurrHeader = header; - - // Rebuild mCompleteState from the updated raw data so that - // copyLedgerStateSnapshot() always returns fresh state. This uses the - // private constructor that skips Soroban config loading; the normal - // ledger-close path will follow up with setCompleteState() which - // provides the full state including Soroban config. - // TODO: make less sketchy - LedgerHeaderHistoryEntry lcl; - lcl.header = header; - HistoryArchiveState has; - has.currentLedger = header.ledgerSeq; - mCompleteState = std::shared_ptr( - new CompleteConstLedgerState( - mCurrLiveSnapshot, mLiveHistoricalSnapshots, - mCurrHotArchiveSnapshot, mHotArchiveHistoricalSnapshots, lcl, has)); -} - -void -BucketSnapshotManager::setCompleteState(CompleteConstLedgerStatePtr state) -{ - SharedLockExclusive guard(mSnapshotMutex); - mCompleteState = std::move(state); -} - -LedgerStateSnapshot -BucketSnapshotManager::copyLedgerStateSnapshot() const -{ - SharedLockShared guard(mSnapshotMutex); - releaseAssert(mCompleteState); - return LedgerStateSnapshot(mCompleteState, mAppConnector.getMetrics()); -} - -void -BucketSnapshotManager::maybeUpdateLedgerStateSnapshot( - LedgerStateSnapshot& snapshot) const -{ - SharedLockShared guard(mSnapshotMutex); - releaseAssert(mCompleteState); - if (snapshot.getLedgerSeq() != - mCompleteState->getLastClosedLedgerHeader().header.ledgerSeq) - { - snapshot = - LedgerStateSnapshot(mCompleteState, mAppConnector.getMetrics()); - } -} -} diff --git a/src/bucket/BucketSnapshotManager.h b/src/bucket/BucketSnapshotManager.h deleted file mode 100644 index ed44cb33dd..0000000000 --- a/src/bucket/BucketSnapshotManager.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -// Copyright 2024 Stellar Development Foundation and contributors. Licensed -// under the Apache License, Version 2.0. See the COPYING file at the root -// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 - -#include "bucket/BucketListSnapshot.h" -#include "bucket/HotArchiveBucket.h" -#include "bucket/LiveBucket.h" -#include "ledger/LedgerStateSnapshot.h" -#include "util/NonCopyable.h" -#include "util/ThreadAnnotations.h" - -#include -#include - -namespace medida -{ -class MetricsRegistry; -} - -namespace stellar -{ - -class AppConnector; -class LiveBucketList; -class HotArchiveBucketList; - -// This class serves as the boundary between non-threadsafe singleton classes -// (BucketManager, BucketList, Metrics, etc) and threadsafe, parallel BucketList -// snapshots. -class BucketSnapshotManager : NonMovableOrCopyable -{ - private: - AppConnector& mAppConnector; - - // Lock must be held when accessing any member variables holding snapshots - mutable SharedMutex mSnapshotMutex; - - // Snapshot that is maintained and periodically updated by BucketManager on - // the main thread. When background threads need to generate or refresh a - // snapshot, they will copy this snapshot. - std::shared_ptr const> - mCurrLiveSnapshot GUARDED_BY(mSnapshotMutex){}; - std::shared_ptr const> - mCurrHotArchiveSnapshot GUARDED_BY(mSnapshotMutex){}; - - // LedgerHeader corresponding to the current snapshots. Stored separately - // because BucketListSnapshotData no longer contains the header. - LedgerHeader mCurrHeader GUARDED_BY(mSnapshotMutex){}; - - // ledgerSeq that the snapshot is based on -> snapshot - std::map const>> - mLiveHistoricalSnapshots GUARDED_BY(mSnapshotMutex); - std::map const>> - mHotArchiveHistoricalSnapshots GUARDED_BY(mSnapshotMutex); - - uint32_t const mNumHistoricalSnapshots; - - // The current complete ledger state, set by LedgerManager after each - // ledger close. Used by copyLedgerStateSnapshot() to construct - // LedgerStateSnapshot instances. - CompleteConstLedgerStatePtr mCompleteState GUARDED_BY(mSnapshotMutex){}; - - public: - // Called by main thread to update snapshots whenever the BucketList - // is updated - void updateCurrentSnapshot(LiveBucketList const& liveBL, - HotArchiveBucketList const& hotArchiveBL, - LedgerHeader const& header) - LOCKS_EXCLUDED(mSnapshotMutex); - - // numHistoricalLedgers is the number of historical snapshots that the - // snapshot manager will maintain. If numHistoricalLedgers is 5, snapshots - // will be capable of querying state from ledger [lcl, lcl - 5]. - BucketSnapshotManager(AppConnector& app, LiveBucketList const& liveBL, - HotArchiveBucketList const& hotArchiveBL, - LedgerHeader const& header, - uint32_t numHistoricalLedgers); - - // Store the complete ledger state for use by copyLedgerStateSnapshot(). - // Called by LedgerManager after each ledger close. - void setCompleteState(CompleteConstLedgerStatePtr state) - LOCKS_EXCLUDED(mSnapshotMutex); - - // Access raw snapshot data and historical snapshots. Used by - // CompleteConstLedgerState construction. - std::map const>> - getLiveHistoricalSnapshots() const LOCKS_EXCLUDED(mSnapshotMutex); - std::map const>> - getHotArchiveHistoricalSnapshots() const LOCKS_EXCLUDED(mSnapshotMutex); - - // Create a LedgerStateSnapshot containing both live and hot archive - // snapshots plus the current header. Thread-safe. - LedgerStateSnapshot copyLedgerStateSnapshot() const - LOCKS_EXCLUDED(mSnapshotMutex); - - // Refresh `snapshot` if its ledger seq differs from the current snapshot. - // No-op otherwise. - void maybeUpdateLedgerStateSnapshot(LedgerStateSnapshot& snapshot) const - LOCKS_EXCLUDED(mSnapshotMutex); -}; -} diff --git a/src/bucket/test/BucketIndexTests.cpp b/src/bucket/test/BucketIndexTests.cpp index b10f3af291..4bb07c4949 100644 --- a/src/bucket/test/BucketIndexTests.cpp +++ b/src/bucket/test/BucketIndexTests.cpp @@ -6,13 +6,12 @@ // concerning key-value lookup based on the BucketList. #include "bucket/BucketIndexUtils.h" -#include "bucket/BucketInputIterator.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "bucket/BucketUtils.h" #include "bucket/LiveBucket.h" #include "bucket/LiveBucketList.h" #include "bucket/test/BucketTestUtils.h" +#include "ledger/LedgerManager.h" #include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTypeUtils.h" #include "ledger/test/LedgerTestUtils.h" @@ -216,8 +215,7 @@ class BucketIndexTest } while (ledger < mApp->getConfig().QUERY_SNAPSHOT_LEDGERS + 2); ++ledger; - auto snap = - getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); + auto snap = getApp().getLedgerManager().copyLedgerStateSnapshot(); auto lk = LedgerEntryKey(canonicalEntry); auto currentLoadedEntry = snap.loadLiveEntry(lk); @@ -413,8 +411,7 @@ class BucketIndexTest virtual void run(std::optional expectedHitRate = std::nullopt) { - auto snap = - getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); + auto snap = getApp().getLedgerManager().copyLedgerStateSnapshot(); auto& hitMeter = getBM().getCacheHitMeter(); auto& missMeter = getBM().getCacheMissMeter(); @@ -543,8 +540,7 @@ class BucketIndexTest virtual void runPerf(size_t n) { - auto snap = - getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); + auto snap = getApp().getLedgerManager().copyLedgerStateSnapshot(); for (size_t i = 0; i < n; ++i) { LedgerKeySet searchSubset; @@ -583,8 +579,7 @@ class BucketIndexTest void testInvalidKeys() { - auto snap = - getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); + auto snap = getApp().getLedgerManager().copyLedgerStateSnapshot(); // Load should return empty vector for keys not in bucket list auto keysNotInBL = @@ -753,8 +748,7 @@ class BucketIndexPoolShareTest : public BucketIndexTest virtual void run(std::optional expectedHitRate = std::nullopt) override { - auto snap = - getBM().getBucketSnapshotManager().copyLedgerStateSnapshot(); + auto snap = getApp().getLedgerManager().copyLedgerStateSnapshot(); auto loadResult = snap.loadPoolShareTrustLinesByAccountAndAsset( mAccountToSearch.accountID, mAssetToSearch); validateResults(mTestEntries, loadResult); @@ -1112,9 +1106,8 @@ TEST_CASE("soroban cache population", "[soroban][bucketindex]") auto const& inMemorySorobanState = lm.getInMemorySorobanStateForTesting(); - auto snapshot = test.getBM() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snapshot = + test.getApp().getLedgerManager().copyLedgerStateSnapshot(); // First, test that the cache is maintained correctly via `addBatch` REQUIRE(codeEntries.size() == @@ -1322,9 +1315,7 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") auto ledger = 1; // Use snapshot across ledger to test update behavior - auto snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = app->getLedgerManager().copyLedgerStateSnapshot(); auto checkLoad = [&](LedgerKey const& k, @@ -1397,9 +1388,7 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") HotArchiveBucket::FIRST_PROTOCOL_SUPPORTING_PERSISTENT_EVICTION); addHotArchiveBatchAndUpdateSnapshot(*app, header, archivedEntries, restoredEntries); - snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + snap = app->getLedgerManager().copyLedgerStateSnapshot(); checkResult(); // Add a few batches so that entries are no longer in the top bucket @@ -1407,9 +1396,7 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") { header.ledgerSeq += 1; addHotArchiveBatchAndUpdateSnapshot(*app, header, {}, {}); - snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + snap = app->getLedgerManager().copyLedgerStateSnapshot(); } // Shadow entries via liveEntry @@ -1419,9 +1406,7 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") header.ledgerSeq += 1; addHotArchiveBatchAndUpdateSnapshot(*app, header, {}, {liveShadow1, liveShadow2}); - snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + snap = app->getLedgerManager().copyLedgerStateSnapshot(); // Point load for (auto const& k : {liveShadow1, liveShadow2}) @@ -1440,9 +1425,7 @@ TEST_CASE("hot archive bucket lookups", "[bucket][bucketindex][archive]") header.ledgerSeq += 1; addHotArchiveBatchAndUpdateSnapshot(*app, header, {archivedShadow}, {}); - snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + snap = app->getLedgerManager().copyLedgerStateSnapshot(); // Point load auto entryPtr = snap.loadArchiveEntry(LedgerEntryKey(archivedShadow)); @@ -1620,9 +1603,7 @@ TEST_CASE("getRangeForType bounds verification", "[bucket][bucketindex]") .getCurr(); verifyIndexBounds(bucket); - auto snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = app->getLedgerManager().copyLedgerStateSnapshot(); auto verifyScanForType = [&](LedgerEntryType type, diff --git a/src/bucket/test/BucketListTests.cpp b/src/bucket/test/BucketListTests.cpp index 05d4483121..b58c7788bf 100644 --- a/src/bucket/test/BucketListTests.cpp +++ b/src/bucket/test/BucketListTests.cpp @@ -6,14 +6,9 @@ // concerning the sizes of levels in it, shadowing, the propagation and // archival of entries as they move between levels, and so forth. -// ASIO is somewhat particular about when it gets included -- it wants to be the -// first to include -- so we try to include it before everything -// else. -#include "util/asio.h" #include "bucket/BucketInputIterator.h" #include "bucket/BucketManager.h" #include "bucket/BucketOutputIterator.h" -#include "bucket/BucketSnapshotManager.h" #include "bucket/HotArchiveBucket.h" #include "bucket/HotArchiveBucketList.h" #include "bucket/LiveBucket.h" @@ -26,7 +21,6 @@ #include "lib/util/stdrandom.h" #include "main/Application.h" #include "main/Config.h" -#include "test/Catch2.h" #include "test/TestUtils.h" #include "test/test.h" #include "util/Math.h" @@ -1216,7 +1210,7 @@ TEST_CASE_VERSIONS("eviction scan", "[bucketlist][archival][soroban]") if (!tempOnly) { auto archiveSnap = - bm.getBucketSnapshotManager().copyLedgerStateSnapshot(); + app->getLedgerManager().copyLedgerStateSnapshot(); // Check that persisted entries have been inserted into // HotArchive @@ -1728,7 +1722,7 @@ TEST_CASE_VERSIONS("Searchable BucketListDB snapshots", "[bucketlist]") } closeLedger(*app); - auto blSnap = bm.getBucketSnapshotManager().copyLedgerStateSnapshot(); + auto blSnap = app->getLedgerManager().copyLedgerStateSnapshot(); // Snapshot should automatically update with latest version auto loadedEntry = blSnap.loadLiveEntry(LedgerEntryKey(entry)); diff --git a/src/bucket/test/BucketTestUtils.cpp b/src/bucket/test/BucketTestUtils.cpp index f1753c0604..8b40088fa0 100644 --- a/src/bucket/test/BucketTestUtils.cpp +++ b/src/bucket/test/BucketTestUtils.cpp @@ -5,11 +5,11 @@ #include "BucketTestUtils.h" #include "bucket/BucketInputIterator.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "bucket/HotArchiveBucket.h" #include "bucket/LiveBucket.h" #include "crypto/Hex.h" #include "herder/Herder.h" +#include "ledger/LedgerManager.h" #include "ledger/LedgerTxn.h" #include "main/Application.h" #include "test/test.h" @@ -45,8 +45,7 @@ addLiveBatchAndUpdateSnapshot(Application& app, LedgerHeader header, liveBl.addBatch(app, header.ledgerSeq, header.ledgerVersion, initEntries, liveEntries, deadEntries); - app.getBucketManager().getBucketSnapshotManager().updateCurrentSnapshot( - liveBl, app.getBucketManager().getHotArchiveBucketList(), header); + app.getLedgerManager().updateCanonicalStateForTesting(header); } void @@ -58,8 +57,7 @@ addHotArchiveBatchAndUpdateSnapshot( auto& hotArchiveBl = app.getBucketManager().getHotArchiveBucketList(); hotArchiveBl.addBatch(app, header.ledgerSeq, header.ledgerVersion, archiveEntries, restoredEntries); - app.getBucketManager().getBucketSnapshotManager().updateCurrentSnapshot( - app.getBucketManager().getLiveBucketList(), hotArchiveBl, header); + app.getLedgerManager().updateCanonicalStateForTesting(header); } void @@ -174,9 +172,9 @@ countEntries(std::shared_ptr bucket) template size_t countEntries(std::shared_ptr bucket); template size_t countEntries(std::shared_ptr bucket); -void +std::optional LedgerManagerForBucketTests::finalizeLedgerTxnChanges( - LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, + ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, LedgerHeader lh, uint32_t initialLedgerVers) { @@ -261,14 +259,9 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges( ltx, mApp); } - // Load the final Soroban config just before sealing the ltx. - std::optional finalSorobanConfig; - if (protocolVersionStartsFrom(lh.ledgerVersion, - SOROBAN_PROTOCOL_VERSION)) - { - finalSorobanConfig = - std::make_optional(SorobanNetworkConfig::loadFromLedger(ltx)); - } + // Seal the ltx and collect its entries, but don't load Soroban + // config yet -- test entries (including network config like eviction + // iterator) are added directly to the BucketList below, not via ltx. ltx.getAllEntries(init, live, dead); // Add dead entries from ltx to entries that will be added to BucketList @@ -309,6 +302,22 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges( mApp.getBucketManager().addLiveBatch( mApp, lh, mTestInitEntries, mTestLiveEntries, mTestDeadEntries); + // Load the final Soroban config AFTER addLiveBatch so that test + // entries (e.g. stateArchivalSettings with eviction iterator) are + // visible. We load from a BucketList snapshot rather than ltx + // because test entries bypass ltx entirely. + std::optional finalSorobanConfig; + if (protocolVersionStartsFrom(lh.ledgerVersion, + SOROBAN_PROTOCOL_VERSION)) + { + auto liveData = + std::make_shared>( + mApp.getBucketManager().getLiveBucketList()); + LedgerSnapshot ls(mApp.getMetrics(), std::move(liveData), lh); + finalSorobanConfig = + std::make_optional(SorobanNetworkConfig::loadFromLedger(ls)); + } + mApplyState.updateInMemorySorobanState( mTestInitEntries, mTestLiveEntries, mTestDeadEntries, lh, finalSorobanConfig); @@ -321,10 +330,11 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges( mTestArchiveEntries.clear(); mTestRestoredEntries.clear(); mTestDeletedEntries.clear(); + return finalSorobanConfig; } else { - LedgerManagerImpl::finalizeLedgerTxnChanges( + return LedgerManagerImpl::finalizeLedgerTxnChanges( lclSnapshot, ltx, ledgerCloseMeta, lh, initialLedgerVers); } } diff --git a/src/bucket/test/BucketTestUtils.h b/src/bucket/test/BucketTestUtils.h index 73552c0671..003d401d54 100644 --- a/src/bucket/test/BucketTestUtils.h +++ b/src/bucket/test/BucketTestUtils.h @@ -72,8 +72,8 @@ class LedgerManagerForBucketTests : public LedgerManagerImpl std::vector mTestDeletedEntries; protected: - void finalizeLedgerTxnChanges( - LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, + std::optional finalizeLedgerTxnChanges( + ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, LedgerHeader lh, uint32_t initialLedgerVers) override; diff --git a/src/herder/HerderImpl.cpp b/src/herder/HerderImpl.cpp index 644e03428a..8fa036f0e7 100644 --- a/src/herder/HerderImpl.cpp +++ b/src/herder/HerderImpl.cpp @@ -5,7 +5,6 @@ #include "herder/HerderImpl.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "crypto/Hex.h" #include "crypto/KeyUtils.h" #include "crypto/SHA.h" @@ -29,10 +28,8 @@ #include "medida/counter.h" #include "medida/meter.h" #include "overlay/OverlayManager.h" -#include "process/ProcessManager.h" #include "scp/LocalNode.h" #include "scp/Slot.h" -#include "transactions/MutableTransactionResult.h" #include "transactions/TransactionUtils.h" #include "util/DebugMetaUtils.h" #include "util/Decoder.h" diff --git a/src/invariant/ArchivedStateConsistency.cpp b/src/invariant/ArchivedStateConsistency.cpp index 808d3bee67..acaa41403c 100644 --- a/src/invariant/ArchivedStateConsistency.cpp +++ b/src/invariant/ArchivedStateConsistency.cpp @@ -38,7 +38,7 @@ ArchivedStateConsistency::getName() const std::string ArchivedStateConsistency::checkOnLedgerCommit( - LedgerStateSnapshot const& lclSnapshot, + ApplyLedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, diff --git a/src/invariant/ArchivedStateConsistency.h b/src/invariant/ArchivedStateConsistency.h index 13cf995319..795c2d90be 100644 --- a/src/invariant/ArchivedStateConsistency.h +++ b/src/invariant/ArchivedStateConsistency.h @@ -34,7 +34,7 @@ class ArchivedStateConsistency : public Invariant virtual std::string getName() const override; virtual std::string checkOnLedgerCommit( - LedgerStateSnapshot const& lclSnapshot, + ApplyLedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, diff --git a/src/invariant/BucketListStateConsistency.cpp b/src/invariant/BucketListStateConsistency.cpp index 25178549f0..53bd905c86 100644 --- a/src/invariant/BucketListStateConsistency.cpp +++ b/src/invariant/BucketListStateConsistency.cpp @@ -36,7 +36,7 @@ BucketListStateConsistency::BucketListStateConsistency() : Invariant(true) // 7. The cached total entry sizes match the sum of actual entry sizes std::string BucketListStateConsistency::checkSnapshot( - LedgerStateSnapshot const& snapshot, + ApplyLedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) { diff --git a/src/invariant/BucketListStateConsistency.h b/src/invariant/BucketListStateConsistency.h index a79904318e..e8595470e1 100644 --- a/src/invariant/BucketListStateConsistency.h +++ b/src/invariant/BucketListStateConsistency.h @@ -19,7 +19,7 @@ class BucketListStateConsistency : public Invariant virtual std::string getName() const override; virtual std::string - checkSnapshot(LedgerStateSnapshot const& snapshot, + checkSnapshot(ApplyLedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) override; }; diff --git a/src/invariant/ConservationOfLumens.cpp b/src/invariant/ConservationOfLumens.cpp index 339fe65d0d..f3d9795684 100644 --- a/src/invariant/ConservationOfLumens.cpp +++ b/src/invariant/ConservationOfLumens.cpp @@ -223,7 +223,7 @@ processEntryIfNew(LedgerEntry const& entry, LedgerKey const& key, // Scan live bucket list for entries that can hold the native asset static void -scanLiveBuckets(LedgerStateSnapshot const& snapshot, Asset const& asset, +scanLiveBuckets(ApplyLedgerStateSnapshot const& snapshot, Asset const& asset, AssetContractInfo const& assetContractInfo, int64_t& sumBalance, std::string& errorMsg, std::function const& isStopping) { @@ -270,7 +270,8 @@ scanLiveBuckets(LedgerStateSnapshot const& snapshot, Asset const& asset, } static void -scanHotArchiveBuckets(LedgerStateSnapshot const& snapshot, Asset const& asset, +scanHotArchiveBuckets(ApplyLedgerStateSnapshot const& snapshot, + Asset const& asset, AssetContractInfo const& assetContractInfo, int64_t& sumBalance, std::string& errorMsg, std::function const& isStopping) @@ -309,7 +310,7 @@ scanHotArchiveBuckets(LedgerStateSnapshot const& snapshot, Asset const& asset, std::string ConservationOfLumens::checkSnapshot( - LedgerStateSnapshot const& snapshot, + ApplyLedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) { diff --git a/src/invariant/ConservationOfLumens.h b/src/invariant/ConservationOfLumens.h index b90e2f011c..a58150f798 100644 --- a/src/invariant/ConservationOfLumens.h +++ b/src/invariant/ConservationOfLumens.h @@ -34,7 +34,7 @@ class ConservationOfLumens : public Invariant std::vector const& events, AppConnector& app) override; virtual std::string - checkSnapshot(LedgerStateSnapshot const& snapshot, + checkSnapshot(ApplyLedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) override; diff --git a/src/invariant/Invariant.h b/src/invariant/Invariant.h index f0d6bf62d1..3ad52bda9a 100644 --- a/src/invariant/Invariant.h +++ b/src/invariant/Invariant.h @@ -4,7 +4,6 @@ #pragma once -#include "bucket/BucketSnapshotManager.h" #include "bucket/BucketUtils.h" #include "ledger/LedgerStateSnapshot.h" #include "xdr/Stellar-ledger.h" @@ -74,7 +73,7 @@ class Invariant virtual std::string checkOnLedgerCommit( - LedgerStateSnapshot const& lclSnapshot, + ApplyLedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -84,7 +83,7 @@ class Invariant } virtual std::string - checkSnapshot(LedgerStateSnapshot const& snapshot, + checkSnapshot(ApplyLedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) { diff --git a/src/invariant/InvariantManager.h b/src/invariant/InvariantManager.h index 1167f238fd..b6f96152cc 100644 --- a/src/invariant/InvariantManager.h +++ b/src/invariant/InvariantManager.h @@ -17,6 +17,7 @@ class Bucket; class Invariant; class LedgerManager; class LedgerStateSnapshot; +class ApplyLedgerStateSnapshot; struct EvictedStateVectors; struct LedgerTxnDelta; struct Operation; @@ -55,7 +56,7 @@ class InvariantManager AppConnector& app) = 0; virtual void checkOnLedgerCommit( - LedgerStateSnapshot const& lclSnapshot, + ApplyLedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -67,7 +68,7 @@ class InvariantManager // given ledger state snapshot. These invariants will only run if // INVARIANT_EXTRA_CHECKS is enabled. virtual void - runStateSnapshotInvariant(LedgerStateSnapshot const& snapshot, + runStateSnapshotInvariant(ApplyLedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) = 0; diff --git a/src/invariant/InvariantManagerImpl.cpp b/src/invariant/InvariantManagerImpl.cpp index b935b73847..5412f4a945 100644 --- a/src/invariant/InvariantManagerImpl.cpp +++ b/src/invariant/InvariantManagerImpl.cpp @@ -4,7 +4,6 @@ #include "invariant/InvariantManagerImpl.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "bucket/BucketUtils.h" #include "bucket/LedgerCmp.h" #include "bucket/LiveBucket.h" @@ -27,9 +26,7 @@ #include "util/MetricsRegistry.h" #include "util/ProtocolVersion.h" #include "util/XDRCereal.h" -#include #include -#include #include #include @@ -175,7 +172,7 @@ InvariantManagerImpl::checkOnOperationApply( void InvariantManagerImpl::checkOnLedgerCommit( - LedgerStateSnapshot const& lclSnapshot, + ApplyLedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -194,7 +191,8 @@ InvariantManagerImpl::checkOnLedgerCommit( auto message = fmt::format( FMT_STRING(R"(Invariant "{}" does not hold on ledger commit: {})"), invariant->getName(), result); - onInvariantFailure(invariant, message, lclSnapshot.getLedgerSeq()); + onInvariantFailure(invariant, message, + lclSnapshot.getLedgerSeq() + 1); } } @@ -331,7 +329,7 @@ InvariantManagerImpl::handleInvariantFailure(bool isStrict, // required state, then call this function in a background thread. void InvariantManagerImpl::runStateSnapshotInvariant( - LedgerStateSnapshot const& snapshot, + ApplyLedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) { diff --git a/src/invariant/InvariantManagerImpl.h b/src/invariant/InvariantManagerImpl.h index b4c60befae..5aed7ecb9f 100644 --- a/src/invariant/InvariantManagerImpl.h +++ b/src/invariant/InvariantManagerImpl.h @@ -17,7 +17,6 @@ class Counter; namespace stellar { -class MetricsRegistry; class InvariantManagerImpl : public InvariantManager { @@ -62,7 +61,7 @@ class InvariantManagerImpl : public InvariantManager std::unordered_set const& shadowedKeys) override; virtual void checkOnLedgerCommit( - LedgerStateSnapshot const& lclSnapshot, + ApplyLedgerStateSnapshot const& lclSnapshot, std::vector const& persitentEvictedFromLive, std::vector const& tempAndTTLEvictedFromLive, UnorderedMap const& restoredFromArchive, @@ -81,7 +80,7 @@ class InvariantManagerImpl : public InvariantManager bool shouldRunInvariantSnapshot() const override; void markStartOfInvariantSnapshot() override; - void runStateSnapshotInvariant(LedgerStateSnapshot const& snapshot, + void runStateSnapshotInvariant(ApplyLedgerStateSnapshot const& snapshot, InMemorySorobanState const& inMemorySnapshot, std::function isStopping) override; diff --git a/src/invariant/test/ConservationOfLumensTests.cpp b/src/invariant/test/ConservationOfLumensTests.cpp index 2fcdd48a44..b70e31efcc 100644 --- a/src/invariant/test/ConservationOfLumensTests.cpp +++ b/src/invariant/test/ConservationOfLumensTests.cpp @@ -20,7 +20,6 @@ #include "transactions/test/SorobanTxTestUtils.h" #include "util/Math.h" #include -#include #include using namespace stellar; @@ -333,7 +332,7 @@ TEST_CASE( // Verify the snapshot invariant passes { - auto snap = app.getLedgerManager().getLastClosedSnapshot(); + auto snap = app.getLedgerManager().copyApplyLedgerStateSnapshot(); auto& inMemoryState = app.getLedgerManager().getInMemorySorobanStateForTesting(); @@ -352,7 +351,7 @@ TEST_CASE( closeLedger(test.getApp()); - auto snap = app.getLedgerManager().getLastClosedSnapshot(); + auto snap = app.getLedgerManager().copyApplyLedgerStateSnapshot(); auto& inMemoryState = app.getLedgerManager().getInMemorySorobanStateForTesting(); @@ -417,7 +416,7 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", app->getInvariantManager().enableInvariant("ConservationOfLumens"); - auto snap = app->getLedgerManager().getLastClosedSnapshot(); + auto snap = app->getLedgerManager().copyApplyLedgerStateSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); @@ -450,7 +449,7 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", BucketTestUtils::closeLedger(*app); - auto snap = app->getLedgerManager().getLastClosedSnapshot(); + auto snap = app->getLedgerManager().copyApplyLedgerStateSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); @@ -510,7 +509,7 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", app->getInvariantManager().enableInvariant("ConservationOfLumens"); - auto snap = app->getLedgerManager().getLastClosedSnapshot(); + auto snap = app->getLedgerManager().copyApplyLedgerStateSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); @@ -566,7 +565,7 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", BucketTestUtils::closeLedger(*app); { - auto snap = app->getLedgerManager().getLastClosedSnapshot(); + auto snap = app->getLedgerManager().copyApplyLedgerStateSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); @@ -586,7 +585,7 @@ TEST_CASE("ConservationOfLumens snapshot invariant detects bucket corruption", BucketTestUtils::closeLedger(*app); { - auto snap = app->getLedgerManager().getLastClosedSnapshot(); + auto snap = app->getLedgerManager().copyApplyLedgerStateSnapshot(); auto& inMemoryState = app->getLedgerManager().getInMemorySorobanStateForTesting(); diff --git a/src/invariant/test/InvariantTests.cpp b/src/invariant/test/InvariantTests.cpp index 1344c50551..a8de5500ec 100644 --- a/src/invariant/test/InvariantTests.cpp +++ b/src/invariant/test/InvariantTests.cpp @@ -2,10 +2,7 @@ // under the Apache License, Version 2.0. See the COPYING file at the root // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 -#include "util/asio.h" - #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "bucket/BucketUtils.h" #include "bucket/LiveBucket.h" #include "bucket/test/BucketTestUtils.h" @@ -371,9 +368,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") } // Make sure the entries have not been evicted - auto snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = app->getLedgerManager().copyLedgerStateSnapshot(); REQUIRE(snap.loadLiveEntry(LedgerEntryKey(tempEntry))); REQUIRE(snap.loadLiveEntry(LedgerEntryKey(persistentEntry))); REQUIRE(snap.loadLiveEntry(getTTLKey(tempEntry))); @@ -385,9 +380,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") { closeLedger(*app); - snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + snap = app->getLedgerManager().copyLedgerStateSnapshot(); REQUIRE(!snap.loadLiveEntry(LedgerEntryKey(tempEntry))); REQUIRE(!snap.loadLiveEntry(LedgerEntryKey(persistentEntry))); REQUIRE(!snap.loadLiveEntry(getTTLKey(tempEntry))); @@ -404,20 +397,17 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") auto ledgerVersion = lm.getLastClosedLedgerHeader().header.ledgerVersion; - app->getBucketManager() - .getBucketSnapshotManager() - .maybeUpdateLedgerStateSnapshot(snap); + auto applySnap = + app->getLedgerManager().copyApplyLedgerStateSnapshot(); // Manually trigger eviction so we can test the invariant directly LedgerTxn ltx(app->getLedgerTxnRoot()); ltx.loadHeader().current().ledgerSeq++; auto evictedState = - app->getBucketManager().resolveBackgroundEvictionScan(snap, ltx, - {}); + app->getBucketManager().resolveBackgroundEvictionScan(applySnap, + ltx, {}); - app->getBucketManager() - .getBucketSnapshotManager() - .maybeUpdateLedgerStateSnapshot(snap); + applySnap = app->getLedgerManager().copyApplyLedgerStateSnapshot(); // Persistent entry REQUIRE(evictedState.archivedEntries.size() == 1); @@ -441,7 +431,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -459,7 +449,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -474,7 +464,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -494,7 +484,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -509,7 +499,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -529,7 +519,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -548,7 +538,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") { REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -556,7 +546,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") { REQUIRE_NOTHROW( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap)); } } @@ -567,7 +557,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") evictedState.deletedKeys.push_back(getTTLKey(liveTempEntry)); REQUIRE_THROWS_AS( app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap), InvariantDoesNotHold); } @@ -577,7 +567,7 @@ TEST_CASE_VERSIONS("State archival eviction invariant", "[invariant][archival]") // Valid eviction should always pass UnorderedMap emptyMap; REQUIRE_NOTHROW(app->getInvariantManager().checkOnLedgerCommit( - snap, evictedState.archivedEntries, + applySnap, evictedState.archivedEntries, evictedState.deletedKeys, emptyMap, emptyMap)); } } @@ -607,9 +597,7 @@ TEST_CASE("BucketList state consistency invariant", "[invariant]") closeLedger(*app); auto makeSnap = [&]() { - return app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + return app->getLedgerManager().copyApplyLedgerStateSnapshot(); }; auto noopIsStopping = []() { return false; }; diff --git a/src/ledger/InMemorySorobanState.cpp b/src/ledger/InMemorySorobanState.cpp index 29eae79429..ded1ec1bcf 100644 --- a/src/ledger/InMemorySorobanState.cpp +++ b/src/ledger/InMemorySorobanState.cpp @@ -445,7 +445,7 @@ InMemorySorobanState::getTTL(LedgerKey const& ledgerKey) const void InMemorySorobanState::initializeStateFromSnapshot( - LedgerStateSnapshot const& snap) + ApplyLedgerStateSnapshot const& snap) { releaseAssertOrThrow(mContractDataEntries.empty()); releaseAssertOrThrow(mContractCodeEntries.empty()); diff --git a/src/ledger/InMemorySorobanState.h b/src/ledger/InMemorySorobanState.h index a070138324..0a85aa4840 100644 --- a/src/ledger/InMemorySorobanState.h +++ b/src/ledger/InMemorySorobanState.h @@ -17,7 +17,7 @@ namespace stellar { -class LedgerStateSnapshot; +class ApplyLedgerStateSnapshot; class InvariantManagerImpl; class SorobanMetrics; @@ -439,7 +439,7 @@ class InMemorySorobanState // is reading state when these functions are called. // Initialize the map from a bucket list snapshot - void initializeStateFromSnapshot(LedgerStateSnapshot const& snap); + void initializeStateFromSnapshot(ApplyLedgerStateSnapshot const& snap); // Update the map with entries from a ledger close. ledgerSeq must be // exactly mLastClosedLedgerSeq + 1. diff --git a/src/ledger/LedgerManager.h b/src/ledger/LedgerManager.h index 918155a3bf..fd8322c01c 100644 --- a/src/ledger/LedgerManager.h +++ b/src/ledger/LedgerManager.h @@ -230,8 +230,21 @@ class LedgerManager virtual LedgerHeaderHistoryEntry const& getLastClosedLedgerHeader() const = 0; - // Get a LedgerStateSnapshot of the last closed ledger - virtual LedgerStateSnapshot getLastClosedSnapshot() const = 0; + // Create a thread-safe copy of the current canonical ledger state + // snapshot. Can be called from any thread. + virtual LedgerStateSnapshot copyLedgerStateSnapshot() const = 0; + + // Create a thread-safe copy of the current canonical ledger state + // snapshot, typed as an apply-time snapshot. Used by legacy (pre-V23) + // code paths that need an ApplyLedgerStateSnapshot but don't have + // access to ApplyState. + // TODO: Refactor such that this doesn' have to be a public function + virtual ApplyLedgerStateSnapshot copyApplyLedgerStateSnapshot() const = 0; + + // Refresh `snapshot` if its ledger seq differs from the current canonical + // state. No-op otherwise. Can be called from any thread. + virtual void + maybeUpdateLedgerStateSnapshot(LedgerStateSnapshot& snapshot) const = 0; // return the HAS that corresponds to the last closed ledger as persisted in // the database @@ -273,6 +286,7 @@ class LedgerManager virtual std::chrono::milliseconds getExpectedLedgerCloseTime() const = 0; #ifdef BUILD_TESTS + virtual void updateCanonicalStateForTesting(LedgerHeader const& header) = 0; virtual std::vector const& getLastClosedLedgerTxMeta() = 0; virtual std::optional const& diff --git a/src/ledger/LedgerManagerImpl.cpp b/src/ledger/LedgerManagerImpl.cpp index 073bc744f1..ba08592de3 100644 --- a/src/ledger/LedgerManagerImpl.cpp +++ b/src/ledger/LedgerManagerImpl.cpp @@ -4,7 +4,6 @@ #include "ledger/LedgerManagerImpl.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "bucket/HotArchiveBucketList.h" #include "bucket/LiveBucketList.h" #include "catchup/AssumeStateWork.h" @@ -28,7 +27,6 @@ #include "ledger/LedgerTxn.h" #include "ledger/LedgerTxnEntry.h" #include "ledger/LedgerTxnHeader.h" -#include "ledger/LedgerTypeUtils.h" #include "ledger/P23HotArchiveBug.h" #include "ledger/SharedModuleCacheCompiler.h" #include "main/Application.h" @@ -50,12 +48,12 @@ #include "util/Logging.h" #include "util/MetricsRegistry.h" #include "util/ProtocolVersion.h" +#include "util/ThreadAnnotations.h" #include "util/XDRCereal.h" #include "util/XDRStream.h" #include "util/types.h" #include "work/WorkScheduler.h" #include "xdr/Stellar-ledger-entries.h" -#include "xdrpp/printer.h" #include #include @@ -77,7 +75,6 @@ #include "LedgerManagerImpl.h" #include #include -#include #include #include #include @@ -265,6 +262,13 @@ LedgerManagerImpl::ApplyState::getSorobanInMemoryStateSizeForTesting() const { return mInMemorySorobanState.getSize(); } + +void +LedgerManagerImpl::ApplyState::setLedgerStateForTesting( + CompleteConstLedgerStatePtr state) +{ + mLedgerState = std::move(state); +} #endif void @@ -334,13 +338,31 @@ LedgerManagerImpl::ApplyState::manuallyAdvanceLedgerHeader( LedgerManagerImpl::LedgerManagerImpl(Application& app) : mApp(app) , mApplyState(app) - , mLastClosedLedgerState(std::make_shared( - LedgerHeaderHistoryEntry(), HistoryArchiveState())) + , mNumHistoricalSnapshots(app.getConfig().QUERY_SNAPSHOT_LEDGERS) , mLastClose(mApp.getClock().now()) , mCatchupDuration( app.getMetrics().NewTimer({"ledger", "catchup", "duration"})) , mState(LM_BOOTING_STATE) { + // At this point, we haven't called assumeState yet, so the BucketLists are + // empty. We will create an "empty" snapshot that is not null, but + // references this empty BucketList (and a ledger header with ledgerSeq 0 + // and zero hash). + auto& bm = mApp.getBucketManager(); + LedgerHeaderHistoryEntry emptyLcl; + HistoryArchiveState emptyHas; + + auto initialState = std::make_shared( + bm.getLiveBucketList(), bm.getHotArchiveBucketList(), emptyLcl, + emptyHas, /*sorobanConfig*/ std::nullopt, /*prevState*/ nullptr, + mNumHistoricalSnapshots); + + mApplyState.setLedgerState(initialState); + { + SharedLockExclusive lock(mLedgerStateSnapshotMutex); + mLastClosedLedgerState = initialState; + } + setupLedgerCloseMetaStream(); } @@ -471,8 +493,11 @@ LedgerManagerImpl::startNewLedger(LedgerHeader const& genesisLedger) CLOG_INFO(Ledger, "Root account: {}", skey.getStrKeyPublic()); CLOG_INFO(Ledger, "Root account seed: {}", skey.getStrKeySeed().value); - auto& appConnector = mApp.getAppConnector(); - auto snap = appConnector.copyLedgerStateSnapshot(); + ApplyLedgerStateSnapshot snap = [&] { + SharedLockShared guard(mLedgerStateSnapshotMutex); + return ApplyLedgerStateSnapshot(mLastClosedLedgerState, + mApp.getMetrics()); + }(); auto output = sealLedgerTxnAndStoreInBucketsAndDB(snap, ltx, /*ledgerCloseMeta*/ nullptr, @@ -578,29 +603,23 @@ LedgerManagerImpl::loadLastKnownLedgerInternal(bool skipBuildingFullState) } // Step 4. Restore LedgerManager's LCL state - advanceLastClosedLedgerState( - advanceBucketListSnapshotAndMakeLedgerState(*latestLedgerHeader, has)); + advanceLastClosedLedgerState(advanceApplySnapshotAndMakeLedgerState( + *latestLedgerHeader, has, std::nullopt)); // Maybe truncate checkpoint files if we're restarting after a crash // in applyLedger (in which case any modifications to the ledger state have // been rolled back) mApp.getHistoryManager().restoreCheckpoint(latestLedgerHeader->ledgerSeq); - // Prime module cache with LCL state, not apply-state. This is acceptable - // here because we just started and there is no apply-state yet and no apply - // thread to hold such state. + // Prime module cache if (!skipBuildingFullState) { - auto snap = getLastClosedSnapshot(); mApplyState.compileAllContractsInLedger( - snap, latestLedgerHeader->ledgerVersion); - mApplyState.populateInMemorySorobanState(snap); - } + latestLedgerHeader->ledgerVersion); + mApplyState.populateInMemorySorobanState(); - if (!skipBuildingFullState) - { maybeRunSnapshotInvariantFromLedgerState( - mLastClosedLedgerState, maybeCopySorobanStateForInvariant(), + copyApplyLedgerStateSnapshot(), maybeCopySorobanStateForInvariant(), /* runInParallel */ false); } mApplyState.markEndOfSetupPhase(); @@ -627,7 +646,8 @@ LedgerManagerImpl::getDatabase() uint32_t LedgerManagerImpl::getLastMaxTxSetSize() const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); return mLastClosedLedgerState->getLastClosedLedgerHeader() .header.maxTxSetSize; @@ -636,7 +656,8 @@ LedgerManagerImpl::getLastMaxTxSetSize() const uint32_t LedgerManagerImpl::getLastMaxTxSetSizeOps() const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); auto n = mLastClosedLedgerState->getLastClosedLedgerHeader().header.maxTxSetSize; @@ -685,7 +706,8 @@ LedgerManagerImpl::maxSorobanTransactionResources() int64_t LedgerManagerImpl::getLastMinBalance(uint32_t ownerCount) const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); auto const& lh = mLastClosedLedgerState->getLastClosedLedgerHeader().header; if (protocolVersionIsBefore(lh.ledgerVersion, ProtocolVersion::V_9)) @@ -697,7 +719,8 @@ LedgerManagerImpl::getLastMinBalance(uint32_t ownerCount) const uint32_t LedgerManagerImpl::getLastReserve() const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); return mLastClosedLedgerState->getLastClosedLedgerHeader() .header.baseReserve; @@ -706,7 +729,8 @@ LedgerManagerImpl::getLastReserve() const uint32_t LedgerManagerImpl::getLastTxFee() const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); return mLastClosedLedgerState->getLastClosedLedgerHeader().header.baseFee; } @@ -714,7 +738,8 @@ LedgerManagerImpl::getLastTxFee() const LedgerHeaderHistoryEntry const& LedgerManagerImpl::getLastClosedLedgerHeader() const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); return mLastClosedLedgerState->getLastClosedLedgerHeader(); } @@ -722,7 +747,8 @@ LedgerManagerImpl::getLastClosedLedgerHeader() const HistoryArchiveState LedgerManagerImpl::getLastClosedLedgerHAS() const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); return mLastClosedLedgerState->getLastClosedHistoryArchiveState(); } @@ -730,7 +756,8 @@ LedgerManagerImpl::getLastClosedLedgerHAS() const uint32_t LedgerManagerImpl::getLastClosedLedgerNum() const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); return mLastClosedLedgerState->getLastClosedLedgerHeader().header.ledgerSeq; } @@ -755,7 +782,7 @@ LedgerManagerImpl::maybeCopySorobanStateForInvariant() void LedgerManagerImpl::maybeRunSnapshotInvariantFromLedgerState( - CompleteConstLedgerStatePtr const& ledgerState, + ApplyLedgerStateSnapshot const& ledgerState, std::shared_ptr inMemorySnapshotForInvariant, bool runInParallel) const { @@ -768,15 +795,15 @@ LedgerManagerImpl::maybeRunSnapshotInvariantFromLedgerState( } // Verify consistency of all snapshot state. - auto ledgerSeq = ledgerState->getLastClosedLedgerHeader().header.ledgerSeq; + auto ledgerSeq = ledgerState.getLedgerSeq(); inMemorySnapshotForInvariant->assertLastClosedLedger(ledgerSeq); // Note: No race condition acquiring app by reference, as all worker // threads are joined before application destruction. // Make sure we make a new snapshot copy since invariant will run on another // thread. - auto cb = [snap = LedgerStateSnapshot(ledgerState, mApp.getMetrics()), - &app = mApp, inMemorySnapshotForInvariant]() { + auto cb = [snap = ledgerState, &app = mApp, + inMemorySnapshotForInvariant]() { app.getInvariantManager().runStateSnapshotInvariant( std::move(snap), *inMemorySnapshotForInvariant, [&app]() { return app.isStopping(); }); @@ -795,15 +822,18 @@ LedgerManagerImpl::maybeRunSnapshotInvariantFromLedgerState( SorobanNetworkConfig const& LedgerManagerImpl::getLastClosedSorobanNetworkConfig() const { - releaseAssert(threadIsMain()); - releaseAssert(hasLastClosedSorobanNetworkConfig()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); + releaseAssert(mLastClosedLedgerState); + releaseAssert(mLastClosedLedgerState->hasSorobanConfig()); return mLastClosedLedgerState->getSorobanConfig(); } bool LedgerManagerImpl::hasLastClosedSorobanNetworkConfig() const { - releaseAssert(threadIsMain()); + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); return mLastClosedLedgerState->hasSorobanConfig(); } @@ -862,8 +892,7 @@ LedgerManagerImpl::rebuildInMemorySorobanStateForTesting(uint32_t ledgerVersion) { mApplyState.resetToSetupPhase(); mApplyState.getInMemorySorobanStateForTesting().clearForTesting(); - auto snap = getLastClosedSnapshot(); - mApplyState.populateInMemorySorobanState(snap); + mApplyState.populateInMemorySorobanState(); mApplyState.markEndOfSetupPhase(); } @@ -965,19 +994,19 @@ LedgerManagerImpl::ApplyState::finishPendingCompilation() void LedgerManagerImpl::ApplyState::compileAllContractsInLedger( - LedgerStateSnapshot const& snap, uint32_t minLedgerVersion) + uint32_t minLedgerVersion) { assertSetupPhase(); - startCompilingAllContracts(LedgerStateSnapshot(snap), minLedgerVersion); + startCompilingAllContracts(minLedgerVersion); finishPendingCompilation(); } void -LedgerManagerImpl::ApplyState::populateInMemorySorobanState( - LedgerStateSnapshot const& snap) +LedgerManagerImpl::ApplyState::populateInMemorySorobanState() { assertSetupPhase(); - mInMemorySorobanState.initializeStateFromSnapshot(snap); + mInMemorySorobanState.initializeStateFromSnapshot( + copyLedgerStateSnapshot()); } void @@ -1035,7 +1064,7 @@ LedgerManagerImpl::ApplyState::assertSetupPhase() const void LedgerManagerImpl::ApplyState::startCompilingAllContracts( - LedgerStateSnapshot snap, uint32_t minLedgerVersion) + uint32_t minLedgerVersion) { threadInvariant(); // Always stop a previous compilation before starting a new one. Can only @@ -1050,7 +1079,7 @@ LedgerManagerImpl::ApplyState::startCompilingAllContracts( } } mCompiler = std::make_unique( - std::move(snap), mNumCompilationThreads, versions); + copyLedgerStateSnapshot(), mNumCompilationThreads, versions); mCompiler->start(); } @@ -1064,9 +1093,10 @@ LedgerManagerImpl::ApplyState::assertWritablePhase() const void LedgerManagerImpl::ApplyState::maybeRebuildModuleCache( - LedgerStateSnapshot const& snap, uint32_t minLedgerVersion) + uint32_t minLedgerVersion) { assertCommittingPhase(); + auto snap = copyLedgerStateSnapshot(); // There is (currently) a grow-only arena underlying the module cache, so as // entries are uploaded and evicted that arena will still grow. To cap this @@ -1124,8 +1154,7 @@ LedgerManagerImpl::ApplyState::maybeRebuildModuleCache( "Rebuilding module cache: worst-case estimate {} " "model-bytes consumed of {} limit", bytesConsumed, limit); - startCompilingAllContracts(LedgerStateSnapshot(snap), - minLedgerVersion); + startCompilingAllContracts(minLedgerVersion); break; } } @@ -1328,7 +1357,8 @@ LedgerManagerImpl::ledgerCloseComplete( releaseAssert(threadIsMain()); // Kick off the snapshot invariant, if enabled - maybeRunSnapshotInvariantFromLedgerState(mLastClosedLedgerState, + ApplyLedgerStateSnapshot stateCopy = mApplyState.copyLedgerStateSnapshot(); + maybeRunSnapshotInvariantFromLedgerState(stateCopy, inMemorySnapshotForInvariant); uint32_t latestHeardFromNetwork = mApp.getLedgerApplyManager().getLargestLedgerSeqHeard(); @@ -1678,8 +1708,7 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData, auto maybeNewVersion = ltx.loadHeader().current().ledgerVersion; auto ledgerSeq = ltx.loadHeader().current().ledgerSeq; - auto& appConnector = mApp.getAppConnector(); - auto lclSnap = appConnector.copyLedgerStateSnapshot(); + auto lclSnap = mApplyState.copyLedgerStateSnapshot(); auto appliedLedgerState = sealLedgerTxnAndStoreInBucketsAndDB( lclSnap, ltx, ledgerCloseMeta, initialLedgerVers); @@ -1784,10 +1813,10 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData, .header.ledgerVersion, SOROBAN_PROTOCOL_VERSION)) { - // Construct a LedgerStateSnapshot from `appliedLedgerState`, which - // holds the latest committed state, to avoid relying on - // BucketSnapshotManager. - LedgerStateSnapshot snap(appliedLedgerState, mApp.getMetrics()); + // Construct an ApplyLedgerStateSnapshot from `appliedLedgerState`, + // which holds the latest committed state, since the main thread has + // not yet updated the non-apply lcl snapshot + ApplyLedgerStateSnapshot snap(appliedLedgerState, mApp.getMetrics()); mApp.getBucketManager().startBackgroundEvictionScan( std::move(snap), appliedLedgerState->getSorobanConfig()); } @@ -1853,8 +1882,8 @@ LedgerManagerImpl::setLastClosedLedger( header.current(), /* appendToCheckpoint */ false); ltx.commit(); - auto output = - advanceBucketListSnapshotAndMakeLedgerState(lastClosed.header, has); + auto output = advanceApplySnapshotAndMakeLedgerState(lastClosed.header, has, + std::nullopt); advanceLastClosedLedgerState(output); auto ledgerVersion = lastClosed.header.ledgerVersion; @@ -1867,14 +1896,8 @@ LedgerManagerImpl::setLastClosedLedger( { // This should not be additionally conditionalized on lv >= anything, // since we want to support SOROBAN_TEST_EXTRA_PROTOCOL > lv. - // - // Again, since we are only called during catchup and just got a full - // bucket state, there's no tx-apply state to snapshot, in this one - // case we will prime the tx-apply-state's soroban module cache using - // a snapshot _from_ the LCL state. - auto snap = getLastClosedSnapshot(); - mApplyState.compileAllContractsInLedger(snap, ledgerVersion); - mApplyState.populateInMemorySorobanState(snap); + mApplyState.compileAllContractsInLedger(ledgerVersion); + mApplyState.populateInMemorySorobanState(); } mApplyState.markEndOfSetupPhase(); } @@ -1896,7 +1919,7 @@ LedgerManagerImpl::manuallyAdvanceLedgerHeader(LedgerHeader const& header) mApplyState.markStartOfCommitting(); mApplyState.manuallyAdvanceLedgerHeader(header); advanceLastClosedLedgerState( - advanceBucketListSnapshotAndMakeLedgerState(header, has)); + advanceApplySnapshotAndMakeLedgerState(header, has, std::nullopt)); mApplyState.markEndOfCommitting(); } @@ -2030,14 +2053,6 @@ LedgerManagerImpl::maybeResetLedgerCloseMetaDebugStream(uint32_t ledgerSeq) } } -LedgerStateSnapshot -LedgerManagerImpl::getLastClosedSnapshot() const -{ - releaseAssert(threadIsMain()); - releaseAssert(mLastClosedLedgerState); - return LedgerStateSnapshot(mLastClosedLedgerState, mApp.getMetrics()); -} - void LedgerManagerImpl::advanceLastClosedLedgerState( CompleteConstLedgerStatePtr newLedgerState) @@ -2045,6 +2060,7 @@ LedgerManagerImpl::advanceLastClosedLedgerState( releaseAssert(threadIsMain()); releaseAssert(newLedgerState); + SharedLockExclusive lock(mLedgerStateSnapshotMutex); if (mLastClosedLedgerState) { CLOG_DEBUG( @@ -2057,28 +2073,118 @@ LedgerManagerImpl::advanceLastClosedLedgerState( } CompleteConstLedgerStatePtr -LedgerManagerImpl::advanceBucketListSnapshotAndMakeLedgerState( - LedgerHeader const& header, HistoryArchiveState const& has) +LedgerManagerImpl::buildLedgerState( + LedgerHeader const& header, HistoryArchiveState const& has, + CompleteConstLedgerStatePtr prevState, + std::optional sorobanConfig) { - auto ledgerHash = xdrSha256(header); + auto& bm = mApp.getBucketManager(); + + // If the caller didn't provide a SorobanNetworkConfig, load it from the + // BucketList (at this point the BucketList must have already been updated). + if (!sorobanConfig && protocolVersionStartsFrom(header.ledgerVersion, + SOROBAN_PROTOCOL_VERSION)) + { + auto liveData = std::make_shared>( + bm.getLiveBucketList()); + LedgerSnapshot ls(mApp.getMetrics(), std::move(liveData), header); + sorobanConfig = SorobanNetworkConfig::loadFromLedger(ls); + } LedgerHeaderHistoryEntry lcl; lcl.header = header; - lcl.hash = ledgerHash; + lcl.hash = xdrSha256(header); - auto& bm = mApp.getBucketManager(); - // Updating BL snapshot is thread-safe - bm.getBucketSnapshotManager().updateCurrentSnapshot( - bm.getLiveBucketList(), bm.getHotArchiveBucketList(), header); - - auto state = std::make_shared( - bm.getLiveBucketList(), bm.getHotArchiveBucketList(), - bm.getBucketSnapshotManager().getLiveHistoricalSnapshots(), - bm.getBucketSnapshotManager().getHotArchiveHistoricalSnapshots(), lcl, - has, mApp.getMetrics()); - bm.getBucketSnapshotManager().setCompleteState(state); + return std::make_shared( + bm.getLiveBucketList(), bm.getHotArchiveBucketList(), lcl, has, + std::move(sorobanConfig), std::move(prevState), + mNumHistoricalSnapshots); +} + +CompleteConstLedgerStatePtr +LedgerManagerImpl::advanceApplySnapshotAndMakeLedgerState( + LedgerHeader const& header, HistoryArchiveState const& has, + std::optional sorobanConfig) +{ + auto state = buildLedgerState(header, has, mApplyState.getLedgerState(), + std::move(sorobanConfig)); + mApplyState.setLedgerState(state); return state; } + +LedgerStateSnapshot +LedgerManagerImpl::copyLedgerStateSnapshot() const +{ + // Apply thread must use the ApplyState's copyLedgerStateSnapshot. + releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + + SharedLockShared guard(mLedgerStateSnapshotMutex); + releaseAssert(mLastClosedLedgerState); + return LedgerStateSnapshot(mLastClosedLedgerState, mApp.getMetrics()); +} + +ApplyLedgerStateSnapshot +LedgerManagerImpl::copyApplyLedgerStateSnapshot() const +{ + // Apply-thread state may be ahead of mLastClosedLedgerState during + // parallel ledger close, so always read from ApplyState. + mApplyState.threadInvariant(); + return mApplyState.copyLedgerStateSnapshot(); +} + +void +LedgerManagerImpl::maybeUpdateLedgerStateSnapshot( + LedgerStateSnapshot& snapshot) const +{ + SharedLockShared guard(mLedgerStateSnapshotMutex); + releaseAssert(mLastClosedLedgerState); + if (snapshot.getLedgerSeq() != + mLastClosedLedgerState->getLastClosedLedgerHeader().header.ledgerSeq) + { + snapshot = + LedgerStateSnapshot(mLastClosedLedgerState, mApp.getMetrics()); + } +} + +void +LedgerManagerImpl::ApplyState::setLedgerState(CompleteConstLedgerStatePtr state) +{ + assertWritablePhase(); + mLedgerState = std::move(state); +} + +CompleteConstLedgerStatePtr +LedgerManagerImpl::ApplyState::getLedgerState() const +{ + releaseAssert(mLedgerState); + return mLedgerState; +} + +ApplyLedgerStateSnapshot +LedgerManagerImpl::ApplyState::copyLedgerStateSnapshot() const +{ + releaseAssert(mLedgerState); + return ApplyLedgerStateSnapshot(mLedgerState, mAppConnector.getMetrics()); +} + +#ifdef BUILD_TESTS +void +LedgerManagerImpl::updateCanonicalStateForTesting(LedgerHeader const& header) +{ + releaseAssert(threadIsMain()); + + HistoryArchiveState has; + has.currentLedger = header.ledgerSeq; + + auto state = + buildLedgerState(header, has, mLastClosedLedgerState, std::nullopt); + + mApplyState.setLedgerStateForTesting(state); + + SharedLockExclusive lock(mLedgerStateSnapshotMutex); + mLastClosedLedgerState = state; +} +#endif } std::vector @@ -2405,7 +2511,8 @@ LedgerManagerImpl::applySorobanStages(AppConnector& app, AbstractLedgerTxn& ltx, { ZoneScoped; GlobalParallelApplyLedgerState globalParState( - app, ltx, stages, mApplyState.getInMemorySorobanState(), sorobanConfig); + app, mApplyState.copyLedgerStateSnapshot(), ltx, stages, + mApplyState.getInMemorySorobanState(), sorobanConfig); // LedgerTxn is not passed into applySorobanStage, so there's no risk // of the header being updated while we apply the stages. auto const& header = ltx.loadHeader().current(); @@ -2807,9 +2914,9 @@ LedgerManagerImpl::storePersistentStateAndLedgerHeaderInDB( } // NB: This is a separate method so a testing subclass can override it. -void +std::optional LedgerManagerImpl::finalizeLedgerTxnChanges( - LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, + ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, LedgerHeader lh, uint32_t initialLedgerVers) { @@ -2912,11 +3019,12 @@ LedgerManagerImpl::finalizeLedgerTxnChanges( deadEntries); mApplyState.updateInMemorySorobanState(initEntries, liveEntries, deadEntries, lh, finalSorobanConfig); + return finalSorobanConfig; } CompleteConstLedgerStatePtr LedgerManagerImpl::sealLedgerTxnAndStoreInBucketsAndDB( - LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, + ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, uint32_t initialLedgerVers) { @@ -2952,23 +3060,24 @@ LedgerManagerImpl::sealLedgerTxnAndStoreInBucketsAndDB( // protocol version prior to the upgrade. Due to this, we must check the // initial protocol version of ledger instead of the ledger version of // the current ltx header, which may have been modified via an upgrade. - finalizeLedgerTxnChanges(lclSnapshot, ltx, ledgerCloseMeta, ledgerHeader, - initialLedgerVers); + auto sorobanConfig = finalizeLedgerTxnChanges( + lclSnapshot, ltx, ledgerCloseMeta, ledgerHeader, initialLedgerVers); CompleteConstLedgerStatePtr res; - ltx.unsealHeader([this, &res](LedgerHeader& lh) { + ltx.unsealHeader([this, &res, sorobanConfig = std::move(sorobanConfig)]( + LedgerHeader& lh) mutable { mApp.getBucketManager().snapshotLedger(lh); auto has = storePersistentStateAndLedgerHeaderInDB( lh, /* appendToCheckpoint */ true); - res = advanceBucketListSnapshotAndMakeLedgerState(lh, has); + res = advanceApplySnapshotAndMakeLedgerState(lh, has, + std::move(sorobanConfig)); }); releaseAssert(res); if (protocolVersionStartsFrom( initialLedgerVers, REUSABLE_SOROBAN_MODULE_CACHE_PROTOCOL_VERSION)) { - LedgerStateSnapshot snap(res, mApp.getMetrics()); - mApplyState.maybeRebuildModuleCache(snap, initialLedgerVers); + mApplyState.maybeRebuildModuleCache(initialLedgerVers); } return res; diff --git a/src/ledger/LedgerManagerImpl.h b/src/ledger/LedgerManagerImpl.h index edbf2a1503..7ea3fddb79 100644 --- a/src/ledger/LedgerManagerImpl.h +++ b/src/ledger/LedgerManagerImpl.h @@ -4,8 +4,7 @@ #pragma once -#include "util/asio.h" - +#include "bucket/BucketListSnapshot.h" #include "bucket/BucketManager.h" #include "history/HistoryManager.h" #include "ledger/InMemorySorobanState.h" @@ -159,6 +158,9 @@ class LedgerManagerImpl : public LedgerManager AppConnector& mAppConnector; + // Ledger state snapshot that is the base for current ledger apply + CompleteConstLedgerStatePtr mLedgerState; + // The current reusable / inter-ledger soroban module cache. ::rust::Box mModuleCache; @@ -181,11 +183,10 @@ class LedgerManagerImpl : public LedgerManager Phase mPhase{Phase::SETTING_UP_STATE}; // Kicks off (on auxiliary threads) compilation of all contracts in the - // provided snapshot, for ledger protocols starting at minLedgerVersion - // and running through to Config::CURRENT_LEDGER_PROTOCOL_VERSION (to - // enable upgrades). - void startCompilingAllContracts(LedgerStateSnapshot snap, - uint32_t minLedgerVersion); + // apply state snapshot, for ledger protocols starting at + // minLedgerVersion and running through to + // Config::CURRENT_LEDGER_PROTOCOL_VERSION (to enable upgrades). + void startCompilingAllContracts(uint32_t minLedgerVersion); // Checks if ApplyState can currently be modified. For functions that // are only called in ledgerClose, use the stronger @@ -211,6 +212,7 @@ class LedgerManagerImpl : public LedgerManager ::rust::Box const& getModuleCacheForTesting(); uint64_t getSorobanInMemoryStateSizeForTesting() const; + void setLedgerStateForTesting(CompleteConstLedgerStatePtr state); #endif ::rust::Box const& @@ -236,14 +238,12 @@ class LedgerManagerImpl : public LedgerManager // Equivalent to calling `startCompilingAllContracts` followed by // `finishPendingCompilation`. - void compileAllContractsInLedger(LedgerStateSnapshot const& snap, - uint32_t minLedgerVersion); + void compileAllContractsInLedger(uint32_t minLedgerVersion); // Estimates the size of the arena underlying the module cache's shared // wasmi engine, from metrics, and rebuilds if it has likely built up a // lot of dead space inside of it. - void maybeRebuildModuleCache(LedgerStateSnapshot const& snap, - uint32_t minLedgerVersion); + void maybeRebuildModuleCache(uint32_t minLedgerVersion); // Evicts a single contract from the module cache, if it is present. // This should be done whenever a contract LE is evicted from the @@ -258,11 +258,17 @@ class LedgerManagerImpl : public LedgerManager // Populates all live Soroban state into the cache from the provided // snapshot. - void populateInMemorySorobanState(LedgerStateSnapshot const& snap); + void populateInMemorySorobanState(); void handleUpgradeAffectingSorobanInMemoryStateSize( AbstractLedgerTxn& upgradeLtx); + // Advance the ledger state to the provided snapshot. + void setLedgerState(CompleteConstLedgerStatePtr state); + + CompleteConstLedgerStatePtr getLedgerState() const; + ApplyLedgerStateSnapshot copyLedgerStateSnapshot() const; + // Throws if current state is not READY_TO_APPLY, advances to APPLYING void markStartOfApplying(); @@ -289,8 +295,21 @@ class LedgerManagerImpl : public LedgerManager // that gets accessed via the AppConnector, from inside transactions. ApplyState mApplyState; - // Cached LCL state output from last apply (or loaded from DB on startup). - CompleteConstLedgerStatePtr mLastClosedLedgerState; + // We maintain two (potentially different) ledger state snapshots, one for + // the apply thread, and one for everyone else, managed by the main thread. + // mLastClosedLedgerState is managed by the main thread and is copyable + // by all threads (except for apply). This is protected by + // mLedgerStateSnapshotMutex. + // When background apply is enabled, the apply thread will advance it's own + // snapshot immediately after applying a ledger, then post the result back + // to main thread. This means the apply snapshot may be ahead of + // mLastClosedLedgerState at any given point. + mutable SharedMutex mLedgerStateSnapshotMutex; + CompleteConstLedgerStatePtr + mLastClosedLedgerState GUARDED_BY(mLedgerStateSnapshotMutex); + + // Max number of historical snapshots to maintain. + uint32_t const mNumHistoricalSnapshots; VirtualClock::time_point mLastClose; @@ -381,7 +400,7 @@ class LedgerManagerImpl : public LedgerManager // On the ledger in which a protocol upgrade from vN to vN + 1 occurs, // initialLedgerVers must be vN. CompleteConstLedgerStatePtr sealLedgerTxnAndStoreInBucketsAndDB( - LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, + ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, uint32_t initialLedgerVers); @@ -401,7 +420,7 @@ class LedgerManagerImpl : public LedgerManager // If runInParallel is false, runs on the calling thread (this is useful in // certain scenarios such as startup) void maybeRunSnapshotInvariantFromLedgerState( - CompleteConstLedgerStatePtr const& ledgerState, + ApplyLedgerStateSnapshot const& ledgerState, std::shared_ptr inMemorySnapshotForInvariant, bool runInParallel = true) const; @@ -448,17 +467,25 @@ class LedgerManagerImpl : public LedgerManager // NB: LedgerHeader is a copy here to prevent footguns in case ltx // invalidates any header references - virtual void finalizeLedgerTxnChanges( - LedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, + virtual std::optional finalizeLedgerTxnChanges( + ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx, std::unique_ptr const& ledgerCloseMeta, LedgerHeader lh, uint32_t initialLedgerVers); - // Update bucket list snapshot, and construct LedgerState return - // value, which contains all information relevant to ledger state (HAS, - // ledger header, network config, bucketlist snapshot). + // Build a new CompleteConstLedgerState from the current BucketLists, + // copying then updating historical snapshots from prevState. If + // sorobanConfig is not provided, it is loaded from a temporary bucket + // snapshot when the protocol requires it. CompleteConstLedgerStatePtr - advanceBucketListSnapshotAndMakeLedgerState(LedgerHeader const& header, - HistoryArchiveState const& has); + buildLedgerState(LedgerHeader const& header, HistoryArchiveState const& has, + CompleteConstLedgerStatePtr prevState, + std::optional sorobanConfig); + + // Build a new ledger state and advance ApplyState snapshot to it. This does + // not yet publish or post the new snapshot to the main thread. + CompleteConstLedgerStatePtr advanceApplySnapshotAndMakeLedgerState( + LedgerHeader const& header, HistoryArchiveState const& has, + std::optional sorobanConfig); void logTxApplyMetrics(AbstractLedgerTxn& ltx, size_t numTxs, size_t numOps); @@ -554,7 +581,13 @@ class LedgerManagerImpl : public LedgerManager void maybeResetLedgerCloseMetaDebugStream(uint32_t ledgerSeq); SorobanMetrics& getSorobanMetrics() override; - LedgerStateSnapshot getLastClosedSnapshot() const override; + LedgerStateSnapshot copyLedgerStateSnapshot() const override; + ApplyLedgerStateSnapshot copyApplyLedgerStateSnapshot() const override; + void maybeUpdateLedgerStateSnapshot( + LedgerStateSnapshot& snapshot) const override; +#ifdef BUILD_TESTS + void updateCanonicalStateForTesting(LedgerHeader const& header) override; +#endif virtual bool isApplying() const override { diff --git a/src/ledger/LedgerStateSnapshot.cpp b/src/ledger/LedgerStateSnapshot.cpp index e87ccce95e..dd1052db1b 100644 --- a/src/ledger/LedgerStateSnapshot.cpp +++ b/src/ledger/LedgerStateSnapshot.cpp @@ -4,7 +4,9 @@ #include "ledger/LedgerStateSnapshot.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" +#include "bucket/HotArchiveBucketList.h" +#include "bucket/LiveBucketList.h" +#include "ledger/LedgerManager.h" #include "ledger/LedgerTxn.h" #include "main/Application.h" #include "transactions/TransactionFrame.h" @@ -168,9 +170,23 @@ LedgerTxnReadOnly::executeWithMaybeInnerSnapshot( } BucketSnapshotState::BucketSnapshotState(LedgerStateSnapshot const& snap) - : mSnapshot(snap) - , mLedgerHeader(LedgerHeaderWrapper( - std::make_shared(snap.getLedgerHeader()))) + : mLiveSnap(snap.mLiveSnapshot) + , mLedgerHeader(std::make_shared(snap.getLedgerHeader())) +{ +} + +BucketSnapshotState::BucketSnapshotState(ApplyLedgerStateSnapshot const& snap) + : mLiveSnap(static_cast(snap).mLiveSnapshot) + , mLedgerHeader(std::make_shared(snap.getLedgerHeader())) +{ +} + +BucketSnapshotState::BucketSnapshotState( + MetricsRegistry& metrics, + std::shared_ptr const> liveData, + LedgerHeader const& header) + : mLiveSnap(metrics, std::move(liveData), {}, header.ledgerSeq) + , mLedgerHeader(std::make_shared(header)) { } @@ -181,13 +197,13 @@ BucketSnapshotState::~BucketSnapshotState() LedgerHeaderWrapper BucketSnapshotState::getLedgerHeader() const { - return LedgerHeaderWrapper(std::get<1>(mLedgerHeader.mHeader)); + return LedgerHeaderWrapper(mLedgerHeader); } LedgerEntryWrapper BucketSnapshotState::getAccount(AccountID const& account) const { - return LedgerEntryWrapper(mSnapshot.loadLiveEntry(accountKey(account))); + return LedgerEntryWrapper(mLiveSnap.load(accountKey(account))); } LedgerEntryWrapper @@ -208,7 +224,7 @@ BucketSnapshotState::getAccount(LedgerHeaderWrapper const& header, LedgerEntryWrapper BucketSnapshotState::load(LedgerKey const& key) const { - return LedgerEntryWrapper(mSnapshot.loadLiveEntry(key)); + return LedgerEntryWrapper(mLiveSnap.load(key)); } void @@ -240,9 +256,7 @@ LedgerSnapshot::LedgerSnapshot(Application& app) else #endif { - auto snap = app.getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = app.getLedgerManager().copyLedgerStateSnapshot(); mGetter = std::make_unique(snap); } } @@ -252,6 +266,20 @@ LedgerSnapshot::LedgerSnapshot(LedgerStateSnapshot const& snap) { } +LedgerSnapshot::LedgerSnapshot(ApplyLedgerStateSnapshot const& snap) + : mGetter(std::make_unique(snap)) +{ +} + +LedgerSnapshot::LedgerSnapshot( + MetricsRegistry& metrics, + std::shared_ptr const> liveData, + LedgerHeader const& header) + : mGetter(std::make_unique( + metrics, std::move(liveData), header)) +{ +} + LedgerHeaderWrapper LedgerSnapshot::getLedgerHeader() const { @@ -282,94 +310,69 @@ CompleteConstLedgerState::checkInvariant() const { releaseAssert(mLastClosedHistoryArchiveState.currentLedger == mLastClosedLedgerHeader.header.ledgerSeq); - // Initial placeholder state (ledgerSeq 0) may have null bucket data - // TODO: Get rid of this since it's kinda sketchy - if (mLastClosedLedgerHeader.header.ledgerSeq > 0) + releaseAssert(mLiveBucketData); + releaseAssert(mHotArchiveBucketData); +} + +namespace +{ +// Build the next historical snapshot map by copying the previous map, +// evicting the oldest entry if at capacity, and inserting the previous +// state's current snapshot keyed by its ledger sequence number. +template +auto +rotateHistorical( + std::shared_ptr const> const& prevData, + std::map const>> const& + prevHistorical, + uint32_t prevLedgerSeq, uint32_t numHistorical) +{ + std::map const>> + result; + if (numHistorical == 0 || !prevData) { - releaseAssert(mLiveBucketData); - releaseAssert(mHotArchiveBucketData); + return result; } + result = prevHistorical; + if (result.size() == numHistorical) + { + result.erase(result.begin()); + } + result.emplace(prevLedgerSeq, prevData); + return result; } +} // anonymous namespace CompleteConstLedgerState::CompleteConstLedgerState( - BucketListBase const& liveBL, - BucketListBase const& hotArchiveBL, - std::map const>> - liveHistorical, - std::map const>> - hotArchiveHistorical, - LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, - HistoryArchiveState const& lastClosedHistoryArchiveState, - MetricsRegistry& metrics) + LiveBucketList const& liveBL, HotArchiveBucketList const& hotArchiveBL, + LedgerHeaderHistoryEntry const& lcl, HistoryArchiveState const& has, + std::optional sorobanConfig, + CompleteConstLedgerStatePtr prevState, uint32_t numHistorical) : mLiveBucketData( std::make_shared>(liveBL)) - , mLiveHistoricalSnapshots(std::move(liveHistorical)) + , mLiveHistoricalSnapshots( + prevState ? rotateHistorical( + prevState->mLiveBucketData, + prevState->mLiveHistoricalSnapshots, + prevState->mLastClosedLedgerHeader.header.ledgerSeq, + numHistorical) + : std::map const>>{}) , mHotArchiveBucketData( std::make_shared>( hotArchiveBL)) - , mHotArchiveHistoricalSnapshots(std::move(hotArchiveHistorical)) - , mSorobanConfig( - protocolVersionStartsFrom(lastClosedLedgerHeader.header.ledgerVersion, - SOROBAN_PROTOCOL_VERSION) - ? std::make_optional([&]() { - // Create a temporary CompleteConstLedgerState (without - // Soroban config) to serve as the canonical state for the - // LedgerStateSnapshot used during config loading. - // TODO: Sketchy, please remove - auto tempState = - std::shared_ptr( - new CompleteConstLedgerState( - mLiveBucketData, mLiveHistoricalSnapshots, - mHotArchiveBucketData, - mHotArchiveHistoricalSnapshots, - lastClosedLedgerHeader, - lastClosedHistoryArchiveState)); - LedgerStateSnapshot tempSnap(tempState, metrics); - LedgerSnapshot ls(tempSnap); - return SorobanNetworkConfig::loadFromLedger(ls); - }()) - : std::nullopt) - , mLastClosedLedgerHeader(lastClosedLedgerHeader) - , mLastClosedHistoryArchiveState(lastClosedHistoryArchiveState) -{ - checkInvariant(); -} - -CompleteConstLedgerState::CompleteConstLedgerState( - LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, - HistoryArchiveState const& lastClosedHistoryArchiveState) - : mLiveBucketData(nullptr) - , mLiveHistoricalSnapshots() - , mHotArchiveBucketData(nullptr) - , mHotArchiveHistoricalSnapshots() - , mSorobanConfig(std::nullopt) - , mLastClosedLedgerHeader(lastClosedLedgerHeader) - , mLastClosedHistoryArchiveState(lastClosedHistoryArchiveState) -{ - checkInvariant(); -} - -CompleteConstLedgerState::CompleteConstLedgerState( - std::shared_ptr const> liveBucketData, - std::map const>> - liveHistorical, - std::shared_ptr const> - hotArchiveBucketData, - std::map const>> - hotArchiveHistorical, - LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, - HistoryArchiveState const& lastClosedHistoryArchiveState) - : mLiveBucketData(std::move(liveBucketData)) - , mLiveHistoricalSnapshots(std::move(liveHistorical)) - , mHotArchiveBucketData(std::move(hotArchiveBucketData)) - , mHotArchiveHistoricalSnapshots(std::move(hotArchiveHistorical)) - , mSorobanConfig(std::nullopt) - , mLastClosedLedgerHeader(lastClosedLedgerHeader) - , mLastClosedHistoryArchiveState(lastClosedHistoryArchiveState) + , mHotArchiveHistoricalSnapshots( + prevState ? rotateHistorical( + prevState->mHotArchiveBucketData, + prevState->mHotArchiveHistoricalSnapshots, + prevState->mLastClosedLedgerHeader.header.ledgerSeq, + numHistorical) + : std::map const>>{}) + , mSorobanConfig(std::move(sorobanConfig)) + , mLastClosedLedgerHeader(lcl) + , mLastClosedHistoryArchiveState(has) { checkInvariant(); } @@ -518,4 +521,10 @@ LedgerStateSnapshot::scanAllArchiveEntries( { mHotArchiveSnapshot.scanAllEntries(std::move(callback)); } + +ApplyLedgerStateSnapshot::ApplyLedgerStateSnapshot( + CompleteConstLedgerStatePtr state, MetricsRegistry& metrics) + : LedgerStateSnapshot(std::move(state), metrics) +{ +} } diff --git a/src/ledger/LedgerStateSnapshot.h b/src/ledger/LedgerStateSnapshot.h index 5dcb333962..38f0881de1 100644 --- a/src/ledger/LedgerStateSnapshot.h +++ b/src/ledger/LedgerStateSnapshot.h @@ -18,13 +18,15 @@ namespace stellar class Application; class TransactionFrame; class LedgerSnapshot; +class ApplyLedgerStateSnapshot; class CompleteConstLedgerState; class EvictionStatistics; struct EvictionMetrics; struct EvictionResultCandidates; struct InflationWinner; struct StateArchivalSettings; -template class BucketListBase; +class LiveBucketList; +class HotArchiveBucketList; // NB: we can't use unique_ptr here, because this object gets passed to a // lambda, and std::function requires its callable to be copyable (C++23 fixes @@ -59,7 +61,6 @@ class LedgerEntryWrapper class LedgerHeaderWrapper { std::variant> mHeader; - friend class BucketSnapshotState; public: explicit LedgerHeaderWrapper(LedgerTxnHeader&& header); @@ -129,6 +130,9 @@ class LedgerStateSnapshot SearchableHotArchiveBucketListSnapshot mHotArchiveSnapshot; std::reference_wrapper mMetrics; + friend class CompleteConstLedgerState; + friend class BucketSnapshotState; + public: // Construct from CompleteConstLedgerState explicit LedgerStateSnapshot(CompleteConstLedgerStatePtr state, @@ -169,29 +173,51 @@ class LedgerStateSnapshot uint32_t ledgerSeq) const; void scanAllArchiveEntries( std::function callback) const; +}; - private: - friend class CompleteConstLedgerState; - - // TODO: Remove +// A strong typedef for LedgerStateSnapshot that represents a snapshot used +// during apply time. This is identical to LedgerStateSnapshot in practice, but +// is a distinct type to prevent accidental interchange between apply-time +// snapshots and other snapshots (e.g., from mLastClosedLedgerState). +class ApplyLedgerStateSnapshot : private LedgerStateSnapshot +{ friend class BucketSnapshotState; - friend class LedgerSnapshot; - friend class LedgerManagerImpl; + + public: + explicit ApplyLedgerStateSnapshot(CompleteConstLedgerStatePtr state, + MetricsRegistry& metrics); + + using LedgerStateSnapshot::getLedgerHeader; + using LedgerStateSnapshot::getLedgerSeq; + using LedgerStateSnapshot::getState; + using LedgerStateSnapshot::loadArchiveEntry; + using LedgerStateSnapshot::loadArchiveKeys; + using LedgerStateSnapshot::loadArchiveKeysFromLedger; + using LedgerStateSnapshot::loadInflationWinners; + using LedgerStateSnapshot::loadLiveEntry; + using LedgerStateSnapshot::loadLiveKeys; + using LedgerStateSnapshot::loadLiveKeysFromLedger; + using LedgerStateSnapshot::loadPoolShareTrustLinesByAccountAndAsset; + using LedgerStateSnapshot::scanAllArchiveEntries; + using LedgerStateSnapshot::scanForEviction; + using LedgerStateSnapshot::scanLiveEntriesOfType; }; // A concrete implementation of read-only BucketList snapshot wrapper class BucketSnapshotState : public AbstractLedgerStateSnapshot { - LedgerStateSnapshot mSnapshot; + SearchableLiveBucketListSnapshot mLiveSnap; // Store a copy of the header. This is needed for validation flow where // for certain validation scenarios the header needs to be modified. - LedgerHeaderWrapper mLedgerHeader; - - // TODO: Remove - friend class LedgerSnapshot; + std::shared_ptr mLedgerHeader; public: explicit BucketSnapshotState(LedgerStateSnapshot const& snap); + explicit BucketSnapshotState(ApplyLedgerStateSnapshot const& snap); + BucketSnapshotState( + MetricsRegistry& metrics, + std::shared_ptr const> liveData, + LedgerHeader const& header); ~BucketSnapshotState() override; LedgerHeaderWrapper getLedgerHeader() const override; @@ -222,6 +248,13 @@ class LedgerSnapshot : public NonMovableOrCopyable LedgerSnapshot(AbstractLedgerTxn& ltx); LedgerSnapshot(Application& app); explicit LedgerSnapshot(LedgerStateSnapshot const& snap); + explicit LedgerSnapshot(ApplyLedgerStateSnapshot const& snap); + // Construct from a lightweight live bucket snapshot + header, + // without requiring a full CompleteConstLedgerState. + LedgerSnapshot( + MetricsRegistry& metrics, + std::shared_ptr const> liveData, + LedgerHeader const& header); LedgerHeaderWrapper getLedgerHeader() const; LedgerEntryWrapper getAccount(AccountID const& account) const; LedgerEntryWrapper @@ -283,51 +316,20 @@ class CompleteConstLedgerState : public NonMovableOrCopyable void checkInvariant() const; - // Private constructor: creates a state without Soroban config, used as a - // temporary during the main constructor's config loading to break the - // circular dependency. - CompleteConstLedgerState( - std::shared_ptr const> liveBucketData, - std::map const>> - liveHistorical, - std::shared_ptr const> - hotArchiveBucketData, - std::map const>> - hotArchiveHistorical, - LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, - HistoryArchiveState const& lastClosedHistoryArchiveState); - - // LedgerStateSnapshot constructs searchable snapshots from our raw data - // TODO: Remove friend class LedgerStateSnapshot; - // BucketSnapshotManager uses the private constructor to rebuild - // mCompleteState in updateCurrentSnapshot without loading Soroban config. - friend class BucketSnapshotManager; public: - // Construct from raw bucket lists. Creates BucketListSnapshotData - // internally. Historical snapshots and metrics are needed for - // SorobanNetworkConfig loading. - CompleteConstLedgerState( - BucketListBase const& liveBL, - BucketListBase const& hotArchiveBL, - std::map const>> - liveHistorical, - std::map const>> - hotArchiveHistorical, - LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, - HistoryArchiveState const& lastClosedHistoryArchiveState, - MetricsRegistry& metrics); - - // Initial empty state constructor (ledger 0, no bucket data) - // TODO: Make less sketchy - CompleteConstLedgerState( - LedgerHeaderHistoryEntry const& lastClosedLedgerHeader, - HistoryArchiveState const& lastClosedHistoryArchiveState); + // Construct a new ledger state, rotating historical snapshots from + // prevState. If prevState is null, history maps will be empty. + // sorobanConfig is nullopt for pre-Soroban protocol versions, or when + // building the empty initial state at startup. + CompleteConstLedgerState(LiveBucketList const& liveBL, + HotArchiveBucketList const& hotArchiveBL, + LedgerHeaderHistoryEntry const& lcl, + HistoryArchiveState const& has, + std::optional sorobanConfig, + CompleteConstLedgerStatePtr prevState, + uint32_t numHistoricalSnapshots); SorobanNetworkConfig const& getSorobanConfig() const; bool hasSorobanConfig() const; diff --git a/src/ledger/LedgerTxn.cpp b/src/ledger/LedgerTxn.cpp index b5f81feb2f..0e5c8b8e33 100644 --- a/src/ledger/LedgerTxn.cpp +++ b/src/ledger/LedgerTxn.cpp @@ -5,7 +5,6 @@ #include "ledger/LedgerTxn.h" #include "bucket/BucketListSnapshot.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "crypto/KeyUtils.h" #include "database/Database.h" #include "ledger/InMemorySorobanState.h" @@ -3343,14 +3342,13 @@ LedgerTxnRoot::Impl::areEntriesMissingInCacheForOffer(OfferEntry const& oe) return false; } -LedgerStateSnapshot const& +ApplyLedgerStateSnapshot const& LedgerTxnRoot::Impl::getLedgerStateSnapshot() const { if (!mLedgerStateSnapshot) { - mLedgerStateSnapshot = mApp.getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + mLedgerStateSnapshot = + mApp.getLedgerManager().copyApplyLedgerStateSnapshot(); } return *mLedgerStateSnapshot; diff --git a/src/ledger/LedgerTxnImpl.h b/src/ledger/LedgerTxnImpl.h index 4013775156..76de284b63 100644 --- a/src/ledger/LedgerTxnImpl.h +++ b/src/ledger/LedgerTxnImpl.h @@ -4,13 +4,11 @@ #pragma once -#include "bucket/BucketSnapshotManager.h" #include "database/Database.h" #include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "util/RandomEvictionCache.h" #include "util/UnorderedSet.h" -#include #include #ifdef USE_POSTGRES #include @@ -621,7 +619,7 @@ class LedgerTxnRoot::Impl mutable BestOffers mBestOffers; mutable uint64_t mPrefetchHits{0}; mutable uint64_t mPrefetchMisses{0}; - mutable std::optional mLedgerStateSnapshot; + mutable std::optional mLedgerStateSnapshot; size_t mBulkLoadBatchSize; std::unique_ptr mTransaction; @@ -690,7 +688,7 @@ class LedgerTxnRoot::Impl bool areEntriesMissingInCacheForOffer(OfferEntry const& oe); - LedgerStateSnapshot const& getLedgerStateSnapshot() const; + ApplyLedgerStateSnapshot const& getLedgerStateSnapshot() const; public: // Constructor has the strong exception safety guarantee diff --git a/src/ledger/P23HotArchiveBug.cpp b/src/ledger/P23HotArchiveBug.cpp index de312a29ac..0d1482c452 100644 --- a/src/ledger/P23HotArchiveBug.cpp +++ b/src/ledger/P23HotArchiveBug.cpp @@ -10,9 +10,9 @@ #include "bucket/BucketListSnapshot.h" #include "bucket/BucketManager.h" #include "bucket/BucketUtils.h" +#include "ledger/LedgerManager.h" #include "ledger/LedgerTxn.h" #include "ledger/LedgerTxnImpl.h" -#include "ledger/LedgerTypeUtils.h" #include "main/AppConnector.h" #include "main/Application.h" #include @@ -350,9 +350,7 @@ Protocol23CorruptionDataVerifier::verifyArchivalOfCorruptedEntry( // This database can load the actual, correct version of a // given ledger key. This tells us the value that should // have been evicted. - auto snap = app.getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = app.getLedgerManager().copyLedgerStateSnapshot(); // This is the set of all keys incorrectly evicted for this // ledger diff --git a/src/ledger/SharedModuleCacheCompiler.cpp b/src/ledger/SharedModuleCacheCompiler.cpp index 3b483d99c0..e80120400f 100644 --- a/src/ledger/SharedModuleCacheCompiler.cpp +++ b/src/ledger/SharedModuleCacheCompiler.cpp @@ -21,7 +21,7 @@ size_t const SharedModuleCacheCompiler::BUFFERED_WASM_CAPACITY = // The snapshot is passed by copy here to ensure the background loading thread // has its own instance since snapshots themselves aren't thread safe. SharedModuleCacheCompiler::SharedModuleCacheCompiler( - LedgerStateSnapshot snap, size_t numThreads, + ApplyLedgerStateSnapshot snap, size_t numThreads, std::vector const& ledgerVersions) : mModuleCache(rust_bridge::new_module_cache()) , mSnap(std::move(snap)) diff --git a/src/ledger/SharedModuleCacheCompiler.h b/src/ledger/SharedModuleCacheCompiler.h index 3ca8c69882..26bc76bd50 100644 --- a/src/ledger/SharedModuleCacheCompiler.h +++ b/src/ledger/SharedModuleCacheCompiler.h @@ -25,7 +25,7 @@ namespace stellar class SharedModuleCacheCompiler : NonMovableOrCopyable { ::rust::Box mModuleCache; - LedgerStateSnapshot mSnap; + ApplyLedgerStateSnapshot mSnap; std::deque> mWasms; size_t const mNumThreads; @@ -55,7 +55,7 @@ class SharedModuleCacheCompiler : NonMovableOrCopyable bool popAndCompileWasm(size_t thread, std::unique_lock& lock); public: - SharedModuleCacheCompiler(LedgerStateSnapshot snap, size_t numThreads, + SharedModuleCacheCompiler(ApplyLedgerStateSnapshot snap, size_t numThreads, std::vector const& ledgerVersions); ~SharedModuleCacheCompiler(); void start(); diff --git a/src/main/AppConnector.cpp b/src/main/AppConnector.cpp index 0911d5c532..a42e5c43a9 100644 --- a/src/main/AppConnector.cpp +++ b/src/main/AppConnector.cpp @@ -1,6 +1,5 @@ #include "main/AppConnector.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "herder/Herder.h" #include "invariant/InvariantManager.h" #include "ledger/LedgerManager.h" @@ -173,17 +172,19 @@ AppConnector::threadIsType(Application::ThreadType type) const LedgerStateSnapshot AppConnector::copyLedgerStateSnapshot() { - return mApp.getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + return mApp.getLedgerManager().copyLedgerStateSnapshot(); +} + +ApplyLedgerStateSnapshot +AppConnector::copyApplyLedgerStateSnapshot() +{ + return mApp.getLedgerManager().copyApplyLedgerStateSnapshot(); } void AppConnector::maybeUpdateLedgerStateSnapshot(LedgerStateSnapshot& snapshot) { - mApp.getBucketManager() - .getBucketSnapshotManager() - .maybeUpdateLedgerStateSnapshot(snapshot); + mApp.getLedgerManager().maybeUpdateLedgerStateSnapshot(snapshot); } LedgerStateSnapshot& diff --git a/src/main/AppConnector.h b/src/main/AppConnector.h index 1d6ecd8d2d..12b774c8c6 100644 --- a/src/main/AppConnector.h +++ b/src/main/AppConnector.h @@ -74,6 +74,7 @@ class AppConnector bool isStopping() const; LedgerStateSnapshot copyLedgerStateSnapshot(); + ApplyLedgerStateSnapshot copyApplyLedgerStateSnapshot(); void maybeUpdateLedgerStateSnapshot(LedgerStateSnapshot& snapshot); // Get a snapshot of ledger state for use by the overlay thread only. Must diff --git a/src/main/ApplicationUtils.cpp b/src/main/ApplicationUtils.cpp index 4398b3a880..5aa85cf38b 100644 --- a/src/main/ApplicationUtils.cpp +++ b/src/main/ApplicationUtils.cpp @@ -26,7 +26,6 @@ #include "main/PersistentState.h" #include "main/StellarCoreVersion.h" #include "overlay/OverlayManager.h" -#include "scp/LocalNode.h" #include "util/GlobalChecks.h" #include "util/Logging.h" #include "util/XDRCereal.h" @@ -37,7 +36,6 @@ #include #include -#include #include #include #include @@ -894,9 +892,7 @@ dumpWasmBlob(Config cfg, std::string const& hash, std::string const& dir) LOG_INFO(DEFAULT_LOG, "Wrote {} bytes to {}", entry.code.size(), filename); }; - auto snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = app->getLedgerManager().copyLedgerStateSnapshot(); if (hash == "ALL") { snap.scanLiveEntriesOfType( diff --git a/src/main/CommandHandler.cpp b/src/main/CommandHandler.cpp index 14d2e4ee56..18c4e00f18 100644 --- a/src/main/CommandHandler.cpp +++ b/src/main/CommandHandler.cpp @@ -3,8 +3,6 @@ // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 #include "main/CommandHandler.h" -#include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "crypto/KeyUtils.h" #include "herder/Herder.h" #include "history/HistoryArchiveManager.h" diff --git a/src/main/QueryServer.h b/src/main/QueryServer.h index dd1466733d..83008b7203 100644 --- a/src/main/QueryServer.h +++ b/src/main/QueryServer.h @@ -6,7 +6,6 @@ #include "lib/httpthreaded/server.hpp" -#include "bucket/BucketSnapshotManager.h" #include "ledger/LedgerStateSnapshot.h" #include #include diff --git a/src/main/test/QueryServerTests.cpp b/src/main/test/QueryServerTests.cpp index b4ba4b3185..bb15d14375 100644 --- a/src/main/test/QueryServerTests.cpp +++ b/src/main/test/QueryServerTests.cpp @@ -3,7 +3,6 @@ // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 #include "bucket/BucketIndexUtils.h" -#include "bucket/BucketManager.h" #include "bucket/test/BucketTestUtils.h" #include "ledger/LedgerTxnImpl.h" #include "ledger/LedgerTypeUtils.h" diff --git a/src/overlay/OverlayManagerImpl.cpp b/src/overlay/OverlayManagerImpl.cpp index d79d9bf9fa..fa201f90f9 100644 --- a/src/overlay/OverlayManagerImpl.cpp +++ b/src/overlay/OverlayManagerImpl.cpp @@ -4,7 +4,6 @@ #include "overlay/OverlayManagerImpl.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "crypto/Hex.h" #include "crypto/SecretKey.h" #include "crypto/ShortHash.h" @@ -1439,9 +1438,8 @@ OverlayManagerImpl::getOverlayThreadSnapshot() if (!mOverlayThreadSnapshot) { // Create a new snapshot - mOverlayThreadSnapshot = mApp.getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + mOverlayThreadSnapshot = + mApp.getLedgerManager().copyLedgerStateSnapshot(); } return *mOverlayThreadSnapshot; } diff --git a/src/simulation/ApplyLoad.cpp b/src/simulation/ApplyLoad.cpp index 3a2636b33d..b6b49f72ff 100644 --- a/src/simulation/ApplyLoad.cpp +++ b/src/simulation/ApplyLoad.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -20,15 +19,11 @@ #include "herder/HerderImpl.h" -#include "medida/metrics_registry.h" - #include "bucket/BucketListSnapshot.h" #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "util/GlobalChecks.h" #include "util/Logging.h" #include "util/XDRCereal.h" -#include "xdrpp/printer.h" #include namespace stellar diff --git a/src/simulation/test/LoadGeneratorTests.cpp b/src/simulation/test/LoadGeneratorTests.cpp index e0dca494e0..2482bce0e9 100644 --- a/src/simulation/test/LoadGeneratorTests.cpp +++ b/src/simulation/test/LoadGeneratorTests.cpp @@ -3,13 +3,11 @@ // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 #include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "crypto/SHA.h" #include "crypto/SecretKey.h" #include "ledger/LedgerManager.h" #include "ledger/LedgerStateSnapshot.h" #include "main/Config.h" -#include "scp/QuorumSetUtils.h" #include "simulation/ApplyLoad.h" #include "simulation/LoadGenerator.h" #include "simulation/Topologies.h" @@ -956,9 +954,7 @@ TEST_CASE("apply load", "[loadgen][applyload][acceptance]") expectedArchivedEntries - 1}; std::set sampleKeys; - auto snap = app->getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = app->getLedgerManager().copyLedgerStateSnapshot(); for (auto idx : sampleIndices) { diff --git a/src/transactions/InvokeHostFunctionOpFrame.cpp b/src/transactions/InvokeHostFunctionOpFrame.cpp index a16a9ac9f1..391fce2887 100644 --- a/src/transactions/InvokeHostFunctionOpFrame.cpp +++ b/src/transactions/InvokeHostFunctionOpFrame.cpp @@ -272,7 +272,7 @@ class InvokeHostFunctionApplyHelper : virtual LedgerAccessHelper rust::Vec mAutoRestoredRwEntryIndices; HostFunctionMetrics mMetrics; // Used for hot archive access only - LedgerStateSnapshot mStateSnapshot; + ApplyLedgerStateSnapshot mStateSnapshot; rust::Box const& mModuleCache; DiagnosticEventManager& mDiagnosticEvents; @@ -286,7 +286,7 @@ class InvokeHostFunctionApplyHelper : virtual LedgerAccessHelper std::optional& refundableFeeTracker, OperationMetaBuilder& opMeta, InvokeHostFunctionOpFrame const& opFrame, SorobanNetworkConfig const& sorobanConfig, - LedgerStateSnapshot stateSnapshot, + ApplyLedgerStateSnapshot stateSnapshot, rust::Box const& moduleCache) : mApp(app) , mRes(res) @@ -981,11 +981,10 @@ class InvokeHostFunctionPreV23ApplyHelper OperationMetaBuilder& opMeta, InvokeHostFunctionOpFrame const& opFrame, SorobanNetworkConfig const& sorobanConfig, rust::Box const& moduleCache) - : InvokeHostFunctionApplyHelper(app, sorobanBasePrngSeed, res, - refundableFeeTracker, opMeta, opFrame, - sorobanConfig, - app.copyLedgerStateSnapshot(), - moduleCache) + : InvokeHostFunctionApplyHelper( + app, sorobanBasePrngSeed, res, refundableFeeTracker, opMeta, + opFrame, sorobanConfig, app.copyApplyLedgerStateSnapshot(), + moduleCache) , PreV23LedgerAccessHelper(ltx) { } diff --git a/src/transactions/ParallelApplyUtils.cpp b/src/transactions/ParallelApplyUtils.cpp index 46e7c49f47..0256fd7100 100644 --- a/src/transactions/ParallelApplyUtils.cpp +++ b/src/transactions/ParallelApplyUtils.cpp @@ -3,13 +3,11 @@ // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 #include "transactions/ParallelApplyUtils.h" -#include "bucket/BucketSnapshotManager.h" #include "bucket/BucketUtils.h" #include "ledger/LedgerEntryScope.h" #include "ledger/LedgerTxn.h" #include "ledger/NetworkConfig.h" #include "main/AppConnector.h" -#include "transactions/MutableTransactionResult.h" #include "transactions/ParallelApplyStage.h" #include "transactions/TransactionFrameBase.h" #include "util/GlobalChecks.h" @@ -297,15 +295,17 @@ ParallelLedgerAccessHelper::eraseLedgerEntryIfExists(LedgerKey const& key) // them are complete. class ThreadParalllelApplyLedgerState; GlobalParallelApplyLedgerState::GlobalParallelApplyLedgerState( - AppConnector& app, AbstractLedgerTxn& ltx, - std::vector const& stages, + AppConnector& app, ApplyLedgerStateSnapshot snapshot, + AbstractLedgerTxn& ltx, std::vector const& stages, InMemorySorobanState const& inMemoryState, SorobanNetworkConfig const& sorobanConfig) : LedgerEntryScope(ScopeIdT(0, ltx.getHeader().ledgerSeq)) - , mSnapshot(app.copyLedgerStateSnapshot()) + , mSnapshot(std::move(snapshot)) , mInMemorySorobanState(inMemoryState) , mSorobanConfig(sorobanConfig) { + releaseAssertOrThrow(mSnapshot.getLedgerSeq() == + mInMemorySorobanState.getLedgerSeq()); releaseAssertOrThrow(ltx.getHeader().ledgerSeq == mSnapshot.getLedgerSeq() + 1); @@ -846,7 +846,7 @@ ThreadParallelApplyLedgerState::getSorobanConfig() const return mSorobanConfig; } -LedgerStateSnapshot const& +ApplyLedgerStateSnapshot const& ThreadParallelApplyLedgerState::getSnapshot() const { return mSnapshot; diff --git a/src/transactions/ParallelApplyUtils.h b/src/transactions/ParallelApplyUtils.h index 8f0259ed16..f687a8f466 100644 --- a/src/transactions/ParallelApplyUtils.h +++ b/src/transactions/ParallelApplyUtils.h @@ -73,7 +73,7 @@ class ThreadParallelApplyLedgerState { // Copy of the ledger state snapshot from the global state, with fresh // file caches for thread safety. - LedgerStateSnapshot mSnapshot; + ApplyLedgerStateSnapshot mSnapshot; // Reference to the live in-memory Soroban state. For Soroban entries // (CONTRACT_DATA, CONTRACT_CODE, TTL), query this in-memory state instead @@ -174,7 +174,7 @@ class ThreadParallelApplyLedgerState SorobanNetworkConfig const& getSorobanConfig() const; - LedgerStateSnapshot const& getSnapshot() const; + ApplyLedgerStateSnapshot const& getSnapshot() const; rust::Box const& getModuleCache() const; }; @@ -186,7 +186,7 @@ class GlobalParallelApplyLedgerState // close, providing access to both the live bucket list and the hot archive // bucket list. Note that this does not reflect changes from the classic // apply phase, but is a snapshot of the start of the ledger. - LedgerStateSnapshot mSnapshot; + ApplyLedgerStateSnapshot mSnapshot; // Contains an exact one-to-one in-memory mapping of the live snapshot for // CONTRACT_DATA, CONTRACT_CODE, and TTL entries. For these entry types, @@ -242,7 +242,9 @@ class GlobalParallelApplyLedgerState std::unordered_set const& readWriteSet); public: - GlobalParallelApplyLedgerState(AppConnector& app, AbstractLedgerTxn& ltx, + GlobalParallelApplyLedgerState(AppConnector& app, + ApplyLedgerStateSnapshot snapshot, + AbstractLedgerTxn& ltx, std::vector const& stages, InMemorySorobanState const& inMemoryState, SorobanNetworkConfig const& sorobanConfig); diff --git a/src/transactions/RestoreFootprintOpFrame.cpp b/src/transactions/RestoreFootprintOpFrame.cpp index a6c549b360..6f8c39120a 100644 --- a/src/transactions/RestoreFootprintOpFrame.cpp +++ b/src/transactions/RestoreFootprintOpFrame.cpp @@ -288,7 +288,7 @@ class RestoreFootprintParallelApplyHelper : virtual public RestoreFootprintApplyHelper, virtual public ParallelLedgerAccessHelper { - LedgerStateSnapshot mSnapshot; + ApplyLedgerStateSnapshot mSnapshot; public: RestoreFootprintParallelApplyHelper( diff --git a/src/transactions/test/InvokeHostFunctionTests.cpp b/src/transactions/test/InvokeHostFunctionTests.cpp index 22c0ca537b..dc6aa96e63 100644 --- a/src/transactions/test/InvokeHostFunctionTests.cpp +++ b/src/transactions/test/InvokeHostFunctionTests.cpp @@ -9,7 +9,6 @@ #include "util/ProtocolVersion.h" #include "util/UnorderedSet.h" #include "xdr/Stellar-transaction.h" -#include #include #include #include @@ -45,8 +44,6 @@ #include #include #include -#include -#include #include "ledger/LedgerManagerImpl.h" @@ -4649,10 +4646,7 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]") auto lk = client.getContract().getDataKey( makeSymbolSCVal("key"), ContractDataDurability::PERSISTENT); - auto snap = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = test.getApp().getLedgerManager().copyLedgerStateSnapshot(); if (evict) { @@ -4730,10 +4724,8 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]") client.get("key", ContractDataDurability::PERSISTENT, 123); - auto restoredSnap = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto restoredSnap = + test.getApp().getLedgerManager().copyLedgerStateSnapshot(); // Restored entries are deleted from Hot Archive REQUIRE(!restoredSnap.loadArchiveEntry(lk)); @@ -7385,10 +7377,8 @@ TEST_CASE("multiple version of same key in a single eviction scan", closeLedgerOn(test.getApp(), ledgerSeq, 2, 1, 2016); } - auto evictSnap = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto evictSnap = + test.getApp().getLedgerManager().copyLedgerStateSnapshot(); REQUIRE(evictSnap.loadArchiveEntry(lk)); }; @@ -7400,10 +7390,8 @@ TEST_CASE("multiple version of same key in a single eviction scan", // levels of the BucketList. test.invokeRestoreOp({lk}, 20166); - auto restoreSnap = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto restoreSnap = + test.getApp().getLedgerManager().copyLedgerStateSnapshot(); auto loadRes = restoreSnap.loadLiveEntry(lk); REQUIRE(loadRes); @@ -7431,8 +7419,7 @@ TEST_CASE_VERSIONS("do not evict outdated keys", "[archival][soroban]") SorobanTest test(app, cfg, false); ContractStorageTestClient client(test); - auto& snapshotManager = - test.getApp().getBucketManager().getBucketSnapshotManager(); + auto& snapshotManager = test.getApp().getLedgerManager(); // WASM and instance should not expire test.invokeExtendOp(client.getContract().getKeys(), 10'000); @@ -7527,8 +7514,7 @@ TEST_CASE("disable eviction scan", "[archival][soroban]") SorobanTest test(cfg, false); ContractStorageTestClient client(test); - auto& snapshotManager = - test.getApp().getBucketManager().getBucketSnapshotManager(); + auto& snapshotManager = test.getApp().getLedgerManager(); // WASM and instance should not expire test.invokeExtendOp(client.getContract().getKeys(), 10'000); @@ -10010,10 +9996,8 @@ TEST_CASE("autorestore from another contract", "[tx][soroban][archival]") REQUIRE(client2.get("key2", ContractDataDurability::PERSISTENT, std::nullopt) == INVOKE_HOST_FUNCTION_ENTRY_ARCHIVED); - auto archivedSnap = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto archivedSnap = + test.getApp().getLedgerManager().copyLedgerStateSnapshot(); REQUIRE(archivedSnap.loadArchiveKeys({lk1, lk2}).size() == 2); REQUIRE(archivedSnap.loadLiveKeys({lk1, lk2}, "load").size() == 0); @@ -10041,10 +10025,8 @@ TEST_CASE("autorestore from another contract", "[tx][soroban][archival]") /*addContractKeys=*/false); REQUIRE(invocation.withExactNonRefundableResourceFee().invoke()); - auto restoredSnap = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto restoredSnap = + test.getApp().getLedgerManager().copyLedgerStateSnapshot(); REQUIRE(restoredSnap.loadLiveKeys({lk1, lk2}, "load").size() == 2); REQUIRE(restoredSnap.loadArchiveKeys({lk1, lk2}).size() == 0); diff --git a/src/transactions/test/ParallelApplyTest.cpp b/src/transactions/test/ParallelApplyTest.cpp index b0f220fdb7..71201a15d2 100644 --- a/src/transactions/test/ParallelApplyTest.cpp +++ b/src/transactions/test/ParallelApplyTest.cpp @@ -10,8 +10,6 @@ #include -#include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "ledger/LedgerStateSnapshot.h" #include "main/Application.h" #include "test/TestUtils.h" @@ -1123,10 +1121,8 @@ applyTestTransactions(TestConfig const& testConfig, uint32_t protocolVersion, std::optional>>> finalEntries; LedgerSnapshot ls(test.getApp()); - auto archiveSnap = test.getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto archiveSnap = + test.getApp().getLedgerManager().copyLedgerStateSnapshot(); for (auto const& k : allKeys) { std::optional liveEntry; diff --git a/src/transactions/test/SorobanTxTestUtils.cpp b/src/transactions/test/SorobanTxTestUtils.cpp index aa6f66ea68..7e524add93 100644 --- a/src/transactions/test/SorobanTxTestUtils.cpp +++ b/src/transactions/test/SorobanTxTestUtils.cpp @@ -3,8 +3,6 @@ // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 #include "SorobanTxTestUtils.h" -#include "bucket/BucketManager.h" -#include "bucket/BucketSnapshotManager.h" #include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTypeUtils.h" #include "rust/RustBridge.h" @@ -1445,10 +1443,7 @@ SorobanTest::getEntryExpirationStatus(LedgerKey const& key) } return ExpirationStatus::LIVE; } - auto snap = getApp() - .getBucketManager() - .getBucketSnapshotManager() - .copyLedgerStateSnapshot(); + auto snap = getApp().getLedgerManager().copyLedgerStateSnapshot(); if (snap.loadArchiveEntry(key) != nullptr) { return ExpirationStatus::HOT_ARCHIVE; From 99bf7d079869037e8d202aadbf9c9785b476e94e Mon Sep 17 00:00:00 2001 From: Garand Tyson Date: Mon, 16 Feb 2026 18:40:17 -0800 Subject: [PATCH 5/5] Fix minor race condition in invariant check --- src/herder/test/HerderTests.cpp | 4 ++ src/invariant/InvariantManagerImpl.cpp | 3 +- src/ledger/LedgerManager.h | 9 ++- src/ledger/LedgerManagerImpl.cpp | 92 +++++++++++--------------- src/ledger/LedgerManagerImpl.h | 35 +++------- src/ledger/LedgerTxn.cpp | 2 +- src/ledger/P23HotArchiveBug.cpp | 22 +++--- src/ledger/P23HotArchiveBug.h | 12 ++-- src/main/QueryServer.cpp | 3 +- 9 files changed, 77 insertions(+), 105 deletions(-) diff --git a/src/herder/test/HerderTests.cpp b/src/herder/test/HerderTests.cpp index ad6debaa07..71a911e87f 100644 --- a/src/herder/test/HerderTests.cpp +++ b/src/herder/test/HerderTests.cpp @@ -5054,6 +5054,8 @@ TEST_CASE("ledger state update flow with parallel apply", "[herder][parallel]") REQUIRE(lm.getLastClosedLedgerNum() == lcl); REQUIRE(lm.getLastClosedLedgerHAS().currentLedger == lastHeader.ledgerSeq); + REQUIRE(lm.copyLedgerStateSnapshot().getLedgerHeader() == + lastHeader); // Apply state got committed, but has not yet been propagated to // read-only state @@ -5105,6 +5107,8 @@ TEST_CASE("ledger state update flow with parallel apply", "[herder][parallel]") auto readOnly = lm.getLastClosedLedgerHeader(); REQUIRE(readOnly.header.ledgerSeq == lcl + 1); REQUIRE(lm.getLastClosedLedgerNum() == lcl + 1); + REQUIRE(lm.copyLedgerStateSnapshot().getLedgerHeader() == + readOnly.header); auto has = lm.getLastClosedLedgerHAS(); REQUIRE(has.currentLedger == readOnly.header.ledgerSeq); diff --git a/src/invariant/InvariantManagerImpl.cpp b/src/invariant/InvariantManagerImpl.cpp index 5412f4a945..a3bf676644 100644 --- a/src/invariant/InvariantManagerImpl.cpp +++ b/src/invariant/InvariantManagerImpl.cpp @@ -191,8 +191,7 @@ InvariantManagerImpl::checkOnLedgerCommit( auto message = fmt::format( FMT_STRING(R"(Invariant "{}" does not hold on ledger commit: {})"), invariant->getName(), result); - onInvariantFailure(invariant, message, - lclSnapshot.getLedgerSeq() + 1); + onInvariantFailure(invariant, message, lclSnapshot.getLedgerSeq() + 1); } } diff --git a/src/ledger/LedgerManager.h b/src/ledger/LedgerManager.h index fd8322c01c..5a785ce267 100644 --- a/src/ledger/LedgerManager.h +++ b/src/ledger/LedgerManager.h @@ -231,14 +231,15 @@ class LedgerManager getLastClosedLedgerHeader() const = 0; // Create a thread-safe copy of the current canonical ledger state - // snapshot. Can be called from any thread. + // snapshot. Can be called from any thread (except for apply, which must use + // copyApplyLedgerStateSnapshot instead). virtual LedgerStateSnapshot copyLedgerStateSnapshot() const = 0; // Create a thread-safe copy of the current canonical ledger state // snapshot, typed as an apply-time snapshot. Used by legacy (pre-V23) // code paths that need an ApplyLedgerStateSnapshot but don't have // access to ApplyState. - // TODO: Refactor such that this doesn' have to be a public function + // TODO: Refactor such that this doesn't have to be a public function virtual ApplyLedgerStateSnapshot copyApplyLedgerStateSnapshot() const = 0; // Refresh `snapshot` if its ledger seq differs from the current canonical @@ -346,9 +347,7 @@ class LedgerManager advanceLedgerStateAndPublish(uint32_t ledgerSeq, bool calledViaExternalize, LedgerCloseData const& ledgerData, CompleteConstLedgerStatePtr newLedgerState, - bool upgradeApplied, - std::shared_ptr - inMemorySnapshotForInvariant) = 0; + bool upgradeApplied) = 0; virtual void assertSetupPhase() const = 0; #ifdef BUILD_TESTS diff --git a/src/ledger/LedgerManagerImpl.cpp b/src/ledger/LedgerManagerImpl.cpp index ba08592de3..235b81576b 100644 --- a/src/ledger/LedgerManagerImpl.cpp +++ b/src/ledger/LedgerManagerImpl.cpp @@ -618,9 +618,8 @@ LedgerManagerImpl::loadLastKnownLedgerInternal(bool skipBuildingFullState) latestLedgerHeader->ledgerVersion); mApplyState.populateInMemorySorobanState(); - maybeRunSnapshotInvariantFromLedgerState( - copyApplyLedgerStateSnapshot(), maybeCopySorobanStateForInvariant(), - /* runInParallel */ false); + maybeRunSnapshotInvariantFromLedgerState(copyApplyLedgerStateSnapshot(), + /* runInParallel */ false); } mApplyState.markEndOfSetupPhase(); } @@ -738,7 +737,10 @@ LedgerManagerImpl::getLastTxFee() const LedgerHeaderHistoryEntry const& LedgerManagerImpl::getLastClosedLedgerHeader() const { - releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + // Must be main thread: returns a reference into mLastClosedLedgerState, + // which is only replaced on the main thread (advanceLastClosedLedgerState). + // A cross-thread caller could hold a dangling reference after replacement. + releaseAssert(threadIsMain()); SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); return mLastClosedLedgerState->getLastClosedLedgerHeader(); @@ -762,38 +764,24 @@ LedgerManagerImpl::getLastClosedLedgerNum() const return mLastClosedLedgerState->getLastClosedLedgerHeader().header.ledgerSeq; } -std::shared_ptr -LedgerManagerImpl::maybeCopySorobanStateForInvariant() -{ - std::shared_ptr inMemorySnapshotForInvariant = - nullptr; - if (mApp.getInvariantManager().shouldRunInvariantSnapshot()) - { - // The in memory state copy is expensive, so we need to mark - // that start of the invariant scan here, not in the callback, to ensure - // we don't trigger a race condition that creates two copies. - mApp.getInvariantManager().markStartOfInvariantSnapshot(); - inMemorySnapshotForInvariant = - std::make_shared( - mApplyState.getInMemorySorobanState()); - } - return inMemorySnapshotForInvariant; -} - void LedgerManagerImpl::maybeRunSnapshotInvariantFromLedgerState( - ApplyLedgerStateSnapshot const& ledgerState, - std::shared_ptr inMemorySnapshotForInvariant, - bool runInParallel) const + ApplyLedgerStateSnapshot const& ledgerState, bool runInParallel) { - releaseAssert(threadIsMain()); - - if (!inMemorySnapshotForInvariant || - !mApp.getConfig().INVARIANT_EXTRA_CHECKS || mApp.isStopping()) + if (!mApp.getConfig().INVARIANT_EXTRA_CHECKS || mApp.isStopping() || + !mApp.getInvariantManager().shouldRunInvariantSnapshot()) { return; } + // The in memory state copy is expensive, so we need to mark the start of + // the invariant scan here, not in the callback, to ensure we don't trigger + // a race condition that creates two copies. + mApp.getInvariantManager().markStartOfInvariantSnapshot(); + auto inMemorySnapshotForInvariant = + std::make_shared( + mApplyState.getInMemorySorobanState()); + // Verify consistency of all snapshot state. auto ledgerSeq = ledgerState.getLedgerSeq(); inMemorySnapshotForInvariant->assertLastClosedLedger(ledgerSeq); @@ -822,7 +810,10 @@ LedgerManagerImpl::maybeRunSnapshotInvariantFromLedgerState( SorobanNetworkConfig const& LedgerManagerImpl::getLastClosedSorobanNetworkConfig() const { - releaseAssert(!mApp.threadIsType(Application::ThreadType::APPLY)); + // Must be main thread: returns a reference into mLastClosedLedgerState, + // which is only replaced on the main thread (advanceLastClosedLedgerState). + // A cross-thread caller could hold a dangling reference after replacement. + releaseAssert(threadIsMain()); SharedLockShared guard(mLedgerStateSnapshotMutex); releaseAssert(mLastClosedLedgerState); releaseAssert(mLastClosedLedgerState->hasSorobanConfig()); @@ -1346,20 +1337,15 @@ getMetaIOContext(Application& app) } // namespace void -LedgerManagerImpl::ledgerCloseComplete( - uint32_t lcl, bool calledViaExternalize, LedgerCloseData const& ledgerData, - bool upgradeApplied, - std::shared_ptr inMemorySnapshotForInvariant) +LedgerManagerImpl::ledgerCloseComplete(uint32_t lcl, bool calledViaExternalize, + LedgerCloseData const& ledgerData, + bool upgradeApplied) { // We just finished applying `lcl`, maybe change LM's state // Also notify Herder so it can trigger next ledger. releaseAssert(threadIsMain()); - // Kick off the snapshot invariant, if enabled - ApplyLedgerStateSnapshot stateCopy = mApplyState.copyLedgerStateSnapshot(); - maybeRunSnapshotInvariantFromLedgerState(stateCopy, - inMemorySnapshotForInvariant); uint32_t latestHeardFromNetwork = mApp.getLedgerApplyManager().getLargestLedgerSeqHeard(); uint32_t latestQueuedToApply = @@ -1401,8 +1387,7 @@ void LedgerManagerImpl::advanceLedgerStateAndPublish( uint32_t ledgerSeq, bool calledViaExternalize, LedgerCloseData const& ledgerData, - CompleteConstLedgerStatePtr newLedgerState, bool upgradeApplied, - std::shared_ptr inMemorySnapshotForInvariant) + CompleteConstLedgerStatePtr newLedgerState, bool upgradeApplied) { #ifdef BUILD_TESTS if (mAdvanceLedgerStateAndPublishOverride) @@ -1440,7 +1425,7 @@ LedgerManagerImpl::advanceLedgerStateAndPublish( // Maybe set LedgerManager into synced state, maybe let // Herder trigger next ledger ledgerCloseComplete(ledgerSeq, calledViaExternalize, ledgerData, - upgradeApplied, inMemorySnapshotForInvariant); + upgradeApplied); CLOG_INFO(Ledger, "Ledger close complete: {}", ledgerSeq); } @@ -1828,10 +1813,12 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData, mApplyState.markEndOfCommitting(); JITTER_INJECT_DELAY(); - // Step 5: copy the in-memory Soroban state if we should run the snapshot - // invariant for this ledger. At this point, commit has completed and - // in-memory state is immutable. - auto inMemorySnapshotForInvariant = maybeCopySorobanStateForInvariant(); + // Step 5: kick off the snapshot invariant, if the timer has fired. + // Both the apply-state snapshot and the in-memory Soroban state are + // captured here at the same point (after commit), so they are guaranteed + // to be from the same ledger. + maybeRunSnapshotInvariantFromLedgerState( + mApplyState.copyLedgerStateSnapshot()); // Steps 6, 7, 8 are done in `advanceLedgerStateAndPublish` // NB: appliedLedgerState is invalidated after this call. @@ -1839,18 +1826,16 @@ LedgerManagerImpl::applyLedger(LedgerCloseData const& ledgerData, { advanceLedgerStateAndPublish(ledgerSeq, calledViaExternalize, ledgerData, std::move(appliedLedgerState), - upgradeApplied, - inMemorySnapshotForInvariant); + upgradeApplied); } else { auto cb = [this, ledgerSeq, calledViaExternalize, ledgerData, appliedLedgerState = std::move(appliedLedgerState), - upgradeApplied, inMemorySnapshotForInvariant]() mutable { + upgradeApplied]() mutable { advanceLedgerStateAndPublish( ledgerSeq, calledViaExternalize, ledgerData, - std::move(appliedLedgerState), upgradeApplied, - inMemorySnapshotForInvariant); + std::move(appliedLedgerState), upgradeApplied); }; mApp.postOnMainThread(std::move(cb), "advanceLedgerStateAndPublish"); } @@ -2176,12 +2161,12 @@ LedgerManagerImpl::updateCanonicalStateForTesting(LedgerHeader const& header) HistoryArchiveState has; has.currentLedger = header.ledgerSeq; + SharedLockExclusive lock(mLedgerStateSnapshotMutex); auto state = buildLedgerState(header, has, mLastClosedLedgerState, std::nullopt); mApplyState.setLedgerStateForTesting(state); - SharedLockExclusive lock(mLedgerStateSnapshotMutex); mLastClosedLedgerState = state; } #endif @@ -2967,7 +2952,7 @@ LedgerManagerImpl::finalizeLedgerTxnChanges( if (isP24UpgradeLedger && gIsProductionNetwork) { p23_hot_archive_bug::addHotArchiveBatchWithP23HotArchiveFix( - ltx, mApp, lh, evictedState.archivedEntries, + ltx, mApp, lclSnapshot, lh, evictedState.archivedEntries, restoredHotArchiveKeys); } else @@ -2981,7 +2966,8 @@ LedgerManagerImpl::finalizeLedgerTxnChanges( { mApp.getProtocol23CorruptionDataVerifier() ->verifyArchivalOfCorruptedEntry( - evictedState, mApp, lh.ledgerSeq, lh.ledgerVersion); + evictedState, lclSnapshot, lh.ledgerSeq, + lh.ledgerVersion); } } } diff --git a/src/ledger/LedgerManagerImpl.h b/src/ledger/LedgerManagerImpl.h index 7ea3fddb79..b983e40476 100644 --- a/src/ledger/LedgerManagerImpl.h +++ b/src/ledger/LedgerManagerImpl.h @@ -408,22 +408,12 @@ class LedgerManagerImpl : public LedgerManager storePersistentStateAndLedgerHeaderInDB(LedgerHeader const& header, bool appendToCheckpoint); - // Copies in-memory Soroban state for snapshot invariant if required for - // this ledger, or returns nullptr otherwise. Should be called in - // READY_TO_APPLY phase when InMemorySorobanState is read only. - // Also clears the snapshot trigger flag to prevent race conditions. - std::shared_ptr - maybeCopySorobanStateForInvariant(); - - // Trigger snapshot invariant on background thread if - // inMemorySnapshotForInvariant is not null. - // If runInParallel is false, runs on the calling thread (this is useful in - // certain scenarios such as startup) + // If the invariant timer has fired, copies the in-memory Soroban state and + // the apply-state snapshot, then kicks off the snapshot invariant check. + // If runInParallel is false, runs on the calling thread (useful at + // startup). void maybeRunSnapshotInvariantFromLedgerState( - ApplyLedgerStateSnapshot const& ledgerState, - std::shared_ptr - inMemorySnapshotForInvariant, - bool runInParallel = true) const; + ApplyLedgerStateSnapshot const& ledgerState, bool runInParallel = true); static void prefetchTransactionData(AbstractLedgerTxnParent& rootLtx, ApplicableTxSetFrame const& txSet, @@ -561,17 +551,14 @@ class LedgerManagerImpl : public LedgerManager void applyLedger(LedgerCloseData const& ledgerData, bool calledViaExternalize) override; - void advanceLedgerStateAndPublish( - uint32_t ledgerSeq, bool calledViaExternalize, - LedgerCloseData const& ledgerData, - CompleteConstLedgerStatePtr newLedgerState, bool queueRebuildNeeded, - std::shared_ptr - inMemorySnapshotForInvariant = nullptr) override; + void + advanceLedgerStateAndPublish(uint32_t ledgerSeq, bool calledViaExternalize, + LedgerCloseData const& ledgerData, + CompleteConstLedgerStatePtr newLedgerState, + bool queueRebuildNeeded) override; void ledgerCloseComplete(uint32_t lcl, bool calledViaExternalize, LedgerCloseData const& ledgerData, - bool queueRebuildNeeded, - std::shared_ptr - inMemorySnapshotForInvariant); + bool queueRebuildNeeded); void setLastClosedLedger(LedgerHeaderHistoryEntry const& lastClosed, bool rebuildInMemoryState) override; diff --git a/src/ledger/LedgerTxn.cpp b/src/ledger/LedgerTxn.cpp index 0e5c8b8e33..7373084f06 100644 --- a/src/ledger/LedgerTxn.cpp +++ b/src/ledger/LedgerTxn.cpp @@ -2734,10 +2734,10 @@ LedgerTxnRoot::Impl::Impl(Application& app, , mEntryCache(entryCacheSize) , mBulkLoadBatchSize(prefetchBatchSize) , mChild(nullptr) + , mThreadInvariant() #ifdef BEST_OFFER_DEBUGGING , mBestOfferDebuggingEnabled(bestOfferDebuggingEnabled) #endif - , mThreadInvariant() { } diff --git a/src/ledger/P23HotArchiveBug.cpp b/src/ledger/P23HotArchiveBug.cpp index 0d1482c452..09e871bb45 100644 --- a/src/ledger/P23HotArchiveBug.cpp +++ b/src/ledger/P23HotArchiveBug.cpp @@ -7,13 +7,11 @@ #include #include -#include "bucket/BucketListSnapshot.h" #include "bucket/BucketManager.h" #include "bucket/BucketUtils.h" -#include "ledger/LedgerManager.h" +#include "ledger/LedgerStateSnapshot.h" #include "ledger/LedgerTxn.h" #include "ledger/LedgerTxnImpl.h" -#include "main/AppConnector.h" #include "main/Application.h" #include @@ -36,7 +34,8 @@ using namespace internal; void addHotArchiveBatchWithP23HotArchiveFix( - AbstractLedgerTxn& ltx, Application& app, LedgerHeader header, + AbstractLedgerTxn& ltx, Application& app, + ApplyLedgerStateSnapshot const& snapshot, LedgerHeader header, std::vector const& archivedEntries, std::vector const& restoredEntries) { @@ -49,7 +48,6 @@ addHotArchiveBatchWithP23HotArchiveFix( auto updatedArchivedEntries = archivedEntries; updatedArchivedEntries.reserve(updatedArchivedEntries.size() + P23_CORRUPTED_HOT_ARCHIVE_ENTRIES_COUNT); - auto snap = app.getAppConnector().copyLedgerStateSnapshot(); for (size_t i = 0; i < P23_CORRUPTED_HOT_ARCHIVE_ENTRIES_COUNT; ++i) { LedgerEntry corruptedEntry = @@ -67,7 +65,7 @@ addHotArchiveBatchWithP23HotArchiveFix( // Hot Archive that match our expectations for the corrupted entries. // Ensure that the entry exists in Hot Archive. - auto hotArchiveEntry = snap.loadArchiveEntry(corruptedEntryKey); + auto hotArchiveEntry = snapshot.loadArchiveEntry(corruptedEntryKey); if (!hotArchiveEntry) { CLOG_WARNING( @@ -336,8 +334,9 @@ Protocol23CorruptionDataVerifier::verifyRestorationOfCorruptedEntry( void Protocol23CorruptionDataVerifier::verifyArchivalOfCorruptedEntry( - EvictedStateVectors const& evictedState, Application& app, - uint32_t ledgerSeq, uint32_t protocolVersion) + EvictedStateVectors const& evictedState, + ApplyLedgerStateSnapshot const& snapshot, uint32_t ledgerSeq, + uint32_t protocolVersion) { if (!protocolVersionEquals(protocolVersion, ProtocolVersion::V_23)) { @@ -347,11 +346,6 @@ Protocol23CorruptionDataVerifier::verifyArchivalOfCorruptedEntry( // p23 we haven't increased the number of threads. std::lock_guard lock(mMutex); - // This database can load the actual, correct version of a - // given ledger key. This tells us the value that should - // have been evicted. - auto snap = app.getLedgerManager().copyLedgerStateSnapshot(); - // This is the set of all keys incorrectly evicted for this // ledger auto evictedKeysIter = mEvictedSeqToKeys.find(ledgerSeq); @@ -365,7 +359,7 @@ Protocol23CorruptionDataVerifier::verifyArchivalOfCorruptedEntry( { // Load the correct value from the live database. auto evictedLedgerKey = LedgerEntryKey(evictedEntry); - auto databaseEntry = snap.loadLiveEntry(evictedLedgerKey); + auto databaseEntry = snapshot.loadLiveEntry(evictedLedgerKey); releaseAssert(databaseEntry != nullptr); // If there was a corruption diff --git a/src/ledger/P23HotArchiveBug.h b/src/ledger/P23HotArchiveBug.h index 690620217c..565df5f2d2 100644 --- a/src/ledger/P23HotArchiveBug.h +++ b/src/ledger/P23HotArchiveBug.h @@ -18,6 +18,7 @@ namespace stellar { class Application; class AbstractLedgerTxn; +class ApplyLedgerStateSnapshot; class Config; struct EvictedStateVectors; @@ -86,9 +87,11 @@ class Protocol23CorruptionDataVerifier // This should be called for every eviction that occurs during catchup, // non-corrupted evictions are ignored. // This is thread-safe. - void verifyArchivalOfCorruptedEntry(EvictedStateVectors const& evictedState, - Application& app, uint32_t ledgerSeq, - uint32_t protocolVersion); + void + verifyArchivalOfCorruptedEntry(EvictedStateVectors const& evictedState, + ApplyLedgerStateSnapshot const& snapshot, + uint32_t ledgerSeq, + uint32_t protocolVersion); // Verifies that the batch of Hot Archive fixes on protocol 24 upgrade // corresponds to the expected data (i.e. only entries that were never // restored have been fixed, and that the fix comes back to the correct @@ -171,7 +174,8 @@ class Protocol23CorruptionEventReconciler }; void addHotArchiveBatchWithP23HotArchiveFix( - AbstractLedgerTxn& ltx, Application& app, LedgerHeader header, + AbstractLedgerTxn& ltx, Application& app, + ApplyLedgerStateSnapshot const& snapshot, LedgerHeader header, std::vector const& archivedEntries, std::vector const& restoredEntries); diff --git a/src/main/QueryServer.cpp b/src/main/QueryServer.cpp index ccd825a7bc..f09628fc50 100644 --- a/src/main/QueryServer.cpp +++ b/src/main/QueryServer.cpp @@ -87,8 +87,7 @@ QueryServer::QueryServer(std::string const& address, unsigned short port, auto workerPids = mServer.start(); for (auto pid : workerPids) { - mSnapshots.emplace(pid, - mAppConnector.copyLedgerStateSnapshot()); + mSnapshots.emplace(pid, mAppConnector.copyLedgerStateSnapshot()); } } }