Skip to content

Commit a9f1b74

Browse files
committed
MB-29928: Switch fragmentation scoring/weighting to use high-water mark
The justification for this change is based on current KV behaviour and some input from a few 'random' real life log snapshots. 1) Current behaviour of KV's item-pager is to try and keep memory below the high-water mark, at times of memory pressure it will attempt to move memory to the low-water mark. The defragmenter cannot affect mem_used, but it can affect the bucket's resident memory. Here we allow the defragmenter's sleep to become most aggressive when the resident memory is close/above the high-water mark. 2) It's not uncommon for users to have their mem_used between the low and high water marks. This change means that the defragmenter won't just be at it's most 'aggressive' for the users that are happily between the two, only when they approach the high-water mark will the scoring/weighting become higher. Change-Id: Id942db14fe76ca6e18265fb66e379c18eecb6fab Change-Id: I1f8d9098edf38dbd780b746a85da6a1177850482 Reviewed-on: http://review.couchbase.org/c/kv_engine/+/156643 Reviewed-by: Dave Rigby <[email protected]> Tested-by: Build Bot <[email protected]>
1 parent 2d66a5e commit a9f1b74

File tree

3 files changed

+31
-19
lines changed

3 files changed

+31
-19
lines changed

engines/ep/src/defragmenter.cc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,12 @@ DefragmentVisitor& DefragmenterTask::getDefragVisitor() {
242242

243243
float DefragmenterTask::getScoredFragmentation(
244244
const cb::FragmentationStats& fragStats) const {
245-
auto lowWater = stats.mem_low_wat.load();
246-
auto rss = fragStats.getResidentBytes() > lowWater
247-
? lowWater
245+
const auto highWater = stats.mem_high_wat.load();
246+
auto rss = fragStats.getResidentBytes() > highWater
247+
? highWater
248248
: fragStats.getResidentBytes();
249-
return fragStats.getFragmentationRatio() * (double(rss) / double(lowWater));
249+
return fragStats.getFragmentationRatio() *
250+
(double(rss) / double(highWater));
250251
}
251252

252253
DefragmenterTask::SleepTimeAndRunState DefragmenterTask::calculateSleepLinear(

engines/ep/src/defragmenter.h

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,29 @@ class DefragmenterTask : public GlobalTask {
154154
virtual float stepPid(float pv);
155155

156156
/**
157-
* The auto controller modes don't use raw fragmentation, but a 'scored'
158-
* value using low-water mark and RSS. This function gets that value from
159-
* fragStats.
160-
* The current implementation of this returns the following example values
161-
* Consider a raw fragmentation of 23% where allocated=500 and rss=650.
162-
* Then with a low-water mark of n this function returns (score):
157+
* The auto controller modes do not use raw fragmentation, but a 'scored'
158+
* value that is weighted by the ratio of RSS/high-water mark.
159+
*
160+
* This is done because bucket fragmentation alone is not a great guide for
161+
* how aggressive we would want to defragment the hash-table. For example
162+
* an empty or very low utilised bucket, can easily reach high fragmentation
163+
* percentages, yet running the defragmenter at high frequency has little
164+
* effect. Using the RSS/high-water mark ratio means helps to guide our
165+
* defragmentation sleep changes only when:
166+
* 1) There are items to defragment
167+
* 2) When the bucket is actually using a good chunk of its quota
168+
*
169+
* The current implementation of this returns the following example values:
170+
* Here we consider allocated:=500 and rss:=650, this gives a fragmentation
171+
* ratio of 0.23.
172+
*
173+
* Then with a high-water mark of n this function returns:
163174
* n | score
164-
* 600 | 23 (note this case, rss exceeds n).
165-
* 1000 | 14.95
166-
* 2000 | 7.4
167-
* 3000 | 4.98
168-
* 5000 | 2.99
175+
* 600 | 0.23 (note this case, rss exceeds n, we fix weight to 1).
176+
* 1000 | 0.1495
177+
* 2000 | 0.074
178+
* 3000 | 0.0498
179+
* 5000 | 0.0299
169180
*
170181
* @return the scored fragmentation
171182
*/

engines/ep/tests/module_tests/defragmenter_test.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,8 @@ std::chrono::milliseconds MockDefragmenterTask::MockDefragmenterTaskClock::step;
443443
// Test that as the PID runs (and we move time forwards) that it assists in the
444444
// recalculation of sleep time, reducing whilst fragmentation is above threshold
445445
TEST_F(DefragmenterTaskTest, autoCalculateSleep_PID) {
446-
// set lww to some low number
447-
engine->getEpStats().setLowWaterMark(750);
446+
// set hwm to some low number
447+
engine->getEpStats().setHighWaterMark(750);
448448
auto task = std::make_unique<MockDefragmenterTask>(engine.get(),
449449
engine->getEpStats());
450450

@@ -507,8 +507,8 @@ TEST_F(DefragmenterTaskTest, autoCalculateSleep_PID) {
507507
}
508508

509509
TEST_F(DefragmenterTaskTest, autoCalculateSleep) {
510-
// set lww to some low number
511-
engine->getEpStats().setLowWaterMark(750);
510+
// set hwm to some low number
511+
engine->getEpStats().setHighWaterMark(750);
512512
auto task = std::make_unique<MockDefragmenterTask>(engine.get(),
513513
engine->getEpStats());
514514
auto& conf = engine->getConfiguration();

0 commit comments

Comments
 (0)