/// <summary> /// Remove unused snapshots. /// A snapshot is considered unused if it has not been accessed for at least /// (MDHeartbeatPeriod + MDOldSnapshotHeartbeatMargin) minutes. /// However, the last MDMinNumberOfSnapshotsToKeep snapshots will be kept regardless /// of the access time. /// </summary> public void Purge(ILogger logger) { var allSnapshotPaths = _storage.GetInstalledAndInstallingSnapshots(); var threshold = DateTime.UtcNow - _heartbeatPeriod - _oldHeartbeatAgeMargin; var pathSortedByAccessTime = allSnapshotPaths .Select(path => Tuple.Create(path, GetSnapshotAccessTimeUtc(path, logger))) .OrderBy(entry => entry.Item2) .ToArray(); for (var i = 0; i < pathSortedByAccessTime.Length - _minNumberOfSnapshotsToKeep; ++i) { var creationTime = pathSortedByAccessTime[i].Item2; if (creationTime > threshold) { break; } var pathToRemove = pathSortedByAccessTime[i].Item1; try { var message = string.Format(PowerShellWorkerStrings.RemovingDependenciesFolder, pathToRemove); logger.Log(isUserOnlyLog: false, LogLevel.Trace, message); _storage.RemoveSnapshot(pathToRemove); } catch (IOException e) { var message = string.Format(PowerShellWorkerStrings.FailedToRemoveDependenciesFolder, pathToRemove, e.Message); logger.Log(isUserOnlyLog: false, LogLevel.Warning, message, e); } } }
public void InstallSnapshot( IEnumerable <DependencyManifestEntry> dependencies, string targetPath, PowerShell pwsh, DependencySnapshotInstallationMode installationMode, ILogger logger) { var installingPath = CreateInstallingSnapshot(targetPath); logger.Log( isUserOnlyLog: false, LogLevel.Trace, string.Format( PowerShellWorkerStrings.InstallingFunctionAppRequiredModules, installingPath, installationMode)); try { foreach (DependencyInfo module in GetExactVersionsOfDependencies(dependencies)) { InstallModule(module, installingPath, pwsh, logger); } _snapshotContentLogger.LogDependencySnapshotContent(installingPath, logger); switch (installationMode) { case DependencySnapshotInstallationMode.Optional: // If the new snapshot turns out to be equivalent to the latest one, // removing it helps us save storage space and avoid unnecessary worker restarts. // It is ok to do that during background upgrade because the current // worker already has a good enough snapshot, and nothing depends on // the new snapshot yet. PromoteToInstalledOrRemove(installingPath, targetPath, installationMode, logger); break; case DependencySnapshotInstallationMode.Required: // Even if the new snapshot turns out to be equivalent to the latest one, // removing it would not be safe because the current worker already depends // on it, as it has the path to this snapshot already added to PSModulePath. // As opposed to the background upgrade case, this snapshot is *required* for // this worker to run, even though it occupies some space (until the workers // restart and the redundant snapshots are purged). PromoteToInstalled(installingPath, targetPath, installationMode, logger); break; default: throw new ArgumentException($"Unexpected installation mode: {installationMode}", nameof(installationMode)); } } catch (Exception e) { var message = string.Format( PowerShellWorkerStrings.FailedToInstallDependenciesSnapshot, targetPath, installationMode); logger.Log(isUserOnlyLog: false, LogLevel.Warning, message, e); _storage.RemoveSnapshot(installingPath); throw; } finally { _moduleProvider.Cleanup(pwsh); } }
public void InstallSnapshot( IEnumerable <DependencyManifestEntry> dependencies, string targetPath, PowerShell pwsh, ILogger logger) { logger.Log(isUserOnlyLog: false, LogLevel.Trace, PowerShellWorkerStrings.InstallingFunctionAppDependentModules); var installingPath = CreateInstallingSnapshot(targetPath); try { foreach (DependencyInfo module in GetExactVersionsOfDependencies(dependencies)) { logger.Log(isUserOnlyLog: false, LogLevel.Trace, string.Format(PowerShellWorkerStrings.StartedInstallingModule, module.Name, module.ExactVersion)); int tries = 1; while (true) { try { _moduleProvider.SaveModule(pwsh, module.Name, module.ExactVersion, installingPath); var message = string.Format(PowerShellWorkerStrings.ModuleHasBeenInstalled, module.Name, module.ExactVersion); logger.Log(isUserOnlyLog: false, LogLevel.Trace, message); break; } catch (Exception e) { string currentAttempt = GetCurrentAttemptMessage(tries); var errorMsg = string.Format(PowerShellWorkerStrings.FailToInstallModule, module.Name, module.ExactVersion, currentAttempt, e.Message); logger.Log(isUserOnlyLog: false, LogLevel.Error, errorMsg); if (tries >= MaxNumberOfTries) { errorMsg = string.Format(PowerShellWorkerStrings.FailToInstallFuncAppDependencies, e.Message); throw new DependencyInstallationException(errorMsg, e); } } // Wait for 2^(tries-1) seconds between retries. In this case, it would be 1, 2, and 4 seconds, respectively. var waitTimeSpan = TimeSpan.FromSeconds(Math.Pow(2, tries - 1)); Thread.Sleep(waitTimeSpan); tries++; } } _storage.PromoteInstallingSnapshotToInstalledAtomically(targetPath); } catch (Exception e) { var message = string.Format(PowerShellWorkerStrings.FailedToInstallDependenciesSnapshot, targetPath); logger.Log(isUserOnlyLog: false, LogLevel.Warning, message, e); _storage.RemoveSnapshot(installingPath); throw; } finally { _moduleProvider.Cleanup(pwsh); } }