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.cpp b/src/bucket/BucketListSnapshot.cpp
index eae919bc06..99a44a8dfc 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,
+ uint32_t ledgerSeq)
: mData(std::move(data))
, mHistoricalSnapshots(std::move(historicalSnapshots))
+ , mLedgerSeq(ledgerSeq)
, mMetrics(metrics)
, mBulkLoadMeter(
metrics.NewMeter({BucketT::METRIC_STRING, "query", "loads"}, "query"))
@@ -87,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
@@ -292,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{};
@@ -336,7 +363,7 @@ SearchableBucketListSnapshot::loadKeysInternal(
return keys.empty() ? Loop::COMPLETE : Loop::INCOMPLETE;
};
- if (!ledgerSeq || *ledgerSeq == mData->getLedgerSeq())
+ if (!ledgerSeq || *ledgerSeq == mLedgerSeq)
{
loopAllBuckets(loadKeysLoop, *mData);
}
@@ -370,34 +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;
-}
-
-template
-uint32_t
-SearchableBucketListSnapshot::getLedgerSeq() const
-{
- releaseAssert(mData);
- return mData->getLedgerSeq();
-}
-
-template
-LedgerHeader const&
-SearchableBucketListSnapshot::getLedgerHeader() const
-{
- releaseAssert(mData);
- return mData->header;
+ return iter->second.get();
}
template
@@ -424,9 +435,10 @@ SearchableLiveBucketListSnapshot::SearchableLiveBucketListSnapshot(
std::shared_ptr const> data,
std::map const>>
- historicalSnapshots)
- : SearchableBucketListSnapshot(metrics, std::move(data),
- std::move(historicalSnapshots))
+ historicalSnapshots,
+ uint32_t ledgerSeq)
+ : SearchableBucketListSnapshot(
+ metrics, std::move(data), std::move(historicalSnapshots), ledgerSeq)
{
}
@@ -855,9 +867,10 @@ SearchableHotArchiveBucketListSnapshot::SearchableHotArchiveBucketListSnapshot(
std::shared_ptr const> data,
std::map const>>
- historicalSnapshots)
+ historicalSnapshots,
+ uint32_t ledgerSeq)
: SearchableBucketListSnapshot(
- metrics, std::move(data), std::move(historicalSnapshots))
+ metrics, std::move(data), std::move(historicalSnapshots), ledgerSeq)
{
}
diff --git a/src/bucket/BucketListSnapshot.h b/src/bucket/BucketListSnapshot.h
index 0412a5f966..371932456a 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
@@ -34,7 +33,6 @@ class Timer;
namespace stellar
{
-class BucketSnapshotManager;
struct EvictionMetrics;
struct EvictionResultCandidates;
struct EvictionResultEntry;
@@ -43,10 +41,12 @@ struct StateArchivalSettings;
class EvictionStatistics;
template class BucketListBase;
template class BucketLevel;
+class CompleteConstLedgerState;
+class LedgerStateSnapshot;
-// 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,20 +84,27 @@ template class SearchableBucketListSnapshot
std::map const>>
mHistoricalSnapshots;
+ // 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 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 --
@@ -151,9 +154,17 @@ template class SearchableBucketListSnapshot
std::shared_ptr const> data,
std::map const>>
- historicalSnapshots);
+ historicalSnapshots,
+ 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
@@ -170,9 +181,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;
@@ -191,7 +199,8 @@ class SearchableLiveBucketListSnapshot
std::shared_ptr const> data,
std::map const>>
- historicalSnapshots);
+ historicalSnapshots,
+ uint32_t ledgerSeq);
Loop scanForEvictionInBucket(
std::shared_ptr const& bucket, EvictionIterator& iter,
@@ -200,6 +209,9 @@ class SearchableLiveBucketListSnapshot
UnorderedSet& keysInEvictableEntries) const;
public:
+ SearchableLiveBucketListSnapshot(SearchableLiveBucketListSnapshot const&) =
+ default;
+
std::vector
loadKeys(std::set const& inKeys,
std::string const& label) const;
@@ -222,7 +234,9 @@ class SearchableLiveBucketListSnapshot
LedgerEntryType type,
std::function callback) const;
- friend class BucketSnapshotManager;
+ friend class BucketSnapshotState;
+ friend class CompleteConstLedgerState;
+ friend class LedgerStateSnapshot;
};
// Hot archive bucket list snapshot
@@ -234,9 +248,13 @@ class SearchableHotArchiveBucketListSnapshot
std::shared_ptr const> data,
std::map const>>
- historicalSnapshots);
+ historicalSnapshots,
+ uint32_t ledgerSeq);
public:
+ SearchableHotArchiveBucketListSnapshot(
+ SearchableHotArchiveBucketListSnapshot const&) = default;
+
std::vector
loadKeys(std::set const& inKeys) const;
@@ -245,7 +263,7 @@ class SearchableHotArchiveBucketListSnapshot
void scanAllEntries(
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..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"
@@ -17,7 +16,7 @@
#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"
#include "ledger/NetworkConfig.h"
@@ -33,8 +32,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
@@ -49,7 +49,6 @@
#include "medida/counter.h"
#include "medida/meter.h"
#include "medida/timer.h"
-#include "work/WorkScheduler.h"
#include "xdrpp/printer.h"
#include
@@ -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,29 +1148,27 @@ BucketManager::maybeSetIndex(
}
void
-BucketManager::startBackgroundEvictionScan(
- SearchableSnapshotConstPtr lclSnapshot, SorobanNetworkConfig const& cfg)
+BucketManager::startBackgroundEvictionScan(ApplyLedgerStateSnapshot 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 +1179,7 @@ BucketManager::startBackgroundEvictionScan(
EvictedStateVectors
BucketManager::resolveBackgroundEvictionScan(
- SearchableSnapshotConstPtr lclSnapshot, AbstractLedgerTxn& ltx,
+ ApplyLedgerStateSnapshot const& lclSnapshot, AbstractLedgerTxn& ltx,
LedgerKeySet const& modifiedKeys)
{
ZoneScoped;
@@ -1203,7 +1189,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())
{
diff --git a/src/bucket/BucketManager.h b/src/bucket/BucketManager.h
index 24da0e171c..2a768db71a 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"
@@ -19,7 +20,6 @@
#include
#include