Skip to content

Commit fc6843e

Browse files
committed
Changed random eviction cache to accounts only
1 parent 6c8c90b commit fc6843e

17 files changed

+202
-60
lines changed

docs/stellar-core_example.cfg

+6-7
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,12 @@ MAX_DEX_TX_OPERATIONS_IN_TX_SET = 0
235235
# 0, indiviudal index is always used. Default page size 16 kb.
236236
BUCKETLIST_DB_INDEX_PAGE_SIZE_EXPONENT = 14
237237

238-
# BUCKETLIST_DB_CACHED_PERCENT (Integer) default 10
239-
# Percentage of entries cached by BucketListDB when Bucket size is larger
240-
# than BUCKETLIST_DB_INDEX_CUTOFF. Note that this value does not impact
241-
# Buckets smaller than BUCKETLIST_DB_INDEX_CUTOFF, as they are always
242-
# completely held in memory. Roughly speaking, RAM usage for BucketList
243-
# cache == BucketListSize * (BUCKETLIST_DB_CACHED_PERCENT / 100).
244-
BUCKETLIST_DB_CACHED_PERCENT = 10
238+
# BUCKETLIST_DB_MEMORY_FOR_CACHING (Integer) default 3000
239+
# Memory used for caching entries by BucketListDB when Bucket size is
240+
# larger than BUCKETLIST_DB_INDEX_CUTOFF, in MB. Note that this value does
241+
# not impact Buckets smaller than BUCKETLIST_DB_INDEX_CUTOFF, as they are
242+
# always completely held in memory. If set to 0, caching is disabled.
243+
BUCKETLIST_DB_MEMORY_FOR_CACHING = 3000
245244

246245
# BUCKETLIST_DB_INDEX_CUTOFF (Integer) default 100
247246
# Size, in MB, determining whether a bucket should have an individual

