Skip to content

Commit e6c1f3b

Browse files
Fixed counter bug in in-memory bucket indexes (#4668)
# Description Resolves #4667 # Checklist - [x] Reviewed the [contributing](https://github.com/stellar/stellar-core/blob/master/CONTRIBUTING.md#submitting-changes) document - [x] Rebased on top of master (no merge commits) - [x] Ran `clang-format` v8.0.0 (via `make format` or the Visual Studio extension) - [x] Compiles - [x] Ran all tests - [ ] If change impacts performance, include supporting evidence per the [performance document](https://github.com/stellar/stellar-core/blob/master/performance-eval/performance-eval.md)
2 parents df3edf9 + fe70a81 commit e6c1f3b

8 files changed

+141
-24
lines changed

docs/metrics.md

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ bucketlistDB-<X>.bulk.loads | meter | number of entries Buck
4444
bucketlistDB-live.bulk.inflationWinners | timer | time to load inflation winners
4545
bucketlistDB-live.bulk.poolshareTrustlines | timer | time to load poolshare trustlines by accountID and assetID
4646
bucketlistDB-live.bulk.prefetch | timer | time to prefetch
47+
bucketlistDB-live.bulk.eviction | timer | time to load for eviction scan
48+
bucketlistDB-live.bulk.query | timer | time to load for query server
4749
bucketlistDB-<X>.point.<Y> | timer | time to load single entry of type <Y> on BucketList <X> (live/hotArchive)
4850
bucketlistDB-cache.hit | meter | number of cache hits on Live BucketList Disk random eviction cache
4951
bucketlistDB-cache.miss | meter | number of cache misses on Live BucketList Disk random eviction cache

src/bucket/BucketSnapshot.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ LiveBucketSnapshot::scanForEviction(
237237

238238
auto processQueue = [&]() {
239239
auto loadResult = populateLoadedEntries(
240-
keysToSearch, bl.loadKeysWithLimits(keysToSearch, nullptr));
240+
keysToSearch,
241+
bl.loadKeysWithLimits(keysToSearch, "eviction", nullptr));
241242
for (auto& e : maybeEvictQueue)
242243
{
243244
// If TTL entry has not yet been deleted

src/bucket/InMemoryIndex.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ InMemoryIndex::InMemoryIndex(BucketManager const& bm,
7979
continue;
8080
}
8181

82+
mCounters.template count<LiveBucket>(be);
83+
8284
// Populate assetPoolIDMap
8385
LedgerKey lk = getBucketLedgerKey(be);
8486
if (be.type() == INITENTRY)

src/bucket/SearchableBucketList.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,9 @@ SearchableLiveBucketListSnapshot::loadInflationWinners(size_t maxWinners,
204204
std::vector<LedgerEntry>
205205
SearchableLiveBucketListSnapshot::loadKeysWithLimits(
206206
std::set<LedgerKey, LedgerEntryIdCmp> const& inKeys,
207-
LedgerKeyMeter* lkMeter) const
207+
std::string const& label, LedgerKeyMeter* lkMeter) const
208208
{
209-
auto timer = getBulkLoadTimer("prefetch", inKeys.size()).TimeScope();
209+
auto timer = getBulkLoadTimer(label, inKeys.size()).TimeScope();
210210
auto op = loadKeysInternal(inKeys, lkMeter, std::nullopt);
211211
releaseAssertOrThrow(op);
212212
return std::move(*op);

src/bucket/SearchableBucketList.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class SearchableLiveBucketListSnapshot
2828

2929
std::vector<LedgerEntry>
3030
loadKeysWithLimits(std::set<LedgerKey, LedgerEntryIdCmp> const& inKeys,
31-
LedgerKeyMeter* lkMeter) const;
31+
std::string const& label, LedgerKeyMeter* lkMeter) const;
3232

3333
EvictionResultCandidates scanForEviction(
3434
uint32_t ledgerSeq, EvictionCounters& counters, EvictionIterator iter,

src/bucket/test/BucketIndexTests.cpp

+129-17
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
// This file contains tests for the BucketIndex and higher-level operations
66
// concerning key-value lookup based on the BucketList.
77

8+
#include "bucket/BucketInputIterator.h"
89
#include "bucket/BucketManager.h"
910
#include "bucket/BucketSnapshotManager.h"
1011
#include "bucket/BucketUtils.h"
1112
#include "bucket/LiveBucket.h"
1213
#include "bucket/LiveBucketList.h"
1314
#include "bucket/test/BucketTestUtils.h"
15+
#include "ledger/LedgerTypeUtils.h"
1416
#include "ledger/test/LedgerTestUtils.h"
1517
#include "lib/catch.hpp"
1618
#include "main/Application.h"
@@ -308,7 +310,7 @@ class BucketIndexTest
308310

309311
// Test bulk load lookup
310312
auto loadResult =
311-
searchableBL->loadKeysWithLimits(mKeysToSearch, nullptr);
313+
searchableBL->loadKeysWithLimits(mKeysToSearch, "test", nullptr);
312314
validateResults(mTestEntries, loadResult);
313315

314316
if (expectedHitRate)
@@ -356,8 +358,8 @@ class BucketIndexTest
356358
mKeysToSearch.size());
357359

358360
// Run bulk lookup again
359-
auto loadResult2 =
360-
searchableBL->loadKeysWithLimits(mKeysToSearch, nullptr);
361+
auto loadResult2 = searchableBL->loadKeysWithLimits(
362+
mKeysToSearch, "test", nullptr);
361363
validateResults(mTestEntries, loadResult2);
362364

363365
checkHitRate(expectedHitRate, startingHitCount, startingMissCount,
@@ -401,7 +403,7 @@ class BucketIndexTest
401403
}
402404

403405
auto blLoad =
404-
searchableBL->loadKeysWithLimits(searchSubset, nullptr);
406+
searchableBL->loadKeysWithLimits(searchSubset, "test", nullptr);
405407
validateResults(testEntriesSubset, blLoad);
406408
}
407409
}
@@ -420,8 +422,8 @@ class BucketIndexTest
420422
LedgerKeySet invalidKeys(keysNotInBL.begin(), keysNotInBL.end());
421423

422424
// Test bulk load
423-
REQUIRE(searchableBL->loadKeysWithLimits(invalidKeys, nullptr).size() ==
424-
0);
425+
REQUIRE(searchableBL->loadKeysWithLimits(invalidKeys, "test", nullptr)
426+
.size() == 0);
425427

426428
// Test individual load
427429
for (auto const& key : invalidKeys)
@@ -745,6 +747,124 @@ TEST_CASE("do not load outdated values", "[bucket][bucketindex]")
745747
testAllIndexTypes(f);
746748
}
747749

