Skip to content

Commit f2ffc49

Browse files
committed
Extract entries by reference from LTX.
Currently `getAllEntries` call seals the LTX and unnecessarily copies every entry, which adds up to a few ms if thousands of entries are involved. With this change we just return the references to all the entries. Since this call is used just once in the 'production' path, and the results are only used in a couple of read-only calls, it shouldn't be hard to maintain the lifetime correctness for this method.
1 parent 4cdc91b commit f2ffc49

20 files changed

Lines changed: 256 additions & 103 deletions

src/bucket/BucketListBase.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,6 @@ template void BucketListBase<HotArchiveBucket>::addBatchInternal(
890890

891891
template void BucketListBase<LiveBucket>::addBatchInternal(
892892
Application& app, uint32_t currLedger, uint32_t currLedgerProtocol,
893-
std::vector<LedgerEntry> const& initEntries,
894-
std::vector<LedgerEntry> const& liveEntries,
895-
std::vector<LedgerKey> const& deadEntries);
893+
LedgerEntryRefs const& initEntries, LedgerEntryRefs const& liveEntries,
894+
LedgerKeyRefs const& deadEntries);
896895
}

src/bucket/BucketManager.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,9 +1024,9 @@ BucketManager::forgetUnreferencedBuckets(HistoryArchiveState const& has)
10241024

10251025
void
10261026
BucketManager::addLiveBatch(Application& app, LedgerHeader header,
1027-
std::vector<LedgerEntry> const& initEntries,
1028-
std::vector<LedgerEntry> const& liveEntries,
1029-
std::vector<LedgerKey> const& deadEntries)
1027+
LedgerEntryRefs initEntries,
1028+
LedgerEntryRefs liveEntries,
1029+
LedgerKeyRefs deadEntries)
10301030
{
10311031
ZoneScoped;
10321032
#ifdef BUILD_TESTS

src/bucket/BucketManager.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "bucket/BucketMergeMap.h"
88
#include "history/HistoryArchive.h"
99
#include "ledger/ImmutableLedgerView.h"
10+
#include "ledger/LedgerEntryRefs.h"
1011
#include "ledger/NetworkConfig.h"
1112
#include "main/Config.h"
1213
#include "util/ThreadAnnotations.h"
@@ -315,9 +316,8 @@ class BucketManager : NonMovableOrCopyable
315316
// `header` value should be taken from the ledger at which this batch is
316317
// being added.
317318
void addLiveBatch(Application& app, LedgerHeader header,
318-
std::vector<LedgerEntry> const& initEntries,
319-
std::vector<LedgerEntry> const& liveEntries,
320-
std::vector<LedgerKey> const& deadEntries);
319+
LedgerEntryRefs initEntries, LedgerEntryRefs liveEntries,
320+
LedgerKeyRefs deadEntries);
321321
void addHotArchiveBatch(Application& app, LedgerHeader header,
322322
std::vector<LedgerEntry> const& archivedEntries,
323323
std::vector<LedgerKey> const& restoredEntries);

src/bucket/LiveBucket.cpp

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -377,11 +377,24 @@ LiveBucket::getRangeForType(LedgerEntryType type) const
377377
return getIndex().getRangeForType(type);
378378
}
379379

