Skip to content

Commit 1fbba81

Browse files
jameseh96daverigby
authored andcommitted
MB-48716: Make expiry pager config change update task waketime
Recent changes for MB-41403 which ensured the ExpiredItemPager task is not recreated for every config change. However, in doing so, changes to the task sleep time configuration were no longer immediately applied; instead the task had to sleep until it's existing wake time, to then snooze for the newly configured period. Resolve this by calling into ExecutorPool to ensure the wake time used by the pool is directly updated when the config changes. Change-Id: I0620e9884549da631a419064403f75a753835886 Reviewed-on: http://review.couchbase.org/c/kv_engine/+/163008 Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent 584d3a1 commit 1fbba81

File tree

4 files changed

+83
-30
lines changed

4 files changed

+83
-30
lines changed

engines/ep/src/item_pager.cc

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -272,29 +272,52 @@ ExpiredItemPager::ExpiredItemPager(EventuallyPersistentEngine* e,
272272
auto cfg = config.wlock();
273273
cfg->sleepTime = std::chrono::seconds(stime);
274274
cfg->initialRunTime = taskTime;
275-
updateWakeTimeFromCfg(*cfg);
275+
snooze(calculateWakeTimeFromCfg(*cfg).count());
276276
}
277277

278278
void ExpiredItemPager::updateSleepTime(std::chrono::seconds sleepTime) {
279279
auto cfg = config.wlock();
280280
cfg->sleepTime = sleepTime;
281-
updateWakeTimeFromCfg(*cfg);
281+
ExecutorPool::get()->snooze(getId(),
282+
calculateWakeTimeFromCfg(*cfg).count());
282283
}
283284
void ExpiredItemPager::updateInitialRunTime(ssize_t initialRunTime) {
284285
auto cfg = config.wlock();
285286
cfg->initialRunTime = initialRunTime;
286-
updateWakeTimeFromCfg(*cfg);
287+
ExecutorPool::get()->snooze(getId(),
288+
calculateWakeTimeFromCfg(*cfg).count());
287289
}
288290

289291
std::chrono::seconds ExpiredItemPager::getSleepTime() const {
290292
return config.rlock()->sleepTime;
291293
}
292294

295+
bool ExpiredItemPager::enable() {
296+
auto cfg = config.wlock();
297+
if (cfg->enabled) {
298+
return false;
299+
}
300+
cfg->enabled = true;
301+
snooze(calculateWakeTimeFromCfg(*cfg).count());
302+
ExecutorPool::get()->schedule(shared_from_this());
303+
return true;
304+
}
305+
306+
bool ExpiredItemPager::disable() {
307+
auto cfg = config.wlock();
308+
if (!cfg->enabled) {
309+
return false;
310+
}
311+
cfg->enabled = false;
312+
ExecutorPool::get()->cancel(getId());
313+
return true;
314+
}
315+
293316
bool ExpiredItemPager::isEnabled() const {
294317
return config.rlock()->enabled;
295318
}
296319

297-
void ExpiredItemPager::updateWakeTimeFromCfg(
320+
std::chrono::seconds ExpiredItemPager::calculateWakeTimeFromCfg(
298321
const ExpiredItemPager::Config& cfg) {
299322
auto initialSleep = double(cfg.sleepTime.count());
300323
if (cfg.initialRunTime != -1) {
@@ -325,8 +348,9 @@ void ExpiredItemPager::updateWakeTimeFromCfg(
325348

326349
initialSleep = difftime(mktime(&timeTarget), mktime(&timeNow));
327350
}
328-
snooze(initialSleep);
329351
updateExpPagerTime(initialSleep);
352+
using namespace std::chrono;
353+
return duration_cast<seconds>(duration<double>(initialSleep));
330354
}
331355

332356
bool ExpiredItemPager::run() {
@@ -373,9 +397,15 @@ bool ExpiredItemPager::run() {
373397
maxExpectedDurationForVisitorTask);
374398
}
375399
}
376-
auto sleepTime = config.rlock()->sleepTime;
377-
snooze(sleepTime.count());
378-
updateExpPagerTime(sleepTime.count());
400+
{
401+
// hold the lock while calling snooze - avoids a config change updating
402+
// the sleep time immediately after we read it, then this snooze
403+
// here overwriting the wake time with the old value
404+
auto cfg = config.rlock();
405+
auto sleepTime = cfg->sleepTime.count();
406+
snooze(sleepTime);
407+
updateExpPagerTime(sleepTime);
408+
}
379409

380410
return true;
381411
}

engines/ep/src/item_pager.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <folly/Synchronized.h>
1616
#include <memcached/types.h> // for ssize_t
1717
#include <chrono>
18+
#include <memory>
1819

