Skip to content

Commit f193405

Browse files
committed
MB-40531: [BP] Change order of vbuckets paging visitor visits
Backport of http://review.couchbase.org/c/kv_engine/+/137340/ 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/+/137666 Well-Formed: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]> Tested-by: James Harrison <[email protected]>
1 parent c320484 commit f193405

File tree

12 files changed

+176
-7
lines changed

12 files changed

+176
-7
lines changed

engines/ep/src/ep_vb.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,14 @@ bool EPVBucket::eligibleToPageOut(const HashTable::HashBucketLock& lh,
448448
return v.eligibleForEviction(eviction);
449449
}
450450

451+
size_t EPVBucket::getPageableMemUsage() {
452+
if (eviction == FULL_EVICTION) {
453+
return ht.getItemMemory();
454+
} else {
455+
return ht.getItemMemory() - ht.getMetadataMemory();
456+
}
457+
}
458+
451459
void EPVBucket::queueBackfillItem(queued_item& qi,
452460
const GenerateBySeqno generateBySeqno) {
453461
LockHolder lh(backfill.mutex);

engines/ep/src/ep_vb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class EPVBucket : public VBucket {
9696
bool eligibleToPageOut(const HashTable::HashBucketLock& lh,
9797
const StoredValue& v) const override;
9898

99+
size_t getPageableMemUsage() override;
100+
99101
bool areDeletedItemsAlwaysResident() const override;
100102

101103
void addStats(bool details, ADD_STAT add_stat, const void* c) override;

engines/ep/src/ephemeral_vb.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ bool EphemeralVBucket::eligibleToPageOut(const HashTable::HashBucketLock& lh,
128128
return true;
129129
}
130130

131+
size_t EphemeralVBucket::getPageableMemUsage() {
132+
if (getState() == vbucket_state_replica) {
133+
// Ephemeral buckets are not backed by disk - nothing can be evicted
134+
// from a replica as deleting from replicas would cause inconsistency
135+
// with the active. When the active vb evicts items deletions will be
136+
// streamed to the replica.
137+
return 0;
138+
}
139+
return ht.getItemMemory();
140+
}
141+
131142
bool EphemeralVBucket::areDeletedItemsAlwaysResident() const {
132143
// Ephemeral buckets do keep all deleted items resident in memory.
133144
// (We have nowhere else to store them, given there is no disk).

engines/ep/src/ephemeral_vb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class EphemeralVBucket : public VBucket {
102102
bool eligibleToPageOut(const HashTable::HashBucketLock& lh,
103103
const StoredValue& v) const override;
104104

105+
size_t getPageableMemUsage() override;
106+
105107
bool areDeletedItemsAlwaysResident() const override;
106108

107109
void addStats(bool details, ADD_STAT add_stat, const void* c) override;

engines/ep/src/fakes/fake_executorpool.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,10 @@ class CheckedExecutor : public ExecutorThread {
184184
run();
185185
}
186186

187-
void runCurrentTask() {
187+
std::string runCurrentTask() {
188+
auto name = getTaskName();
188189
run();
190+
return name;
189191
}
190192

191193
ProcessClock::time_point completeCurrentTask() {

engines/ep/src/item_pager.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,33 @@ PagingVisitor::PagingVisitor(KVBucket& s,
300300
}
301301
}
302302

303+
std::function<bool(const VBucket::id_type&, const VBucket::id_type&)>
304+
PagingVisitor::getVBucketComparator() const {
305+
// Get the pageable mem used of each vb _once_ and cache it.
306+
// Fetching these values repeatedly in the comparator could cause issues
307+
// as the values can change _during_ a given sort call.
308+
309+
std::map<VBucket::id_type, size_t> pageableMemUsed;
310+
311+
for (const auto& vbid : store.getVBuckets().getBuckets()) {
312+
auto vb = store.getVBucket(vbid);
313+
pageableMemUsed[vbid] = vb ? vb->getPageableMemUsage() : 0;
314+
}
315+
316+
// Capture initializers are C++14
317+
return [pageableMemUsed, this](const VBucket::id_type& a,
318+
const VBucket::id_type& b) mutable {
319+
auto vbA = store.getVBucket(a);
320+
auto vbB = store.getVBucket(b);
321+
bool aReplica = vbA && vbA->getState() == vbucket_state_replica;
322+
bool bReplica = vbB && vbB->getState() == vbucket_state_replica;
323+
// sort replicas before all other vbucket states, then sort by
324+
// pageableMemUsed
325+
return std::make_pair(aReplica, pageableMemUsed[a]) >
326+
std::make_pair(bReplica, pageableMemUsed[b]);
327+
};
328+
}
329+
303330
void PagingVisitor::update() {
304331
store.deleteExpiredItems(expired, ExpireBy::Pager);
305332

engines/ep/src/paging_visitor.h

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

8383
void complete() override;
8484

85+
std::function<bool(const uint16_t&, const uint16_t&)> getVBucketComparator()
86+
const override;
87+
8588
/**
8689
* Get the number of items ejected during the visit.
8790
*/

engines/ep/src/vbucket.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,15 @@ class VBucket : public std::enable_shared_from_this<VBucket> {
10321032
virtual bool eligibleToPageOut(const HashTable::HashBucketLock& lh,
10331033
const StoredValue& v) const = 0;
10341034

1035+
/**
1036+
* Check how much memory could be reclaimed if every resident item
1037+
* were evicted.
1038+
* Note, the real amount of reclaimable memory will be lower than this,
1039+
* as certain items (prepares, dirty items, system events,
1040+
* deletes for ephemeral) cannot be evicted.
1041+
*/
1042+
virtual size_t getPageableMemUsage() = 0;
1043+
10351044
/**
10361045
* Add an item in the store
10371046
*

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

2222
#include <string>
2323

24+
#include <gmock/gmock.h>
25+
2426
/**
2527
* Mock PagingVisitor class. Provide access to ItemEviction data structure.
2628
*/
@@ -52,6 +54,11 @@ class MockPagingVisitor : public PagingVisitor {
5254
agePercentage,
5355
freqCounterAgeThreshold,
5456
evictionPolicy) {
57+
using namespace testing;
58+
ON_CALL(*this, visitBucket(_))
59+
.WillByDefault(Invoke([this](VBucketPtr& vb) {
60+
PagingVisitor::visitBucket(vb);
61+
}));
5562
}
5663

5764
ItemEviction& getItemEviction() {
@@ -69,4 +76,6 @@ class MockPagingVisitor : public PagingVisitor {
6976
void setCurrentBucket(VBucketPtr _currentBucket) {
7077
currentBucket = _currentBucket;
7178
}
79+
80+
MOCK_METHOD1(visitBucket, void(VBucketPtr&));
7281
};

engines/ep/tests/module_tests/evp_store_single_threaded_test.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ ProcessClock::time_point SingleThreadedKVBucketTest::runNextTask(
5454
return executor.completeCurrentTask();
5555
}
5656

57-
ProcessClock::time_point SingleThreadedKVBucketTest::runNextTask(TaskQueue& taskQ) {
57+
std::pair<ProcessClock::time_point, std::string>
58+
SingleThreadedKVBucketTest::runNextTask(TaskQueue& taskQ) {
5859
CheckedExecutor executor(task_executor, taskQ);
5960

6061
// Run the task
61-
executor.runCurrentTask();
62-
return executor.completeCurrentTask();
62+
auto name = executor.runCurrentTask();
63+
return {executor.completeCurrentTask(), name};
6364
}
6465

6566
void SingleThreadedKVBucketTest::SetUp() {

0 commit comments

Comments
 (0)