Skip to content

Commit 789033c

Browse files
committed
MB-40531: Change order of vbuckets paging visitor visits
Testing after http://review.couchbase.org/c/kv_engine/+/133197 uncovered unexpected increases in cache miss ratio for a working set which fits within the quota comfortably. This occurred after some time, despite the workload reading the same keys in a loop. Investigation discovered a large disparity in per-vbucket residency (see MB for info). Vbuckets with a lower vbid could be <20% resident while other vbuckets remained fully resident. The fully resident vbs logically contain resident cold data which has never been evicted despite never being accessed. This patch changes the order the paging visitor visits vbuckets. They are now visited: * replicas before other vbucket states * from highest pageable memory to lowest (within a given vb state) Testing demonstrates a lower standard deviation of the distribution of per-vbucket residency ratios. Change-Id: Ia8a0fa368821518a25659da03852076b70ac478a Reviewed-on: http://review.couchbase.org/c/kv_engine/+/136628 Tested-by: James Harrison <[email protected]> Well-Formed: Build Bot <[email protected]> Reviewed-by: Paolo Cocchi <[email protected]>
1 parent dd3aaf9 commit 789033c

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed

engines/ep/src/paging_visitor.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
#include <phosphor/phosphor.h>
4040
#include <memory>
41+
#include <utility>
4142

4243
static const size_t MAX_PERSISTENCE_QUEUE_SIZE = 1000000;
4344

@@ -331,6 +332,32 @@ void PagingVisitor::complete() {
331332
}
332333
}
333334

335+
std::function<bool(const Vbid&, const Vbid&)>
336+
PagingVisitor::getVBucketComparator() const {
337+
// Get the pageable mem used of each vb _once_ and cache it.
338+
// Fetching these values repeatedly in the comparator could cause issues as
339+
// the values can change _during_ a given sort call.
340+
341+
std::map<Vbid, size_t> pageableMemUsed;
342+
343+
for (const auto& vbid : store.getVBuckets().getBuckets()) {
344+
auto vb = store.getVBucket(vbid);
345+
pageableMemUsed[vbid] = vb ? vb->getPageableMemUsage() : 0;
346+
}
347+
348+
return [pageableMemUsed = std::move(pageableMemUsed), &store = store](
349+
const Vbid& a, const Vbid& b) mutable {
350+
auto vbA = store.getVBucket(a);
351+
auto vbB = store.getVBucket(b);
352+
bool aReplica = vbA && vbA->getState() == vbucket_state_replica;
353+
bool bReplica = vbB && vbB->getState() == vbucket_state_replica;
354+
// sort replicas before all other vbucket states, then sort by
355+
// pageableMemUsed
356+
return std::make_pair(aReplica, pageableMemUsed[a]) >
357+
std::make_pair(bReplica, pageableMemUsed[b]);
358+
};
359+
}
360+
334361
// Removes checkpoints that are both closed and unreferenced, thereby
335362
// freeing the associated memory.
336363
// @param vb The vbucket whose eligible checkpoints are removed from.