380+
#ifdef BUILD_TESTS
380381
std::vector<BucketEntry>
381382
LiveBucket::convertToBucketEntry(bool useInit,
382383
std::vector<LedgerEntry> const& initEntries,
383384
std::vector<LedgerEntry> const& liveEntries,
384385
std::vector<LedgerKey> const& deadEntries)
386+
{
387+
auto initRefs = toRefs(initEntries);
388+
auto liveRefs = toRefs(liveEntries);
389+
auto deadRefs = toRefs(deadEntries);
390+
return convertToBucketEntry(useInit, initRefs, liveRefs, deadRefs);
391+
}
392+
#endif
393+
394+
std::vector<BucketEntry>
395+
LiveBucket::convertToBucketEntry(bool useInit, LedgerEntryRefs initEntries,
396+
LedgerEntryRefs liveEntries,
397+
LedgerKeyRefs deadEntries)
385398
{
386399
ZoneScoped;
387400
size_t totalSize =
@@ -393,21 +406,21 @@ LiveBucket::convertToBucketEntry(bool useInit,
393406
std::vector<BucketEntry*> sortedEntries;
394407
sortedEntries.reserve(totalSize);
395408

396-
for (auto const& e : initEntries)
409+
for (LedgerEntry const& e : initEntries)
397410
{
398411
auto& ce = entries.emplace_back();
399412
ce.type(useInit ? INITENTRY : LIVEENTRY);
400413
ce.liveEntry() = e;
401414
sortedEntries.push_back(&ce);
402415
}
403-
for (auto const& e : liveEntries)
416+
for (LedgerEntry const& e : liveEntries)
404417
{
405418
auto& ce = entries.emplace_back();
406419
ce.type(LIVEENTRY);
407420
ce.liveEntry() = e;
408421
sortedEntries.push_back(&ce);
409422
}
410-
for (auto const& e : deadEntries)
423+
for (LedgerKey const& e : deadEntries)
411424
{
412425
auto& ce = entries.emplace_back();
413426
ce.type(DEADENTRY);
@@ -433,12 +446,27 @@ LiveBucket::convertToBucketEntry(bool useInit,
433446
return bucket;
434447
}
435448

449+
#ifdef BUILD_TESTS
436450
std::shared_ptr<LiveBucket>
437451
LiveBucket::fresh(BucketManager& bucketManager, uint32_t protocolVersion,
438452
std::vector<LedgerEntry> const& initEntries,
439453
std::vector<LedgerEntry> const& liveEntries,
440454
std::vector<LedgerKey> const& deadEntries,
441455
bool countMergeEvents, asio::io_context& ctx, bool doFsync)
456+
{
457+
auto initRefs = toRefs(initEntries);
458+
auto liveRefs = toRefs(liveEntries);
459+
auto deadRefs = toRefs(deadEntries);
460+
return fresh(bucketManager, protocolVersion, initRefs, liveRefs, deadRefs,
461+
countMergeEvents, ctx, doFsync);
462+
}
463+
#endif
464+
465+
std::shared_ptr<LiveBucket>
466+
LiveBucket::fresh(BucketManager& bucketManager, uint32_t protocolVersion,
467+
LedgerEntryRefs initEntries, LedgerEntryRefs liveEntries,
468+
LedgerKeyRefs deadEntries, bool countMergeEvents,
469+
asio::io_context& ctx, bool doFsync)
442470
{
443471
ZoneScoped;
444472
// When building fresh buckets after protocol version 10 (i.e. version
@@ -480,10 +508,9 @@ LiveBucket::fresh(BucketManager& bucketManager, uint32_t protocolVersion,
480508
std::shared_ptr<LiveBucket>
481509
LiveBucket::freshInMemoryOnly(BucketManager& bucketManager,
482510
uint32_t protocolVersion,
483-
std::vector<LedgerEntry> const& initEntries,
484-
std::vector<LedgerEntry> const& liveEntries,
485-
std::vector<LedgerKey> const& deadEntries,
486-
bool countMergeEvents)
511+
LedgerEntryRefs initEntries,
512+
LedgerEntryRefs liveEntries,
513+
LedgerKeyRefs deadEntries, bool countMergeEvents)
487514
{
488515
ZoneScoped;
489516
// When building fresh buckets after protocol version 10 (i.e. version

src/bucket/LiveBucket.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "bucket/BucketBase.h"
88
#include "bucket/BucketUtils.h"
99
#include "bucket/LiveBucketIndex.h"
10+
#include "ledger/LedgerEntryRefs.h"
1011

1112
namespace medida
1213
{
@@ -82,11 +83,17 @@ class LiveBucket : public BucketBase<LiveBucket, LiveBucketIndex>,
8283
static void checkProtocolLegality(BucketEntry const& entry,
8384
uint32_t protocolVersion);
8485

86+
static std::vector<BucketEntry>
87+
convertToBucketEntry(bool useInit, LedgerEntryRefs initEntries,
88+
LedgerEntryRefs liveEntries,
89+
LedgerKeyRefs deadEntries);
90+
#ifdef BUILD_TESTS
8591
static std::vector<BucketEntry>
8692
convertToBucketEntry(bool useInit,
8793
std::vector<LedgerEntry> const& initEntries,
8894
std::vector<LedgerEntry> const& liveEntries,
8995
std::vector<LedgerKey> const& deadEntries);
96+
#endif
9097

9198
template <typename InputSource>
9299
static void mergeCasesWithEqualKeys(
@@ -116,22 +123,27 @@ class LiveBucket : public BucketBase<LiveBucket, LiveBucketIndex>,
116123
// Create a fresh bucket from given vectors of init (created) and live
117124
// (updated) LedgerEntries, and dead LedgerEntryKeys. The bucket will
118125
// be sorted, hashed, and adopted in the provided BucketManager.
126+
static std::shared_ptr<LiveBucket>
127+
fresh(BucketManager& bucketManager, uint32_t protocolVersion,
128+
LedgerEntryRefs initEntries, LedgerEntryRefs liveEntries,
129+
LedgerKeyRefs deadEntries, bool countMergeEvents,
130+
asio::io_context& ctx, bool doFsync);
131+
#ifdef BUILD_TESTS
119132
static std::shared_ptr<LiveBucket>
120133
fresh(BucketManager& bucketManager, uint32_t protocolVersion,
121134
std::vector<LedgerEntry> const& initEntries,
122135
std::vector<LedgerEntry> const& liveEntries,
123136
std::vector<LedgerKey> const& deadEntries, bool countMergeEvents,
124137
asio::io_context& ctx, bool doFsync);
138+
#endif
125139

126140
// Create a fresh bucket that exists only in memory, without writing to
127141
// disk, calculating a hash, or indexing. This should only be used for
128142
// "level -1" snap buckets that are immediately merged into level 0.
129143
static std::shared_ptr<LiveBucket>
130144
freshInMemoryOnly(BucketManager& bucketManager, uint32_t protocolVersion,
131-
std::vector<LedgerEntry> const& initEntries,
132-
std::vector<LedgerEntry> const& liveEntries,
133-
std::vector<LedgerKey> const& deadEntries,
134-
bool countMergeEvents);
145+
LedgerEntryRefs initEntries, LedgerEntryRefs liveEntries,
146+
LedgerKeyRefs deadEntries, bool countMergeEvents);
135147

136148
// Returns true if the given BucketEntry should be dropped in the bottom
137149
// level bucket (i.e. DEADENTRY)

src/bucket/LiveBucketList.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ namespace stellar
1414
void
1515
LiveBucketList::addBatch(Application& app, uint32_t currLedger,
1616
uint32_t currLedgerProtocol,
17-
std::vector<LedgerEntry> const& initEntries,
18-
std::vector<LedgerEntry> const& liveEntries,
19-
std::vector<LedgerKey> const& deadEntries)
17+
LedgerEntryRefs initEntries,
18+
LedgerEntryRefs liveEntries, LedgerKeyRefs deadEntries)
2019
{
2120
ZoneScoped;
2221
addBatchInternal(app, currLedger, currLedgerProtocol, initEntries,
@@ -26,6 +25,21 @@ LiveBucketList::addBatch(Application& app, uint32_t currLedger,
2625
maybeInitializeCaches(app.getConfig());
2726
}
2827

28+
#ifdef BUILD_TESTS
29+
void
30+
LiveBucketList::addBatch(Application& app, uint32_t currLedger,
31+
uint32_t currLedgerProtocol,
32+
std::vector<LedgerEntry> const& initEntries,
33+
std::vector<LedgerEntry> const& liveEntries,
34+
std::vector<LedgerKey> const& deadEntries)
35+
{
36+
auto initRefs = toRefs(initEntries);
37+
auto liveRefs = toRefs(liveEntries);
38+
auto deadRefs = toRefs(deadEntries);
39+
addBatch(app, currLedger, currLedgerProtocol, initRefs, liveRefs, deadRefs);
40+
}
41+
#endif
42+
2943
BucketEntryCounters
3044
LiveBucketList::sumBucketEntryCounters() const
3145
{

src/bucket/LiveBucketList.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,16 @@ class LiveBucketList : public BucketListBase<LiveBucket>
4545
// should have spilled due to passing through `currLedger`. The `currLedger`
4646
// and `currProtocolVersion` values should be taken from the ledger at which
4747
// this batch is being added.
48+
void addBatch(Application& app, uint32_t currLedger,
49+
uint32_t currLedgerProtocol, LedgerEntryRefs initEntries,
50+
LedgerEntryRefs liveEntries, LedgerKeyRefs deadEntries);
51+
#ifdef BUILD_TESTS
4852
void addBatch(Application& app, uint32_t currLedger,
4953
uint32_t currLedgerProtocol,
5054
std::vector<LedgerEntry> const& initEntries,
5155
std::vector<LedgerEntry> const& liveEntries,
5256
std::vector<LedgerKey> const& deadEntries);
57+
#endif
5358

5459
BucketEntryCounters sumBucketEntryCounters() const;
5560

src/bucket/test/BucketTestUtils.cpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ getAppLedgerVersion(Application::pointer app)
3737

3838
void
3939
addLiveBatchAndUpdateSnapshot(Application& app, LedgerHeader header,
40-
std::vector<LedgerEntry> const& initEntries,
41-
std::vector<LedgerEntry> const& liveEntries,
42-
std::vector<LedgerKey> const& deadEntries)
40+
LedgerEntryRefs initEntries,
41+
LedgerEntryRefs liveEntries,
42+
LedgerKeyRefs deadEntries)
4343
{
4444
auto& liveBl = app.getBucketManager().getLiveBucketList();
4545
liveBl.addBatch(app, header.ledgerSeq, header.ledgerVersion, initEntries,
@@ -48,6 +48,18 @@ addLiveBatchAndUpdateSnapshot(Application& app, LedgerHeader header,
4848
app.getLedgerManager().updateCanonicalStateForTesting(header);
4949
}
5050

51+
void
52+
addLiveBatchAndUpdateSnapshot(Application& app, LedgerHeader header,
53+
std::vector<LedgerEntry> const& initEntries,
54+
std::vector<LedgerEntry> const& liveEntries,
55+
std::vector<LedgerKey> const& deadEntries)
56+
{
57+
auto initRefs = toRefs(initEntries);
58+
auto liveRefs = toRefs(liveEntries);
59+
auto deadRefs = toRefs(deadEntries);
60+
addLiveBatchAndUpdateSnapshot(app, header, initRefs, liveRefs, deadRefs);
61+
}
62+
5163
void
5264
addHotArchiveBatchAndUpdateSnapshot(
5365
Application& app, LedgerHeader header,
@@ -181,8 +193,8 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges(
181193
if (mUseTestEntries)
182194
{
183195
// Seal the ltx but throw its entries away.
184-
std::vector<LedgerEntry> init, live;
185-
std::vector<LedgerKey> dead;
196+
LedgerEntryRefVec init, live;
197+
LedgerKeyRefVec dead;
186198

187199
// Any V20 features must be behind initialLedgerVers check, see comment
188200
// in LedgerManagerImpl::ledgerClosed
@@ -262,7 +274,7 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges(
262274
// Seal the ltx and collect its entries, but don't load Soroban
263275
// config yet -- test entries (including network config like eviction
264276
// iterator) are added directly to the BucketList below, not via ltx.
265-
ltx.getAllEntries(init, live, dead);
277+
ltx.sealAndBorrowAllEntries(init, live, dead);
266278

267279
// Add dead entries from ltx to entries that will be added to BucketList
268280
// so we can test background eviction properly
@@ -280,7 +292,7 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges(
280292
// When the actual entries have the same key as test entries, we
281293
// override the actual entries with the test entries (here we
282294
// just don't add the actual entries if they're already present).
283-
for (auto const& liveEntry : live)
295+
for (LedgerEntry const& liveEntry : live)
284296
{
285297
if (std::find_if(mTestLiveEntries.begin(),
286298
mTestLiveEntries.end(),
@@ -295,12 +307,15 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges(
295307
}
296308

297309
// Use the testing values.
310+
auto testInitRefs = toRefs(mTestInitEntries);
311+
auto testLiveRefs = toRefs(mTestLiveEntries);
312+
auto testDeadRefs = toRefs(mTestDeadEntries);
298313
mApplyState.addAnyContractsToModuleCache(lh.ledgerVersion,
299-
mTestInitEntries);
314+
testInitRefs);
300315
mApplyState.addAnyContractsToModuleCache(lh.ledgerVersion,
301-
mTestLiveEntries);
302-
mApp.getBucketManager().addLiveBatch(
303-
mApp, lh, mTestInitEntries, mTestLiveEntries, mTestDeadEntries);
316+
testLiveRefs);
317+
mApp.getBucketManager().addLiveBatch(mApp, lh, testInitRefs,
318+
testLiveRefs, testDeadRefs);
304319

305320
// Load the final Soroban config AFTER addLiveBatch so that test
306321
// entries (e.g. stateArchivalSettings with eviction iterator) are
@@ -325,8 +340,7 @@ LedgerManagerForBucketTests::finalizeLedgerTxnChanges(
325340
}
326341

327342
mApplyState.updateInMemorySorobanState(
328-
mTestInitEntries, mTestLiveEntries, mTestDeadEntries, lh,
329-
finalSorobanConfig);
343+
testInitRefs, testLiveRefs, testDeadRefs, lh, finalSorobanConfig);
330344

331345
mUseTestEntries = false;
332346
mAlsoAddActualEntries = false;

src/bucket/test/BucketTestUtils.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include "ledger/LedgerEntryRefs.h"
78
#include "test/TestUtils.h"
89
#include "xdr/Stellar-ledger.h"
910

@@ -12,6 +13,10 @@ namespace stellar
1213
namespace BucketTestUtils
1314
{
1415

16+
void addLiveBatchAndUpdateSnapshot(Application& app, LedgerHeader header,
17+
LedgerEntryRefs initEntries,
18+
LedgerEntryRefs liveEntries,
19+
LedgerKeyRefs deadEntries);
1520
void addLiveBatchAndUpdateSnapshot(Application& app, LedgerHeader header,
1621
std::vector<LedgerEntry> const& initEntries,
1722
std::vector<LedgerEntry> const& liveEntries,
@@ -124,5 +129,6 @@ class BucketTestApplication : public TestApplication
124129
return std::make_unique<LedgerManagerForBucketTests>(*this);
125130
}
126131
};
127-
}
128-
}
132+
133+
} // namespace BucketTestUtils
134+
} // namespace stellar

src/invariant/test/BucketListIsConsistentWithDatabaseTests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ struct BucketListGenerator
111111
mLiveKeys.insert(LedgerEntryKey(le));
112112
}
113113

114-
std::vector<LedgerEntry> initEntries, liveEntries;
115-
std::vector<LedgerKey> deadEntries;
114+
LedgerEntryRefVec initEntries, liveEntries;
115+
LedgerKeyRefVec deadEntries;
116116
auto header = ltx.loadHeader().current();
117-
ltx.getAllEntries(initEntries, liveEntries, deadEntries);
117+
ltx.sealAndBorrowAllEntries(initEntries, liveEntries, deadEntries);
118118
BucketTestUtils::addLiveBatchAndUpdateSnapshot(
119119
*app, header, initEntries, liveEntries, deadEntries);
120120
ltx.commit();

0 commit comments

Comments
 (0)