Skip to content

Commit 4b3d201

Browse files
owend74daverigby
authored andcommitted
MB-33918: Fix cursor dropping trigger issue
There was an issue where we were doing an integer divide in the calculation used to determine whether cursor dropping should be triggered and also in the calculation used to determine the amount of memory to recover. This resulted in cursor dropping being triggered too aggressively and thinking it need to recover more memory than memory it could. Change-Id: I4571b51739a3e778fcf5463be361db5e0fe92da9 Reviewed-on: http://review.couchbase.org/108266 Well-Formed: Build Bot <[email protected]> Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent e613ef5 commit 4b3d201

File tree

3 files changed

+108
-39
lines changed

3 files changed

+108
-39
lines changed

engines/ep/src/checkpoint_remover.cc

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include <phosphor/phosphor.h>
2929
#include <platform/make_unique.h>
3030

31+
#include <utility>
32+
3133
/**
3234
* Remove all the closed unreferenced checkpoints for each vbucket.
3335
*/
@@ -90,7 +92,8 @@ class CheckpointVisitor : public VBucketVisitor {
9092
std::atomic<bool> &stateFinalizer;
9193
};
9294

93-
void ClosedUnrefCheckpointRemoverTask::cursorDroppingIfNeeded(void) {
95+
std::pair<bool, size_t>
96+
ClosedUnrefCheckpointRemoverTask::isCursorDroppingNeeded() const {
9497
/**
9598
* Cursor dropping will commence if one of the following conditions is met:
9699
* 1. if the total memory used is greater than the upper threshold which is
@@ -112,8 +115,8 @@ void ClosedUnrefCheckpointRemoverTask::cursorDroppingIfNeeded(void) {
112115
.getActiveVBucketsTotalCheckpointMemoryUsage();
113116

114117
const auto chkptMemLimit =
115-
bucketQuota *
116-
(config.getCursorDroppingCheckpointMemUpperMark() / 100);
118+
(bucketQuota * config.getCursorDroppingCheckpointMemUpperMark()) /
119+
100;
117120

118121
const bool hitCheckpointMemoryThreshold =
119122
activeVBChkptMemSize >= chkptMemLimit;
@@ -140,8 +143,9 @@ void ClosedUnrefCheckpointRemoverTask::cursorDroppingIfNeeded(void) {
140143
// memory usage threshold.
141144
amountOfMemoryToClear =
142145
stats.getEstimatedTotalMemoryUsed() -
143-
(bucketQuota *
144-
(config.getCursorDroppingCheckpointMemLowerMark() / 100));
146+
((bucketQuota *
147+
config.getCursorDroppingCheckpointMemLowerMark()) /
148+
100);
145149
LOG(EXTENSION_LOG_INFO,
146150
"Triggering cursor dropping as checkpoint_memory (%lu MB) "
147151
"exceeds cursor_dropping_checkpoint_mem_upper_mark (%lu%%, "
@@ -163,43 +167,49 @@ void ClosedUnrefCheckpointRemoverTask::cursorDroppingIfNeeded(void) {
163167
toMB(stats.cursorDroppingUThreshold.load()),
164168
toMB(amountOfMemoryToClear));
165169
}
170+
// Cursor dropping is required.
171+
return std::make_pair(true, amountOfMemoryToClear);
172+
}
173+
// Cursor dropping is not required.
174+
return std::make_pair(false, 0);
175+
}
166176

167-
size_t memoryCleared = 0;
168-
KVBucketIface* kvBucket = engine->getKVBucket();
169-
// Get a list of active vbuckets sorted by memory usage
170-
// of their respective checkpoint managers.
171-
auto vbuckets =
172-
kvBucket->getVBuckets().getActiveVBucketsSortedByChkMgrMem();
173-
for (const auto& it: vbuckets) {
174-
if (memoryCleared < amountOfMemoryToClear) {
175-
uint16_t vbid = it.first;
176-
VBucketPtr vb = kvBucket->getVBucket(vbid);
177-
if (vb) {
178-
// Get a list of cursors that can be dropped from the
179-
// vbucket's checkpoint manager, so as to unreference
180-
// an estimated number of checkpoints.
181-
std::vector<std::string> cursors =
182-
vb->checkpointManager->getListOfCursorsToDrop();
183-
std::vector<std::string>::iterator itr = cursors.begin();
184-
for (; itr != cursors.end(); ++itr) {
185-
if (memoryCleared < amountOfMemoryToClear) {
186-
if (engine->getDcpConnMap().handleSlowStream(vbid,
187-
*itr))
188-
{
189-
auto memoryFreed =
190-
vb->getChkMgrMemUsageOfUnrefCheckpoints();
191-
++stats.cursorsDropped;
192-
stats.cursorMemoryFreed += memoryFreed;
193-
memoryCleared += memoryFreed;
194-
}
195-
} else {
196-
break;
177+
void ClosedUnrefCheckpointRemoverTask::attemptCursorDropping(
178+
size_t amountOfMemoryToClear) {
179+
size_t memoryCleared = 0;
180+
KVBucketIface* kvBucket = engine->getKVBucket();
181+
// Get a list of active vbuckets sorted by memory usage
182+
// of their respective checkpoint managers.
183+
auto vbuckets =
184+
kvBucket->getVBuckets().getActiveVBucketsSortedByChkMgrMem();
185+
for (const auto& it : vbuckets) {
186+
if (memoryCleared < amountOfMemoryToClear) {
187+
uint16_t vbid = it.first;
188+
VBucketPtr vb = kvBucket->getVBucket(vbid);
189+
if (vb) {
190+
// Get a list of cursors that can be dropped from the
191+
// vbucket's checkpoint manager, so as to unreference
192+
// an estimated number of checkpoints.
193+
std::vector<std::string> cursors =
194+
vb->checkpointManager->getListOfCursorsToDrop();
195+
std::vector<std::string>::iterator itr = cursors.begin();
196+
for (; itr != cursors.end(); ++itr) {
197+
if (memoryCleared < amountOfMemoryToClear) {
198+
if (engine->getDcpConnMap().handleSlowStream(vbid,
199+
*itr)) {
200+
auto memoryFreed =
201+
vb->getChkMgrMemUsageOfUnrefCheckpoints();
202+
++stats.cursorsDropped;
203+
stats.cursorMemoryFreed += memoryFreed;
204+
memoryCleared += memoryFreed;
197205
}
206+
} else {
207+
break;
198208
}
199209
}
200-
} else { // memoryCleared >= amountOfMemoryToClear
201-
break;
202210
}
211+
} else { // memoryCleared >= amountOfMemoryToClear
212+
break;
203213
}
204214
}
205215
}
@@ -208,7 +218,13 @@ bool ClosedUnrefCheckpointRemoverTask::run(void) {
208218
TRACE_EVENT0("ep-engine/task", "ClosedUnrefCheckpointRemoverTask");
209219
bool inverse = true;
210220
if (available.compare_exchange_strong(inverse, false)) {
211-
cursorDroppingIfNeeded();
221+
bool shouldTriggerCursorDropping{false};
222+
size_t amountOMemoryToClear{0};
223+
std::tie(shouldTriggerCursorDropping, amountOMemoryToClear) =
224+
isCursorDroppingNeeded();
225+
if (shouldTriggerCursorDropping) {
226+
attemptCursorDropping(amountOMemoryToClear);
227+
}
212228
KVBucketIface* kvBucket = engine->getKVBucket();
213229
auto pv =
214230
std::make_unique<CheckpointVisitor>(kvBucket, stats, available);

engines/ep/src/checkpoint_remover.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class ClosedUnrefCheckpointRemoverTask : public GlobalTask {
4242
GlobalTask(e, TaskId::ClosedUnrefCheckpointRemoverTask, interval, false),
4343
engine(e), stats(st), sleepTime(interval), available(true) {}
4444

45-
void cursorDroppingIfNeeded(void);
45+
void attemptCursorDropping(size_t amountOfMemoryToClear);
4646

4747
bool run(void);
4848

@@ -56,6 +56,8 @@ class ClosedUnrefCheckpointRemoverTask : public GlobalTask {
5656
return std::chrono::milliseconds(25);
5757
}
5858

59+
std::pair<bool, size_t> isCursorDroppingNeeded() const;
60+
5961
private:
6062
EventuallyPersistentEngine *engine;
6163
EPStats &stats;

engines/ep/tests/module_tests/checkpoint_remover_test.cc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,55 @@ TEST_F(CheckpointRemoverEPTest, CursorDropMemoryFreed) {
207207

208208
// There should only be the one checkpoint cursor now for persistence
209209
ASSERT_EQ(1, checkpointManager->getNumOfCursors());
210+
}
211+
212+
// Test that we correctly determine whether to trigger cursor dropping.
213+
TEST_F(CheckpointRemoverEPTest, cursorDroppingTriggerTest) {
214+
setVBucketStateAndRunPersistTask(vbid, vbucket_state_active);
215+
auto& config = engine->getConfiguration();
216+
const auto& task = std::make_shared<ClosedUnrefCheckpointRemoverTask>(
217+
engine.get(),
218+
engine->getEpStats(),
219+
engine->getConfiguration().getChkRemoverStime());
220+
221+
bool shouldTriggerCursorDropping{false};
222+
size_t amountOfMemoryToClear{0};
223+
224+
/*
225+
* With a large max size (with no other changes) we should
226+
* conclude the cursor dropping is not required.
227+
*/
228+
config.setMaxSize(10240);
229+
230+
std::tie(shouldTriggerCursorDropping, amountOfMemoryToClear) =
231+
task->isCursorDroppingNeeded();
232+
EXPECT_FALSE(shouldTriggerCursorDropping);
233+
EXPECT_EQ(0, amountOfMemoryToClear);
234+
235+
/*
236+
* Trigger first condition for cursor dropping:
237+
* the total memory used is greater than the upper threshold which is
238+
* a percentage of the quota, specified by cursor_dropping_upper_mark
239+
*/
240+
config.setMaxSize(1024);
241+
242+
std::tie(shouldTriggerCursorDropping, amountOfMemoryToClear) =
243+
task->isCursorDroppingNeeded();
244+
EXPECT_TRUE(shouldTriggerCursorDropping);
245+
EXPECT_LT(0, amountOfMemoryToClear);
246+
247+
/*
248+
* Trigger second condition for cursor dropping:
249+
* the overall checkpoint memory usage goes above a certain % of the
250+
* bucket quota, specified by cursor_dropping_checkpoint_mem_upper_mark and
251+
* the checkpoint memory usage is above the memory low watermark.
252+
*/
253+
config.setMaxSize(10240);
254+
engine->getEpStats().mem_low_wat.store(1);
255+
config.setCursorDroppingCheckpointMemUpperMark(1);
256+
257+
std::tie(shouldTriggerCursorDropping, amountOfMemoryToClear) =
258+
task->isCursorDroppingNeeded();
259+
EXPECT_TRUE(shouldTriggerCursorDropping);
260+
EXPECT_LT(0, amountOfMemoryToClear);
210261
}

0 commit comments

Comments
 (0)