engines/ep/src/paging_visitor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ class PagingVisitor : public PausableVBucketVisitor, public HashTableVisitor {
8484

8585
void complete() override;
8686

87+
std::function<bool(const Vbid&, const Vbid&)> getVBucketComparator()
88+
const override;
89+
8790
/**
8891
* Override the setUpHashBucketVisit method so that we can acquire a
8992
* Collections::VB::Manifest::ReadHandle. Required if we evict any items in

engines/ep/tests/mock/mock_paging_visitor.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include "paging_visitor.h"
2222
#include <string>
2323

24+
#include <folly/portability/GMock.h>
25+
2426
/**
2527
* Mock PagingVisitor class. Provide access to ItemEviction data structure.
2628
*/
@@ -44,6 +46,11 @@ class MockPagingVisitor : public PagingVisitor {
4446
vbFilter,
4547
agePercentage,
4648
freqCounterAgeThreshold) {
49+
using namespace testing;
50+
ON_CALL(*this, visitBucket(_))
51+
.WillByDefault(Invoke([this](const VBucketPtr& vb) {
52+
PagingVisitor::visitBucket(vb);
53+
}));
4754
}
4855

4956
ItemEviction& getItemEviction() {
@@ -61,4 +68,6 @@ class MockPagingVisitor : public PagingVisitor {
6168
void setCurrentBucket(VBucketPtr _currentBucket) {
6269
currentBucket = _currentBucket;
6370
}
71+
72+
MOCK_METHOD1(visitBucket, void(const VBucketPtr&));
6473
};

engines/ep/tests/module_tests/item_pager_test.cc

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,98 @@ TEST_P(STItemPagerTest, ActiveEvictedIfReplicaEvictionInsufficient) {
10001000
EXPECT_LT(replicaRR, 5);
10011001
}
10021002

1003+
MATCHER_P(VBPtrVbidMatcher,
1004+
vbid,
1005+
"Check the provided VBucket pointer points to a vbucket with the "
1006+
"given vbid") {
1007+
return arg && arg->getId() == vbid;
1008+
}
1009+
1010+
TEST_P(STItemPagerTest, ItemPagerEvictionOrder) {
1011+
// MB-40531: Test that the paging visitor visits vbuckets ordered by:
1012+
// * replica vbuckets before active/pending
1013+
// * highest pageableMemUsage first
1014+
1015+
std::vector<Vbid> activeVBs = {Vbid(0), Vbid(1)};
1016+
std::vector<Vbid> replicaVBs = {Vbid(2), Vbid(3)};
1017+
std::vector<Vbid> allVBs = {Vbid(0), Vbid(1), Vbid(2), Vbid(3)};
1018+
1019+
// Set all vbs, including replicaVBs, as active initially (so we can
1020+
// populate them easily).
1021+
for (const auto& vbid : allVBs) {
1022+
setVBucketStateAndRunPersistTask(vbid, vbucket_state_active);
1023+
}
1024+
1025+
// populate the vbuckets with different numbers of equal sized items.
1026+
// Pageable mem usage should be proportional to the number of items.
1027+
// The visitor should therefore visit them in the commented order
1028+
for (const auto& setup : std::vector<std::pair<Vbid, int>>{
1029+
{activeVBs[0], 30}, // 4th
1030+
{activeVBs[1], 40}, // 3rd
1031+
{replicaVBs[0], 20}, // 1st
1032+
{replicaVBs[1], 10}, // 2nd
1033+
}) {
1034+
Vbid vbid;
1035+
int itemCount;
1036+
std::tie(vbid, itemCount) = setup;
1037+
for (int i = 0; i < itemCount; ++i) {
1038+
auto key = makeStoredDocKey("key-" + std::to_string(i));
1039+
auto item = make_item(vbid, key, std::string(100, 'x'));
1040+
ASSERT_EQ(ENGINE_SUCCESS, storeItem(item));
1041+
}
1042+
}
1043+
1044+
// flush all vbs
1045+
for (const auto& vbid : allVBs) {
1046+
flushDirectlyIfPersistent(Vbid(vbid));
1047+
}
1048+
1049+
// flip the replica vbs to the correct state
1050+
setVBucketStateAndRunPersistTask(replicaVBs[0], vbucket_state_replica);
1051+
setVBucketStateAndRunPersistTask(replicaVBs[1], vbucket_state_replica);
1052+
1053+
auto& stats = engine->getEpStats();
1054+
auto available = std::make_shared<std::atomic<bool>>();
1055+
auto& config = engine->getConfiguration();
1056+
1057+
auto pv = std::make_unique<MockPagingVisitor>(
1058+
*store,
1059+
stats,
1060+
EvictionRatios{1.0 /*active*/, 1.0 /*replica*/},
1061+
available,
1062+
ITEM_PAGER,
1063+
false,
1064+
VBucketFilter(ephemeral() ? activeVBs : allVBs),
1065+
config.getItemEvictionAgePercentage(),
1066+
config.getItemEvictionFreqCounterAgeThreshold());
1067+
1068+
using namespace testing;
1069+
InSequence s;
1070+
1071+
// set up expectations _before_ moving the pv into the VBCBAdaptor
1072+
if (!ephemeral()) {
1073+
// ephemeral cannot evict from replicas, so the pager visitor won't
1074+
// visit them at all.
1075+
EXPECT_CALL(*pv, visitBucket(VBPtrVbidMatcher(replicaVBs[0])));
1076+
EXPECT_CALL(*pv, visitBucket(VBPtrVbidMatcher(replicaVBs[1])));
1077+
}
1078+
EXPECT_CALL(*pv, visitBucket(VBPtrVbidMatcher(activeVBs[1])));
1079+
EXPECT_CALL(*pv, visitBucket(VBPtrVbidMatcher(activeVBs[0])));
1080+
1081+
auto label = "Item pager";
1082+
auto taskid = TaskId::ItemPagerVisitor;
1083+
VBCBAdaptor task(store,
1084+
taskid,
1085+
std::move(pv),
1086+
label,
1087+
/*shutdown*/ false);
1088+
1089+
// call the run method repeatedly until it returns false, indicating the
1090+
// visitor is definitely done, rather than paused
1091+
while (task.run())
1092+
;
1093+
}
1094+
10031095
/**
10041096
* Test fixture for Ephemeral-only item pager tests.
10051097
*/

0 commit comments

Comments
 (0)