migrate functionScheduler to eventBase timer

Summary:
This is reasonably straightforward, although a little
more fiddly than I'd hoped because the timer wheel stuff doesn't
offer a convenient way to set up a recurring timer.

I've also made the inode unloading code get run globally for all
mounts; it was previously scheduling one timer per mount point.
This nets out the same; the function scheduler was just a single
thread anyway, so there is no change in the level of concurrency.
I believe that this tidies up the unload counter too; it looked
like we'd set the counter to be the result of the last mount
point that we processed rather than the aggregate of all mounts.

Having the unload timer be associated with the server rather
than the mount points means that we don't have to do anything
special to coordinate with the timer management when the mount
point is being torn down.

Reviewed By: bolinfest

Differential Revision: D5792938

fbshipit-source-id: 1a14bb7b7f4952139e684fe6b52f64bd1ba70dd0
This commit is contained in:
Wez Furlong 2017-09-13 08:26:50 -07:00 committed by Facebook Github Bot
parent e837848da5
commit fe1a376e0f
2 changed files with 64 additions and 50 deletions

View File

@ -157,6 +157,44 @@ folly::Future<Unit> EdenServer::unmountAll() {
});
}
void EdenServer::scheduleFlushStats() {
mainEventBase_->timer().scheduleTimeoutFn(
[this] {
flushStatsNow();
scheduleFlushStats();
},
std::chrono::seconds(1));
}
void EdenServer::unloadInodes() {
std::vector<TreeInodePtr> roots;
{
auto mountPoints = mountPoints_.wlock();
for (auto& entry : *mountPoints) {
roots.emplace_back(entry.second.edenMount->getRootInode());
}
}
if (!roots.empty()) {
XLOG(INFO) << "UnloadInodeScheduler Unloading Free Inodes";
auto serviceData = stats::ServiceData::get();
uint64_t totalUnloaded = serviceData->getCounter(kPeriodicUnloadCounterKey);
for (auto& rootInode : roots) {
totalUnloaded += rootInode->unloadChildrenNow(
std::chrono::minutes(FLAGS_unload_age_minutes));
}
serviceData->setCounter(kPeriodicUnloadCounterKey, totalUnloaded);
}
scheduleInodeUnload(std::chrono::hours(FLAGS_unload_interval_hours));
}
void EdenServer::scheduleInodeUnload(std::chrono::milliseconds timeout) {
mainEventBase_->timer().scheduleTimeoutFn(
[this] { unloadInodes(); }, timeout);
}
void EdenServer::prepare() {
acquireEdenLock();
// Store a pointer to the EventBase that will be used to drive
@ -165,18 +203,23 @@ void EdenServer::prepare() {
createThriftServer();
localStore_ = make_shared<LocalStore>(rocksPath_);
functionScheduler_ = make_shared<folly::FunctionScheduler>();
functionScheduler_->setThreadName("EdenFuncSched");
functionScheduler_->start();
// Start stats aggregation
functionScheduler_->addFunction(
[this] { flushStatsNow(); }, std::chrono::seconds(1));
scheduleFlushStats();
// Set the ServiceData counter for tracking number of inodes unloaded by
// periodic job for unloading inodes to zero on EdenServer start.
stats::ServiceData::get()->setCounter(kPeriodicUnloadCounterKey, 0);
// Schedule a periodic job to unload unused inodes based on the last access
// time. currently Eden does not have accurate timestamp tracking for inodes,
// so using unloadChildrenNow just to validate the behaviour. We will have to
// modify current unloadChildrenNow function to unload inodes based on the
// last access time.
if (FLAGS_unload_interval_hours > 0) {
scheduleInodeUnload(std::chrono::minutes(FLAGS_start_delay_minutes));
}
auto pool =
make_shared<wangle::CPUThreadPoolExecutor>(FLAGS_num_eden_threads);
wangle::setCPUExecutor(pool);
@ -279,29 +322,6 @@ void EdenServer::mount(shared_ptr<EdenMount> edenMount) {
// wait for it to finish.
(void)finishFuture;
// Adding function for the newly added mountpoint to Schedule
// a periodic job to unload unused inodes based on the last access time.
// currently Eden doesnot have accurate timestamp tracking for inodes, so
// using unloadChildrenNow just to validate the behaviour. We will have to
// modify current unloadChildrenNow function to unload inodes based on the
// last access time.
if (FLAGS_unload_interval_hours > 0) {
functionScheduler_->addFunction(
[edenMount] {
auto rootInode = (edenMount.get())->getRootInode();
XLOG(INFO) << "UnloadInodeScheduler Unloading Free Inodes";
auto unloadCount = rootInode->unloadChildrenNow(
std::chrono::minutes(FLAGS_unload_age_minutes));
unloadCount +=
stats::ServiceData::get()->getCounter(kPeriodicUnloadCounterKey);
stats::ServiceData::get()->setCounter(
kPeriodicUnloadCounterKey, unloadCount);
},
std::chrono::hours(FLAGS_unload_interval_hours),
getPeriodicUnloadFunctionName(edenMount.get()),
std::chrono::minutes(FLAGS_start_delay_minutes));
}
// Register callback for getting Loaded inodes in the memory for a mountPoint.
stats::ServiceData::get()->getDynamicCounters()->registerCallback(
edenMount->getCounterName(CounterName::LOADED), [edenMount] {
@ -361,8 +381,6 @@ Future<Unit> EdenServer::unmount(StringPiece mountPath) {
void EdenServer::mountFinished(EdenMount* edenMount) {
auto mountPath = edenMount->getPath().stringPiece();
XLOG(INFO) << "mount point \"" << mountPath << "\" stopped";
functionScheduler_->cancelFunctionAndWait(
getPeriodicUnloadFunctionName(edenMount));
stats::ServiceData::get()->getDynamicCounters()->unregisterCallback(
edenMount->getCounterName(CounterName::LOADED));
stats::ServiceData::get()->getDynamicCounters()->unregisterCallback(
@ -386,10 +404,6 @@ void EdenServer::mountFinished(EdenMount* edenMount) {
});
}
string EdenServer::getPeriodicUnloadFunctionName(const EdenMount* mount) {
return folly::to<string>("unload:", mount->getPath().stringPiece());
}
EdenServer::MountList EdenServer::getMountPoints() const {
MountList results;
{
@ -527,7 +541,6 @@ void EdenServer::stop() const {
void EdenServer::shutdown() {
unmountAll().get();
functionScheduler_->shutdown();
}
void EdenServer::flushStatsNow() const {

View File

@ -180,15 +180,6 @@ class EdenServer {
*/
void flushStatsNow() const;
/**
* Returns shared_ptr of FunctionScheduler which can be used to schedule
* periodic jobs in EdenServer, Currently this function scheduler is used for
* unloading free inodes periodically and stats aggregation
*/
std::shared_ptr<folly::FunctionScheduler> getFunctionScheduler() {
return functionScheduler_;
}
/**
* Get the main thread's EventBase.
*
@ -221,6 +212,22 @@ class EdenServer {
EdenServer(EdenServer const&) = delete;
EdenServer& operator=(EdenServer const&) = delete;
// Schedules a timer to flush stats (and reschedule itself).
// We should have at most one of these pending at a time.
// Must be called only from the eventBase thread.
void scheduleFlushStats();
// Schedule a call to unloadInodes() to happen after timeout
// has expired.
// Must be called only from the eventBase thread.
void scheduleInodeUnload(std::chrono::milliseconds timeout);
// Perform unloading of inodes based on their last access time
// and then schedule another call to unloadInodes() to happen
// at the next appropriate interval. The unload attempt applies to
// all mounts.
void unloadInodes();
std::shared_ptr<BackingStore> createBackingStore(
folly::StringPiece type,
folly::StringPiece name);
@ -230,7 +237,6 @@ class EdenServer {
// Called when a mount has been unmounted and has stopped.
void mountFinished(EdenMount* mountPoint);
std::string getPeriodicUnloadFunctionName(const EdenMount* mount);
// Called before destructing EdenServer
void shutdown();
@ -260,11 +266,6 @@ class EdenServer {
folly::Synchronized<MountMap> mountPoints_;
mutable fusell::ThreadLocalEdenStats edenStats_;
/**
* Function scheduler to unload free inodes periodically
*/
std::shared_ptr<folly::FunctionScheduler> functionScheduler_;
/**
* The EventBase driving the main thread loop.
*