1920
typedef std::pair<int64_t, int64_t> row_range_t;
2021

@@ -134,7 +135,8 @@ class ItemPager : public GlobalTask {
134135
* Dispatcher job responsible for purging expired items from
135136
* memory and disk.
136137
*/
137-
class ExpiredItemPager : public GlobalTask {
138+
class ExpiredItemPager : public GlobalTask,
139+
public std::enable_shared_from_this<ExpiredItemPager> {
138140
public:
139141

140142
/**
@@ -167,14 +169,20 @@ class ExpiredItemPager : public GlobalTask {
167169
std::chrono::seconds getSleepTime() const;
168170

169171
/**
170-
* Lock the config for writing, and return a handle.
172+
* Enable and schedule the expiry pager task.
171173
*
172-
* Used when other operations need synchronising with config changes
173-
* (e.g., scheduling or cancelling the task)
174+
* @return true if the task was scheduled, false if it was already
175+
* enabled
174176
*/
175-
auto wlockConfig() {
176-
return config.wlock();
177-
}
177+
bool enable();
178+
179+
/**
180+
* Disable and cancel the expiry pager task.
181+
*
182+
* @return true if the task was cancelled, false if it was already
183+
* disabled
184+
*/
185+
bool disable();
178186

179187
bool isEnabled() const;
180188

@@ -196,8 +204,7 @@ class ExpiredItemPager : public GlobalTask {
196204
ssize_t initialRunTime = -1;
197205
bool enabled = false;
198206
};
199-
200-
void updateWakeTimeFromCfg(const Config& cfg);
207+
std::chrono::seconds calculateWakeTimeFromCfg(const Config& cfg);
201208

202209
folly::Synchronized<Config> config;
203210
/**

engines/ep/src/kv_bucket.cc

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,26 +2120,15 @@ void KVBucket::setExpiryPagerTasktime(ssize_t val) {
21202120
}
21212121

21222122
void KVBucket::enableExpiryPager() {
2123-
// hold the config handle while scheduling the task to avoid
2124-
// racing with another thread cancelling it.
2125-
auto cfg = expiryPagerTask->wlockConfig();
2126-
if (cfg->enabled) {
2123+
if (!expiryPagerTask->enable()) {
21272124
EP_LOG_DEBUG_RAW("Expiry Pager already enabled!");
2128-
return;
21292125
}
2130-
cfg->enabled = true;
2131-
ExecutorPool::get()->cancel(expiryPagerTask->getId());
2132-
ExecutorPool::get()->schedule(expiryPagerTask);
21332126
}
21342127

21352128
void KVBucket::disableExpiryPager() {
2136-
auto cfg = expiryPagerTask->wlockConfig();
2137-
if (!cfg->enabled) {
2129+
if (!expiryPagerTask->disable()) {
21382130
EP_LOG_DEBUG_RAW("Expiry Pager already disabled!");
2139-
return;
21402131
}
2141-
cfg->enabled = false;
2142-
ExecutorPool::get()->cancel(expiryPagerTask->getId());
21432132
}
21442133

21452134
void KVBucket::wakeUpExpiryPager() {

engines/ep/tests/module_tests/kv_bucket_test.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,33 @@ TEST_F(KVBucketTest, DataRaceInDoWorkerStat) {
11771177
pool->cancel(task->getId());
11781178
}
11791179

1180+
TEST_F(KVBucketTest, ExpiryConfigChangeWakesTask) {
1181+
// schedule the expiry pager task.
1182+
store->enableExpiryPager();
1183+
// check that the task has a longer runtime to start with
1184+
ASSERT_GT(store->getExpiryPagerSleeptime(), 100);
1185+
1186+
// check the task has not run yet.
1187+
auto& epstats = engine->getEpStats();
1188+
ASSERT_EQ(0, epstats.expiryPagerRuns);
1189+
1190+
// try to change the config to get the task to run asap
1191+
store->setExpiryPagerSleeptime(0);
1192+
1193+
using namespace std::chrono;
1194+
using namespace std::chrono_literals;
1195+
auto deadline = steady_clock::now() + 5s;
1196+
1197+
while (epstats.expiryPagerRuns == 0 &&
1198+
std::chrono::steady_clock::now() < deadline) {
1199+
std::this_thread::sleep_for(std::chrono::microseconds(100));
1200+
}
1201+
1202+
// check that the task has run before our deadline - it wouldn't have
1203+
// if the config change did not wake the task through the pool.
1204+
EXPECT_GT(epstats.expiryPagerRuns, 0);
1205+
}
1206+
11801207
void KVBucketTest::storeAndDeleteItem(Vbid vbid,
11811208
const DocKey& key,
11821209
std::string value) {

0 commit comments

Comments
 (0)