Skip to content

Commit 259277f

Browse files
owend74daverigby
authored andcommitted
MB-29333: Only decay frequency for items that are eligible for eviction
The original patch did not address the issue correctly. (see Change-Id: I95d328238176a97d7ac9e80c268c639bbb647953) It only decrements the frequency count if the item is NOT elgible for eviction (from the max u8int_t value of 255). This behaviour is wrong for two reasons: 1) We want to decrement the frequency count of items that were eligible for eviction except that their frequency count is above the threshold. 2) We don't want to decrement the frequency count that are not eligible for eviction (regardless of what their frequency count is). This patch corrects the behaviour to only decrement the fequency count of items that are eligible for eviction except that their frequency count is above the threshold. The original associated test was also found to not exercise the newly added code. This has been addressed and an additional test added. Change-Id: I508964dbf9aefd8055ee7e654caca10d1450aa5b Reviewed-on: http://review.couchbase.org/93338 Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent 7cdbc46 commit 259277f

File tree

4 files changed

+79
-20
lines changed

4 files changed

+79
-20
lines changed

engines/ep/src/item_pager.cc

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ PagingVisitor::PagingVisitor(KVBucket& s,
5555
bool _isEphemeral)
5656
: VBucketVisitor(vbFilter),
5757
ejected(0),
58+
freqCounterThreshold(0),
5859
store(s),
5960
stats(st),
6061
percent(pcnt),
@@ -67,8 +68,7 @@ PagingVisitor::PagingVisitor(KVBucket& s,
6768
wasHighMemoryUsage(s.isMemoryUsageTooHigh()),
6869
taskStart(ProcessClock::now()),
6970
pager_phase(phase),
70-
isEphemeral(_isEphemeral),
71-
freqCounterThreshold(0) {
71+
isEphemeral(_isEphemeral) {
7272
}
7373

7474
bool PagingVisitor::visit(const HashTable::HashBucketLock& lh,
@@ -113,7 +113,6 @@ PagingVisitor::PagingVisitor(KVBucket& s,
113113
* add it to the histogram we want to use the original value.
114114
*/
115115
auto storedValueFreqCounter = v.getFreqCounterValue();
116-
bool evicted = true;
117116

118117
if (storedValueFreqCounter <= freqCounterThreshold) {
119118
/*
@@ -129,28 +128,28 @@ PagingVisitor::PagingVisitor(KVBucket& s,
129128
*/
130129
if (!doEviction(lh, &v)) {
131130
storedValueFreqCounter = std::numeric_limits<uint8_t>::max();
132-
evicted = false;
133131
}
134132
} else {
135133
// If the storedValue is NOT eligible for eviction then
136134
// we want to add the maximum value (255).
137135
if (!currentBucket->eligibleToPageOut(lh, v)) {
138136
storedValueFreqCounter = std::numeric_limits<uint8_t>::max();
139-
evicted = false;
137+
} else {
138+
/*
139+
* MB-29333 - For items that we have visited and did not
140+
* evict just because their frequency counter was too high,
141+
* the frequency counter must be decayed by 1 to
142+
* ensure that they will get evicted if repeatedly
143+
* visited (and assuming their frequency counter is not
144+
* incremented in between visits of the item pager).
145+
*/
146+
if (storedValueFreqCounter > 0) {
147+
v.setFreqCounterValue(storedValueFreqCounter - 1);
148+
}
140149
}
141150
}
142151
itemEviction.addValueToFreqHistogram(storedValueFreqCounter);
143152

144-
/*
145-
* MB-29333 - For items that we have visited but not evicted the
146-
* storedValue frequency counter must be decayed by 1 to ensure
147-
* that an item will get evicted if it is repeatedly visited (and
148-
* is eligible to be evicted).
149-
*/
150-
if (!evicted && storedValueFreqCounter > 0) {
151-
v.setFreqCounterValue(storedValueFreqCounter - 1);
152-
}
153-
154153
// Whilst we are learning it is worth always updating the
155154
// threshold. We also want to update the threshold at periodic
156155
// intervals.

engines/ep/src/paging_visitor.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ class PagingVisitor : public VBucketVisitor, public HashTableVisitor {
9393
// The current vbucket that the eviction algorithm is operating on.
9494
VBucketPtr currentBucket;
9595

96+
// The frequency counter threshold that is used to determine whether we
97+
// should evict items from the hash table.
98+
uint16_t freqCounterThreshold;
99+
96100
private:
97101
// Removes checkpoints that are both closed and unreferenced, thereby
98102
// freeing the associated memory.
@@ -121,8 +125,4 @@ class PagingVisitor : public VBucketVisitor, public HashTableVisitor {
121125
// Indicates whether the vbucket we are visiting is from an ephemeral
122126
// bucket.
123127
bool isEphemeral;
124-
125-
// The frequency counter threshold that is used to determine whether we
126-
// should evict items from the hash table.
127-
uint16_t freqCounterThreshold;
128128
};

engines/ep/tests/mock/mock_paging_visitor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ class MockPagingVisitor : public PagingVisitor {
5656
return ejected;
5757
}
5858

59+
void setFreqCounterThreshold(uint16_t threshold) {
60+
freqCounterThreshold = threshold;
61+
}
62+
5963
void setCurrentBucket(VBucketPtr _currentBucket) {
6064
currentBucket = _currentBucket;
6165
}

engines/ep/tests/module_tests/item_pager_test.cc

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,14 +598,70 @@ TEST_P(STItemPagerTest, decayByOne) {
598598
}
599599
int iterationCount = 0;
600600
while ((pv->getEjected() == 0) &&
601-
iterationCount < ItemEviction::initialFreqCount) {
601+
iterationCount <= ItemEviction::initialFreqCount) {
602+
pv->setFreqCounterThreshold(0);
602603
VBucketPtr vb = store->getVBucket(vbid);
603604
vb->ht.visit(*pv);
604605
iterationCount++;
605606
}
606607
EXPECT_EQ(1, pv->getEjected());
607608
}
608609

610+
/**
611+
* MB-29333: Test that if a vbucket contains a single document with an
612+
* execution frequency of ItemEviction::initialFreqCount, but the document
613+
* is not eligible for eviction (due to being replica in ephemeral case and
614+
* not flushed in the persistent case) check that its frequency count is not
615+
* decremented.
616+
*/
617+
TEST_P(STItemPagerTest, doNotDecayIfCannotEvict) {
618+
const std::string value(512, 'x'); // 512B value to use for documents.
619+
auto key = makeStoredDocKey("xxx_0");
620+
auto item = make_item(vbid, key, value, time_t(0));
621+
storeItem(item);
622+
623+
std::shared_ptr<std::atomic<bool>> available;
624+
std::atomic<item_pager_phase> phase{ACTIVE_AND_PENDING_ONLY};
625+
bool isEphemeral = std::get<0>(GetParam()) == "ephemeral";
626+
std::unique_ptr<MockPagingVisitor> pv =
627+
std::make_unique<MockPagingVisitor>(*engine->getKVBucket(),
628+
engine->getEpStats(),
629+
10.0,
630+
available,
631+
ITEM_PAGER,
632+
false,
633+
0.5,
634+
VBucketFilter(),
635+
&phase,
636+
isEphemeral);
637+
638+
pv->setCurrentBucket(engine->getKVBucket()->getVBucket(vbid));
639+
store->setVBucketState(vbid, vbucket_state_replica, false);
640+
for (int ii = 0; ii <= ItemEviction::initialFreqCount; ii++) {
641+
pv->setFreqCounterThreshold(0);
642+
pv->getItemEviction().reset();
643+
VBucketPtr vb = store->getVBucket(vbid);
644+
vb->ht.visit(*pv);
645+
}
646+
647+
// Now make the document eligible for eviction.
648+
store->setVBucketState(vbid, vbucket_state_active, false);
649+
if (std::get<0>(GetParam()) == "persistent") {
650+
getEPBucket().flushVBucket(vbid);
651+
}
652+
653+
// Check still not be able to evict, because the frequency count is still
654+
// at ItemEviction::initialFreqCount
655+
pv->setFreqCounterThreshold(0);
656+
pv->getItemEviction().reset();
657+
VBucketPtr vb = store->getVBucket(vbid);
658+
vb->ht.visit(*pv);
659+
auto initialFreqCount = ItemEviction::initialFreqCount;
660+
EXPECT_EQ(initialFreqCount, pv->getItemEviction().getFreqThreshold(100.0));
661+
EXPECT_EQ(0, pv->getEjected());
662+
663+
}
664+
609665
/**
610666
* Test fixture for Ephemeral-only item pager tests.
611667
*/

0 commit comments

Comments
 (0)