private async Task RunSourceProviderMaintenance(IExecutionContext executionContext, ITrackingManager trackingManager, TrackingConfig trackingConfig) { var extensionManager = HostContext.GetService <IExtensionManager>(); ISourceProvider sourceProvider = extensionManager.GetExtensions <ISourceProvider>().FirstOrDefault(x => string.Equals(x.RepositoryType, trackingConfig.RepositoryType, StringComparison.OrdinalIgnoreCase)); if (sourceProvider != null) { try { trackingManager.MaintenanceStarted(trackingConfig); string repositoryPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), trackingConfig.SourcesDirectory); await sourceProvider.RunMaintenanceOperations(executionContext, repositoryPath); trackingManager.MaintenanceCompleted(trackingConfig); } catch (Exception ex) { executionContext.Error(StringUtil.Loc("ErrorDuringBuildGC", trackingConfig.FileLocation)); executionContext.Error(ex); } } }
public async Task RunMaintenanceOperation(IExecutionContext executionContext) { Trace.Entering(); ArgUtil.NotNull(executionContext, nameof(executionContext)); // this might be not accurate when the agent is configured for old TFS server int totalAvailableTimeInMinutes = executionContext.Variables.GetInt("maintenance.jobtimeoutinminutes") ?? 60; // start a timer to track how much time we used Stopwatch totalTimeSpent = Stopwatch.StartNew(); var trackingManager = HostContext.GetService <ITrackingManager>(); int staleBuildDirThreshold = executionContext.Variables.GetInt("maintenance.deleteworkingdirectory.daysthreshold") ?? 0; if (staleBuildDirThreshold > 0) { // scan unused build directories executionContext.Output(StringUtil.Loc("DiscoverBuildDir", staleBuildDirThreshold)); trackingManager.MarkExpiredForGarbageCollection(executionContext, TimeSpan.FromDays(staleBuildDirThreshold)); } else { executionContext.Output(StringUtil.Loc("GCBuildDirNotEnabled")); return; } executionContext.Output(StringUtil.Loc("GCBuildDir")); // delete unused build directories trackingManager.DisposeCollectedGarbage(executionContext); // give source provider a chance to run maintenance operation Trace.Info("Scan all SourceFolder tracking files."); string searchRoot = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Build.Path.SourceRootMappingDirectory); if (!Directory.Exists(searchRoot)) { executionContext.Output(StringUtil.Loc("GCDirNotExist", searchRoot)); return; } // <tracking config, tracking file path> List <Tuple <TrackingConfig, string> > optimizeTrackingFiles = new List <Tuple <TrackingConfig, string> >(); var allTrackingFiles = Directory.EnumerateFiles(searchRoot, Constants.Build.Path.TrackingConfigFile, SearchOption.AllDirectories); Trace.Verbose($"Find {allTrackingFiles.Count()} tracking files."); foreach (var trackingFile in allTrackingFiles) { executionContext.Output(StringUtil.Loc("EvaluateTrackingFile", trackingFile)); TrackingConfigBase tracking = trackingManager.LoadIfExists(executionContext, trackingFile); // detect whether the tracking file is in new format. TrackingConfig newTracking = tracking as TrackingConfig; if (newTracking == null) { executionContext.Output(StringUtil.Loc("GCOldFormatTrackingFile", trackingFile)); } else if (string.IsNullOrEmpty(newTracking.RepositoryType)) { // repository not been set. executionContext.Output(StringUtil.Loc("SkipTrackingFileWithoutRepoType", trackingFile)); } else { optimizeTrackingFiles.Add(new Tuple <TrackingConfig, string>(newTracking, trackingFile)); } } // Sort the all tracking file ASC by last maintenance attempted time foreach (var trackingInfo in optimizeTrackingFiles.OrderBy(x => x.Item1.LastMaintenanceAttemptedOn)) { // maintenance has been cancelled. executionContext.CancellationToken.ThrowIfCancellationRequested(); bool runMainenance = false; TrackingConfig trackingConfig = trackingInfo.Item1; string trackingFile = trackingInfo.Item2; if (trackingConfig.LastMaintenanceAttemptedOn == null) { // this folder never run maintenance before, we will do maintenance if there is more than half of the time remains. if (totalTimeSpent.Elapsed.TotalMinutes < totalAvailableTimeInMinutes / 2) // 50% time left { runMainenance = true; } else { executionContext.Output($"Working directory '{trackingConfig.BuildDirectory}' has never run maintenance before. Skip since we may not have enough time."); } } else if (trackingConfig.LastMaintenanceCompletedOn == null) { // this folder did finish maintenance last time, this might indicate we need more time for this working directory if (totalTimeSpent.Elapsed.TotalMinutes < totalAvailableTimeInMinutes / 4) // 75% time left { runMainenance = true; } else { executionContext.Output($"Working directory '{trackingConfig.BuildDirectory}' didn't finish maintenance last time. Skip since we may not have enough time."); } } else { // estimate time for running maintenance TimeSpan estimateTime = trackingConfig.LastMaintenanceCompletedOn.Value - trackingConfig.LastMaintenanceAttemptedOn.Value; // there is more than 10 mins left after we run maintenance on this repository directory if (totalAvailableTimeInMinutes > totalTimeSpent.Elapsed.TotalMinutes + estimateTime.TotalMinutes + 10) { runMainenance = true; } else { executionContext.Output($"Working directory '{trackingConfig.BuildDirectory}' may take about '{estimateTime.TotalMinutes}' mins to finish maintenance. It's too risky since we only have '{totalAvailableTimeInMinutes - totalTimeSpent.Elapsed.TotalMinutes}' mins left for maintenance."); } } if (runMainenance) { var extensionManager = HostContext.GetService <IExtensionManager>(); ISourceProvider sourceProvider = extensionManager.GetExtensions <ISourceProvider>().FirstOrDefault(x => string.Equals(x.RepositoryType, trackingConfig.RepositoryType, StringComparison.OrdinalIgnoreCase)); if (sourceProvider != null) { try { trackingManager.MaintenanceStarted(trackingConfig, trackingFile); string repositoryPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), trackingConfig.SourcesDirectory); await sourceProvider.RunMaintenanceOperations(executionContext, repositoryPath); trackingManager.MaintenanceCompleted(trackingConfig, trackingFile); } catch (Exception ex) { executionContext.Error(StringUtil.Loc("ErrorDuringBuildGC", trackingFile)); executionContext.Error(ex); } } } } }