@@ -182,16 +182,60 @@ using namespace facebook::eden;
182182
183183constexpr auto kMountHealthCheckTimeout = std::chrono::seconds{5 };
184184
185+ using EmittedMountHealthIssues =
186+ folly::Synchronized<std::unordered_map<std::string, std::set<std::string>>>;
187+
188+ bool shouldLogMountHealthIssue (
189+ const std::shared_ptr<EmittedMountHealthIssues>& emittedMountHealthIssues,
190+ const std::string& mountPath,
191+ const std::string& reason) {
192+ auto emittedIssues = emittedMountHealthIssues->wlock ();
193+ auto & emittedReasons = (*emittedIssues)[mountPath];
194+ return emittedReasons.insert (reason).second ;
195+ }
196+
197+ void clearMountHealthIssues (
198+ const std::shared_ptr<EmittedMountHealthIssues>& emittedMountHealthIssues,
199+ const std::string& mountPath) {
200+ auto emittedIssues = emittedMountHealthIssues->wlock ();
201+ emittedIssues->erase (mountPath);
202+ }
203+
204+ void clearAllMountHealthIssues (
205+ const std::shared_ptr<EmittedMountHealthIssues>& emittedMountHealthIssues) {
206+ auto emittedIssues = emittedMountHealthIssues->wlock ();
207+ emittedIssues->clear ();
208+ }
209+
210+ void pruneMountHealthIssues (
211+ const std::shared_ptr<EmittedMountHealthIssues>& emittedMountHealthIssues,
212+ const std::unordered_set<std::string>& configuredMountPaths) {
213+ auto emittedIssues = emittedMountHealthIssues->wlock ();
214+ for (auto it = emittedIssues->begin (); it != emittedIssues->end ();) {
215+ if (configuredMountPaths.find (it->first ) == configuredMountPaths.end ()) {
216+ it = emittedIssues->erase (it);
217+ } else {
218+ ++it;
219+ }
220+ }
221+ }
222+
185223struct MountHealthIssueLogContext {
186224 MountHealthIssueLogContext (
187225 std::weak_ptr<EdenFsEventsLogger> edenFsEventsLogger,
226+ std::shared_ptr<EmittedMountHealthIssues> emittedMountHealthIssues,
188227 std::string mountPath,
189228 std::string repoSource)
190229 : edenFsEventsLogger_{std::move (edenFsEventsLogger)},
230+ emittedMountHealthIssues{std::move (emittedMountHealthIssues)},
191231 mountPath{std::move (mountPath)},
192232 repoSource{std::move (repoSource)} {}
193233
194234 void log (std::string reason, std::string error) const {
235+ if (!shouldLogMountHealthIssue (
236+ emittedMountHealthIssues, mountPath, reason)) {
237+ return ;
238+ }
195239 auto edenFsEventsLogger = edenFsEventsLogger_.lock ();
196240 if (!edenFsEventsLogger) {
197241 return ;
@@ -210,6 +254,7 @@ struct MountHealthIssueLogContext {
210254 }
211255
212256 std::weak_ptr<EdenFsEventsLogger> edenFsEventsLogger_;
257+ std::shared_ptr<EmittedMountHealthIssues> emittedMountHealthIssues;
213258 std::string mountPath;
214259 std::string repoSource;
215260};
@@ -659,6 +704,7 @@ EdenServer::EdenServer(
659704 config_{std::make_shared<ReloadableConfig>(edenConfig)},
660705 mountPoints_{std::make_shared<folly::Synchronized<MountMap>>(
661706 MountMap{kPathMapDefaultCaseSensitive })},
707+ emittedMountHealthIssues_{std::make_shared<EmittedMountHealthIssues>()},
662708 // Store a pointer to the EventBase that will be used to drive
663709 // the main thread. The runServer() code will end up driving this
664710 // EventBase.
@@ -3442,8 +3488,10 @@ void EdenServer::scheduleRunningMountHealthCheck(
34423488
34433489 auto started = make_shared<std::atomic_bool>(false );
34443490 auto completed = make_shared<std::atomic_bool>(false );
3491+ auto emittedMountHealthIssues = emittedMountHealthIssues_;
34453492 auto logContext = make_shared<const MountHealthIssueLogContext>(
34463493 serverState_->getEdenFsEventsLogger (),
3494+ emittedMountHealthIssues,
34473495 std::move (mountPathString),
34483496 std::move (repoSource));
34493497
@@ -3463,7 +3511,10 @@ void EdenServer::scheduleRunningMountHealthCheck(
34633511 return checkRunningEdenMountHealth (logContext->mountPath );
34643512 })
34653513 .thenTry (
3466- [completed, logContext, runningMountHealthChecks](
3514+ [completed,
3515+ emittedMountHealthIssues,
3516+ logContext,
3517+ runningMountHealthChecks](
34673518 folly::Try<std::optional<EdenMountHealthCheckIssue>>&& result) {
34683519 const auto alreadyCompleted = completed->exchange (true );
34693520 {
@@ -3482,6 +3533,8 @@ void EdenServer::scheduleRunningMountHealthCheck(
34823533 }
34833534 const auto & issue = result.value ();
34843535 if (!issue.has_value ()) {
3536+ clearMountHealthIssues (
3537+ emittedMountHealthIssues, logContext->mountPath );
34853538 return ;
34863539 }
34873540 logContext->log (
@@ -3504,12 +3557,20 @@ void EdenServer::accidentalUnmountRecovery() {
35043557 }
35053558
35063559 if (dirs.empty ()) {
3560+ clearAllMountHealthIssues (emittedMountHealthIssues_);
35073561 XLOGF (
35083562 DBG5 ,
35093563 " No mount points currently configured, skipping accidental unmount recovery." );
35103564 return ;
35113565 }
35123566
3567+ std::unordered_set<std::string> configuredMountPaths;
3568+ for (const auto & client : dirs.items ()) {
3569+ configuredMountPaths.insert (
3570+ std::string{canonicalPath (client.first .stringPiece ()).view ()});
3571+ }
3572+ pruneMountHealthIssues (emittedMountHealthIssues_, configuredMountPaths);
3573+
35133574 const auto mountPoints = mountPoints_->rlock ();
35143575 for (const auto & client : dirs.items ()) {
35153576 auto mountPath = canonicalPath (client.first .stringPiece ());
@@ -3518,6 +3579,8 @@ void EdenServer::accidentalUnmountRecovery() {
35183579 if (it != mountPoints->end ()) {
35193580 scheduleRunningMountHealthCheck (mountPath, client.second .asString ());
35203581 } else {
3582+ clearMountHealthIssues (
3583+ emittedMountHealthIssues_, std::string{mountPath.view ()});
35213584 // This mount point is not currently mounted, but it was configured
35223585 // in config.json. This means that the client was unmounted.
35233586 // We should attempt to remount it, if it is unmounted accidentally.
0 commit comments