From fe1a376e0fdfb4dee6a7eddf553168e83d26aec1 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Wed, 13 Sep 2017 08:26:50 -0700 Subject: [PATCH] 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 --- eden/fs/service/EdenServer.cpp | 83 ++++++++++++++++++++-------------- eden/fs/service/EdenServer.h | 31 +++++++------ 2 files changed, 64 insertions(+), 50 deletions(-) diff --git a/eden/fs/service/EdenServer.cpp b/eden/fs/service/EdenServer.cpp index 4983c4f093..55d11d7da4 100644 --- a/eden/fs/service/EdenServer.cpp +++ b/eden/fs/service/EdenServer.cpp @@ -157,6 +157,44 @@ folly::Future EdenServer::unmountAll() { }); } +void EdenServer::scheduleFlushStats() { + mainEventBase_->timer().scheduleTimeoutFn( + [this] { + flushStatsNow(); + scheduleFlushStats(); + }, + std::chrono::seconds(1)); +} + +void EdenServer::unloadInodes() { + std::vector 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(rocksPath_); - functionScheduler_ = make_shared(); - 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(FLAGS_num_eden_threads); wangle::setCPUExecutor(pool); @@ -279,29 +322,6 @@ void EdenServer::mount(shared_ptr 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 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("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 { diff --git a/eden/fs/service/EdenServer.h b/eden/fs/service/EdenServer.h index 369f4a24f5..b9bea6f9f1 100644 --- a/eden/fs/service/EdenServer.h +++ b/eden/fs/service/EdenServer.h @@ -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 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 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 mountPoints_; mutable fusell::ThreadLocalEdenStats edenStats_; - /** - * Function scheduler to unload free inodes periodically - */ - std::shared_ptr functionScheduler_; - /** * The EventBase driving the main thread loop. *