750+
TEST_CASE("bucket entry counters", "[bucket][bucketindex]")
751+
{
752+
// Initialize global counter for all of bucketlist
753+
std::map<LedgerEntryTypeAndDurability, size_t> totalEntryTypeCounts;
754+
std::map<LedgerEntryTypeAndDurability, size_t> totalEntryTypeSizes;
755+
for (uint32_t type =
756+
static_cast<uint32_t>(LedgerEntryTypeAndDurability::ACCOUNT);
757+
type < static_cast<uint32_t>(LedgerEntryTypeAndDurability::NUM_TYPES);
758+
++type)
759+
{
760+
totalEntryTypeCounts[static_cast<LedgerEntryTypeAndDurability>(type)] =
761+
0;
762+
totalEntryTypeSizes[static_cast<LedgerEntryTypeAndDurability>(type)] =
763+
0;
764+
}
765+
766+
auto checkBucket = [&](auto bucket) {
767+
if (bucket->isEmpty())
768+
{
769+
return;
770+
}
771+
772+
// Local counter for each bucket
773+
std::map<LedgerEntryTypeAndDurability, size_t> entryTypeCounts;
774+
std::map<LedgerEntryTypeAndDurability, size_t> entryTypeSizes;
775+
for (uint32_t type =
776+
static_cast<uint32_t>(LedgerEntryTypeAndDurability::ACCOUNT);
777+
type <
778+
static_cast<uint32_t>(LedgerEntryTypeAndDurability::NUM_TYPES);
779+
++type)
780+
{
781+
entryTypeCounts[static_cast<LedgerEntryTypeAndDurability>(type)] =
782+
0;
783+
entryTypeSizes[static_cast<LedgerEntryTypeAndDurability>(type)] = 0;
784+
}
785+
786+
for (LiveBucketInputIterator iter(bucket); iter; ++iter)
787+
{
788+
auto be = *iter;
789+
LedgerKey lk = getBucketLedgerKey(be);
790+
791+
auto count = [&](LedgerEntryTypeAndDurability type) {
792+
entryTypeCounts[type]++;
793+
entryTypeSizes[type] += xdr::xdr_size(be);
794+
totalEntryTypeCounts[type]++;
795+
totalEntryTypeSizes[type] += xdr::xdr_size(be);
796+
};
797+
798+
switch (lk.type())
799+
{
800+
case ACCOUNT:
801+
count(LedgerEntryTypeAndDurability::ACCOUNT);
802+
break;
803+
case TRUSTLINE:
804+
count(LedgerEntryTypeAndDurability::TRUSTLINE);
805+
break;
806+
case OFFER:
807+
count(LedgerEntryTypeAndDurability::OFFER);
808+
break;
809+
case DATA:
810+
count(LedgerEntryTypeAndDurability::DATA);
811+
break;
812+
case CLAIMABLE_BALANCE:
813+
count(LedgerEntryTypeAndDurability::CLAIMABLE_BALANCE);
814+
break;
815+
case LIQUIDITY_POOL:
816+
count(LedgerEntryTypeAndDurability::LIQUIDITY_POOL);
817+
break;
818+
case CONTRACT_DATA:
819+
if (isPersistentEntry(lk))
820+
{
821+
count(
822+
LedgerEntryTypeAndDurability::PERSISTENT_CONTRACT_DATA);
823+
}
824+
else
825+
{
826+
count(
827+
LedgerEntryTypeAndDurability::TEMPORARY_CONTRACT_DATA);
828+
}
829+
break;
830+
case CONTRACT_CODE:
831+
count(LedgerEntryTypeAndDurability::CONTRACT_CODE);
832+
break;
833+
case CONFIG_SETTING:
834+
count(LedgerEntryTypeAndDurability::CONFIG_SETTING);
835+
break;
836+
case TTL:
837+
count(LedgerEntryTypeAndDurability::TTL);
838+
break;
839+
}
840+
}
841+
842+
auto const& indexCounters =
843+
bucket->getIndexForTesting().getBucketEntryCounters();
844+
REQUIRE(indexCounters.entryTypeCounts == entryTypeCounts);
845+
REQUIRE(indexCounters.entryTypeSizes == entryTypeSizes);
846+
};
847+
848+
auto f = [&](Config& cfg) {
849+
auto test = BucketIndexTest(cfg);
850+
test.buildMultiVersionTest();
851+
852+
for (auto i = 0; i < LiveBucketList::kNumLevels; ++i)
853+
{
854+
auto level = test.getBM().getLiveBucketList().getLevel(i);
855+
checkBucket(level.getCurr());
856+
checkBucket(level.getSnap());
857+
}
858+
859+
auto summedCounters =
860+
test.getBM().getLiveBucketList().sumBucketEntryCounters();
861+
REQUIRE(summedCounters.entryTypeCounts == totalEntryTypeCounts);
862+
REQUIRE(summedCounters.entryTypeSizes == totalEntryTypeSizes);
863+
};
864+
865+
testAllIndexTypes(f);
866+
}
867+
748868
TEST_CASE("load from historical snapshots", "[bucket][bucketindex]")
749869
{
750870
auto f = [&](Config& cfg) {
@@ -839,6 +959,9 @@ TEST_CASE("serialize bucket indexes", "[bucket][bucketindex]")
839959

840960
auto& inMemoryIndex = b->getIndexForTesting();
841961
REQUIRE((inMemoryIndex == *onDiskIndex));
962+
auto inMemoryCounters = inMemoryIndex.getBucketEntryCounters();
963+
auto onDiskCounters = onDiskIndex->getBucketEntryCounters();
964+
REQUIRE(inMemoryCounters == onDiskCounters);
842965
}
843966

844967
// Restart app with different config to test that indexes created with
@@ -862,17 +985,6 @@ TEST_CASE("serialize bucket indexes", "[bucket][bucketindex]")
862985

863986
auto& inMemoryIndex = b->getIndexForTesting();
864987
REQUIRE(inMemoryIndex.getPageSize() == (1UL << 10));
865-
auto inMemoryCoutners = inMemoryIndex.getBucketEntryCounters();
866-
// Ensure the inMemoryIndex has some non-zero counters.
867-
REQUIRE(!inMemoryCoutners.entryTypeCounts.empty());
868-
REQUIRE(!inMemoryCoutners.entryTypeSizes.empty());
869-
bool allZero = true;
870-
for (auto const& [k, v] : inMemoryCoutners.entryTypeCounts)
871-
{
872-
allZero = allZero && (v == 0);
873-
allZero = allZero && (inMemoryCoutners.entryTypeSizes.at(k) == 0);
874-
}
875-
REQUIRE(!allZero);
876988

877989
// Check if on-disk index rewritten with correct config params
878990
auto indexFilename = test.getBM().bucketIndexFilename(bucketHash);

src/ledger/LedgerTxn.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -2950,7 +2950,7 @@ LedgerTxnRoot::Impl::prefetchInternal(UnorderedSet<LedgerKey> const& keys,
29502950
insertIfNotLoaded(keysToSearch, key);
29512951
}
29522952
auto blLoad = getSearchableLiveBucketListSnapshot().loadKeysWithLimits(
2953-
keysToSearch, lkMeter);
2953+
keysToSearch, "prefetch", lkMeter);
29542954
cacheResult(populateLoadedEntries(keysToSearch, blLoad, lkMeter));
29552955

29562956
return total;

src/main/QueryServer.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ QueryServer::getLedgerEntryRaw(std::string const& params,
169169
// Otherwise default to current ledger
170170
else
171171
{
172-
loadedKeys =
173-
bl.loadKeysWithLimits(orderedKeys, /*lkMeter=*/nullptr);
172+
loadedKeys = bl.loadKeysWithLimits(orderedKeys, "query",
173+
/*lkMeter=*/nullptr);
174174
root["ledgerSeq"] = bl.getLedgerSeq();
175175
}
176176

0 commit comments

Comments
 (0)