src/bucket/BucketIndexUtils.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ namespace stellar
1919
std::streamoff
2020
getPageSizeFromConfig(Config const& cfg)
2121
{
22-
if (cfg.BUCKETLIST_DB_INDEX_PAGE_SIZE_EXPONENT == 0 ||
23-
cfg.BUCKETLIST_DB_CACHED_PERCENT == 100)
22+
if (cfg.BUCKETLIST_DB_INDEX_PAGE_SIZE_EXPONENT == 0)
2423
{
2524
return 0;
2625
}

src/bucket/BucketIndexUtils.h

+4
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,17 @@ std::streamoff getPageSizeFromConfig(Config const& cfg);
9898
// Builds index for given bucketfile. This is expensive (> 20 seconds
9999
// for the largest buckets) and should only be called once. Constructs a
100100
// DiskIndex or InMemoryIndex depending on config and Bucket size.
101+
// Note: Constructor does not initialize the cache for live bucket indexes,
102+
// as this must be done when the Bucket is being added to the BucketList
101103
template <class BucketT>
102104
std::unique_ptr<typename BucketT::IndexT const>
103105
createIndex(BucketManager& bm, std::filesystem::path const& filename,
104106
Hash const& hash, asio::io_context& ctx, SHA256* hasher);
105107

106108
// Loads index from given file. If file does not exist or if saved
107109
// index does not have expected version or pageSize, return null
110+
// Note: Constructor does not initialize the cache for live bucket indexes,
111+
// as this must be done when the Bucket is being added to the BucketList
108112
template <class BucketT>
109113
std::unique_ptr<typename BucketT::IndexT const>
110114
loadIndex(BucketManager const& bm, std::filesystem::path const& filename,

src/bucket/BucketManager.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,9 @@ BucketManager::assumeState(HistoryArchiveState const& has,
12911291
mLiveBucketList->getLevel(i).setNext(nextFuture);
12921292
}
12931293

1294+
mLiveBucketList->maybeInitializeCaches(
1295+
mConfig.maxAccountsInBucketListCache());
1296+
12941297
if (restartMerges)
12951298
{
12961299
mLiveBucketList->restartMerges(mApp, maxProtocolVersion,

src/bucket/BucketUtils.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,18 @@ BucketEntryCounters::numEntries() const
348348
return num;
349349
}
350350

351+
BucketEntryCounters::BucketEntryCounters()
352+
{
353+
for (uint32_t type =
354+
static_cast<uint32_t>(LedgerEntryTypeAndDurability::ACCOUNT);
355+
type < static_cast<uint32_t>(LedgerEntryTypeAndDurability::NUM_TYPES);
356+
++type)
357+
{
358+
entryTypeCounts[static_cast<LedgerEntryTypeAndDurability>(type)] = 0;
359+
entryTypeSizes[static_cast<LedgerEntryTypeAndDurability>(type)] = 0;
360+
}
361+
}
362+
351363
template void
352364
BucketEntryCounters::count<LiveBucket>(LiveBucket::EntryT const& be);
353365
template void BucketEntryCounters::count<HotArchiveBucket>(

src/bucket/BucketUtils.h

+2
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ struct BucketEntryCounters
208208
{
209209
ar(entryTypeCounts, entryTypeSizes);
210210
}
211+
212+
BucketEntryCounters();
211213
};
212214

213215
template <class BucketT>

src/bucket/LiveBucket.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,15 @@ LiveBucket::getBucketVersion() const
431431
return it.getMetadata().ledgerVersion;
432432
}
433433

434+
void
435+
LiveBucket::maybeInitializeCache(size_t bucketListTotalAccounts,
436+
size_t maxBucketListAccountsToCache) const
437+
{
438+
releaseAssert(mIndex);
439+
mIndex->maybeInitializeCache(bucketListTotalAccounts,
440+
maxBucketListAccountsToCache);
441+
}
442+
434443
BucketEntryCounters const&
435444
LiveBucket::getBucketEntryCounters() const
436445
{

src/bucket/LiveBucket.h

+3
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ class LiveBucket : public BucketBase<LiveBucket, LiveBucketIndex>,
120120

121121
uint32_t getBucketVersion() const;
122122

123+
void maybeInitializeCache(size_t bucketListTotalAccounts,
124+
size_t maxBucketListAccountsToCache) const;
125+
123126
BucketEntryCounters const& getBucketEntryCounters() const;
124127

125128
friend class LiveBucketSnapshot;

src/bucket/LiveBucketIndex.cpp

+68-16
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "bucket/LiveBucketIndex.h"
66
#include "bucket/BucketIndexUtils.h"
77
#include "bucket/BucketManager.h"
8+
#include "bucket/BucketUtils.h"
89
#include "bucket/DiskIndex.h"
910
#include "util/Fs.h"
1011
#include "util/GlobalChecks.h"
@@ -65,21 +66,6 @@ LiveBucketIndex::LiveBucketIndex(BucketManager& bm,
6566
pageSize, filename);
6667
mDiskIndex = std::make_unique<DiskIndex<LiveBucket>>(
6768
bm, filename, pageSize, hash, ctx, hasher);
68-
69-
auto percentCached = bm.getConfig().BUCKETLIST_DB_CACHED_PERCENT;
70-
if (percentCached > 0)
71-
{
72-
auto const& counters = mDiskIndex->getBucketEntryCounters();
73-
auto cacheSize = (counters.numEntries() * percentCached) / 100;
74-
75-
// Minimum cache size of 100 if we are going to cache a non-zero
76-
// number of entries
77-
// We don't want to reserve here, since caches only live as long as
78-
// the lifetime of the Bucket and fill relatively slowly
79-
mCache = std::make_unique<CacheT>(std::max<size_t>(cacheSize, 100),
80-
/*separatePRNG=*/false,
81-
/*reserve=*/false);
82-
}
8369
}
8470
}
8571

@@ -95,6 +81,50 @@ LiveBucketIndex::LiveBucketIndex(BucketManager const& bm, Archive& ar,
9581
releaseAssertOrThrow(pageSize != 0);
9682
}
9783

84+
void
85+
LiveBucketIndex::maybeInitializeCache(size_t bucketListTotalAccounts,
86+
size_t maxBucketListAccountsToCache) const
87+
{
88+
// Everything is already in memory, no need for a redundant cache.
89+
if (mInMemoryIndex)
90+
{
91+
return;
92+
}
93+
94+
// Cache is already initialized
95+
if (std::shared_lock<std::shared_mutex> lock(mCacheMutex); mCache)
96+
{
97+
return;
98+
}
99+
100+
releaseAssert(mDiskIndex);
101+
102+
auto accountsInThisBucket =
103+
mDiskIndex->getBucketEntryCounters().entryTypeCounts.at(
104+
LedgerEntryTypeAndDurability::ACCOUNT);
105+
106+
// Nothing to cache
107+
if (accountsInThisBucket == 0)
108+
{
109+
return;
110+
}
111+
112+
std::unique_lock<std::shared_mutex> lock(mCacheMutex);
113+
if (bucketListTotalAccounts < maxBucketListAccountsToCache)
114+
{
115+
// We can cache the entire bucket
116+
mCache = std::make_unique<CacheT>(bucketListTotalAccounts);
117+
}
118+
else
119+
{
120+
double percentAccountsInBucket =
121+
static_cast<double>(accountsInThisBucket) / bucketListTotalAccounts;
122+
auto cacheSize = static_cast<size_t>(bucketListTotalAccounts *
123+
percentAccountsInBucket);
124+
mCache = std::make_unique<CacheT>(cacheSize);
125+
}
126+
}
127+
98128
LiveBucketIndex::IterT
99129
LiveBucketIndex::begin() const
100130
{
@@ -133,7 +163,7 @@ LiveBucketIndex::markBloomMiss() const
133163
std::shared_ptr<BucketEntry const>
134164
LiveBucketIndex::getCachedEntry(LedgerKey const& k) const
135165
{
136-
if (shouldUseCache())
166+
if (shouldUseCache() && isCachedType(k))
137167
{
138168
std::shared_lock<std::shared_mutex> lock(mCacheMutex);
139169
auto cachePtr = mCache->maybeGet(k);
@@ -262,6 +292,24 @@ LiveBucketIndex::getBucketEntryCounters() const
262292
return mInMemoryIndex->getBucketEntryCounters();
263293
}
264294

295+
bool
296+
LiveBucketIndex::shouldUseCache() const
297+
{
298+
if (mDiskIndex)
299+
{
300+
std::shared_lock<std::shared_mutex> lock(mCacheMutex);
301+
return mCache != nullptr;
302+
}
303+
304+
return false;
305+
}
306+
307+
bool
308+
LiveBucketIndex::isCachedType(LedgerKey const& lk)
309+
{
310+
return lk.type() == ACCOUNT;
311+
}
312+
265313
void
266314
LiveBucketIndex::maybeAddToCache(
267315
std::shared_ptr<BucketEntry const> const& entry) const
@@ -270,6 +318,10 @@ LiveBucketIndex::maybeAddToCache(
270318
{
271319
releaseAssertOrThrow(entry);
272320
auto k = getBucketLedgerKey(*entry);
321+
if (!isCachedType(k))
322+
{
323+
return;
324+
}
273325

274326
// If we are adding an entry to the cache, we must have missed it
275327
// earlier.

src/bucket/LiveBucketIndex.h

+19-8
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ class LiveBucketIndex : public NonMovableOrCopyable
5959
RandomEvictionCache<LedgerKey, std::shared_ptr<BucketEntry const>>;
6060

6161
private:
62-
std::unique_ptr<DiskIndex<LiveBucket> const> mDiskIndex;
63-
std::unique_ptr<InMemoryIndex const> mInMemoryIndex;
64-
std::unique_ptr<CacheT> mCache;
62+
std::unique_ptr<DiskIndex<LiveBucket> const> mDiskIndex{};
63+
std::unique_ptr<InMemoryIndex const> mInMemoryIndex{};
6564

6665
// The indexes themselves are thread safe, as they are immutable after
6766
// construction. The cache is not, all accesses must first acquire this
6867
// mutex.
68+
mutable std::unique_ptr<CacheT> mCache{};
6969
mutable std::shared_mutex mCacheMutex;
7070

7171
medida::Meter& mCacheHitMeter;
@@ -83,11 +83,12 @@ class LiveBucketIndex : public NonMovableOrCopyable
8383
return std::get<InMemoryIndex::IterT>(iter);
8484
}
8585

86-
bool
87-
shouldUseCache() const
88-
{
89-
return mDiskIndex && mCache;
90-
}
86+
bool shouldUseCache() const;
87+
88+
// Because we already have an LTX level cache at apply time, we only need to
89+
// cache entry types that are used during TX validation and flooding.
90+
// Currently, that is only ACCOUNT.
91+
static bool isCachedType(LedgerKey const& lk);
9192

9293
// Returns nullptr if cache is not enabled or entry not found
9394
std::shared_ptr<BucketEntry const> getCachedEntry(LedgerKey const& k) const;
@@ -97,14 +98,24 @@ class LiveBucketIndex : public NonMovableOrCopyable
9798
inline static const uint32_t BUCKET_INDEX_VERSION = 5;
9899

99100
// Constructor for creating new index from Bucketfile
101+
// Note: Constructor does not initialize the cache
100102
LiveBucketIndex(BucketManager& bm, std::filesystem::path const& filename,
101103
Hash const& hash, asio::io_context& ctx, SHA256* hasher);
102104

103105
// Constructor for loading pre-existing index from disk
106+
// Note: Constructor does not initialize the cache
104107
template <class Archive>
105108
LiveBucketIndex(BucketManager const& bm, Archive& ar,
106109
std::streamoff pageSize);
107110

111+
// Initializes the random eviction cache if it has not already been
112+
// initialized. The random eviction cache itself has an entry limit, but we
113+
// expose a memory limit in the validator config. To account for this, we
114+
// check what percentage of the total number of accounts are in this bucket
115+
// and allocate space accordingly.
116+
void maybeInitializeCache(size_t bucketListTotalAccounts,
117+
size_t maxBucketListAccountsToCache) const;
118+
108119
// Returns true if LedgerEntryType not supported by BucketListDB
109120
static bool typeNotSupported(LedgerEntryType t);
110121

src/bucket/LiveBucketList.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ LiveBucketList::addBatch(Application& app, uint32_t currLedger,
2121
ZoneScoped;
2222
addBatchInternal(app, currLedger, currLedgerProtocol, initEntries,
2323
liveEntries, deadEntries);
24+
25+
// Initialize caches for any new buckets we might have added
26+
maybeInitializeCaches(app.getConfig().maxAccountsInBucketListCache());
2427
}
2528

2629
BucketEntryCounters
@@ -41,6 +44,30 @@ LiveBucketList::sumBucketEntryCounters() const
4144
return counters;
4245
}
4346

47+
void
48+
LiveBucketList::maybeInitializeCaches(size_t maxBucketListAccountsToCache) const
49+
{
50+
auto blCounters = sumBucketEntryCounters();
51+
size_t totalAccounts =
52+
blCounters.entryTypeCounts.at(LedgerEntryTypeAndDurability::ACCOUNT);
53+
for (uint32_t i = 0; i < kNumLevels; ++i)
54+
{
55+
auto curr = mLevels[i].getCurr();
56+
if (!curr->isEmpty())
57+
{
58+
curr->maybeInitializeCache(totalAccounts,
59+
maxBucketListAccountsToCache);
60+
}
61+
62+
auto snap = mLevels[i].getSnap();
63+
if (!snap->isEmpty())
64+
{
65+
snap->maybeInitializeCache(totalAccounts,
66+
maxBucketListAccountsToCache);
67+
}
68+
}
69+
}
70+
4471
void
4572
LiveBucketList::updateStartingEvictionIterator(EvictionIterator& iter,
4673
uint32_t firstScanLevel,

src/bucket/LiveBucketList.h

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ class LiveBucketList : public BucketListBase<LiveBucket>
5050
std::vector<LedgerKey> const& deadEntries);
5151

5252
BucketEntryCounters sumBucketEntryCounters() const;
53+
54+
// Initializes any uninitialized caches in the BucketIndex. Should be called
55+
// after every time buckets may have changed in the LiveBucketList.
56+
void maybeInitializeCaches(size_t maxBucketListAccountsToCache) const;
5357
};
5458

5559
}

src/bucket/readme.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ lookup speed and memory overhead. The following configuration flags control thes
9797
on startup. Defaults to true, should only be set to false for testing purposes.
9898
Validators do not currently support persisted indexes. If NODE_IS_VALIDATOR=true,
9999
this value is ignored and indexes are never persisted.
100-
- `BUCKETLIST_DB_CACHED_PERCENT`
101-
- Percentage of entries cached by BucketListDB when Bucket size is larger
102-
than `BUCKETLIST_DB_INDEX_CUTOFF`. Note that this value does not impact
100+
- `BUCKETLIST_DB_MEMORY_FOR_CACHING`
101+
- Memory used for caching entries by BucketListDB when Bucket size is larger
102+
than `BUCKETLIST_DB_INDEX_CUTOFF`, in MB. Note that this value does not impact
103103
Buckets smaller than `BUCKETLIST_DB_INDEX_CUTOFF`, as they are always
104104
completely held in memory.

0 commit comments

Comments
 (0)