/// <summary> /// Initializes a new instance of the <see cref="ContentStoreConfiguration"/> class. /// </summary> public ContentStoreConfiguration ( MaxSizeQuota maxSizeQuota = null, DiskFreePercentQuota diskFreePercentQuota = null, DenyWriteAttributesOnContentSetting denyWriteAttributesOnContent = DenyWriteAttributesOnContentSetting.Disable, int singleInstanceTimeoutSeconds = DefaultSingleInstanceTimeoutSeconds, bool enableElasticity = false, MaxSizeQuota initialElasticSize = null, int?historyBufferSize = default(int?), int?historyWindowSize = default(int?) ) { Contract.Requires(singleInstanceTimeoutSeconds > 0); MaxSizeQuota = maxSizeQuota; DiskFreePercentQuota = diskFreePercentQuota; DenyWriteAttributesOnContent = denyWriteAttributesOnContent; SingleInstanceTimeoutSeconds = singleInstanceTimeoutSeconds; EnableElasticity = enableElasticity; InitialElasticSize = initialElasticSize; HistoryBufferSize = historyBufferSize; HistoryWindowSize = historyWindowSize; Initialize(); }
/// <summary> /// Initializes a new instance of the <see cref="ElasticSizeRule" /> class. /// </summary> public ElasticSizeRule( int?historyWindowSize, MaxSizeQuota?initialElasticSize, Func <long> getCurrentSizeFunc, Func <int, PinSizeHistory.ReadHistoryResult> getPinnedSizeHistoryFunc, IAbsFileSystem fileSystem, AbsolutePath rootPath, double?calibrationCoefficient = default(double?)) : base(OnlyUnlinkedValue) { Contract.Requires(!historyWindowSize.HasValue || historyWindowSize.Value >= 0); Contract.Requires(getCurrentSizeFunc != null); Contract.Requires(getPinnedSizeHistoryFunc != null); Contract.Requires(fileSystem != null); Contract.Requires(rootPath != null); Contract.Requires(!calibrationCoefficient.HasValue || calibrationCoefficient.Value > 1.0); _historyWindowSize = historyWindowSize ?? DefaultHistoryWindowSize; _calibrationCoefficient = calibrationCoefficient ?? DefaultCalibrationCoefficient; _getPinnedSizeHistoryFunc = getPinnedSizeHistoryFunc; _getCurrentSizeFunc = getCurrentSizeFunc; _fileSystem = fileSystem; _rootPath = rootPath; _initialQuota = initialElasticSize ?? SmallQuota; var loadQuotaResult = LoadOrCreateNew(_fileSystem, _initialQuota, _rootPath); _quota = loadQuotaResult.Quota; _historyTimestampInTick = loadQuotaResult.HistoryTimestampInTick; // TODO: CalibrateAsync method fails in tests all the time. Bug #1331905 CalibrateAsync().GetAwaiter().GetResult().IgnoreFailure(); Contract.Assert(IsInsideHardLimit(0, checkIfQuotaEnabled: false).Succeeded); }
/// <summary> /// Initializes a new instance of the <see cref="MaxSizeRule"/> class. /// </summary> public MaxSizeRule(MaxSizeQuota quota, Func <long> getCurrentSizeFunc) : base(OnlyUnlinkedValue) { Contract.Requires(quota != null); Contract.Requires(getCurrentSizeFunc != null); _quota = quota; _getCurrentSizeFunc = getCurrentSizeFunc; }
/// <summary> /// Initializes a new instance of the <see cref="MaxSizeRule"/> class. /// </summary> public MaxSizeRule(MaxSizeQuota quota, EvictAsync evictAsync, Func <long> getCurrentSizeFunc, DistributedEvictionSettings distributedEvictionSettings = null) : base(evictAsync, OnlyUnlinkedValue, distributedEvictionSettings) { Contract.Requires(quota != null); Contract.Requires(evictAsync != null); Contract.Requires(getCurrentSizeFunc != null); _quota = quota; _getCurrentSizeFunc = getCurrentSizeFunc; }
private LoadQuotaResult CreateNew(IAbsFileSystem fileSystem, MaxSizeQuota initialElasticQuota, AbsolutePath rootPath) { var currentSize = _getCurrentSizeFunc(); if (initialElasticQuota.Hard < currentSize) { // If current size is larger than the initial quota, then throw away initial quota, and use current size. // The quota is set by taking into account the current size and hard limit ratio used during shrinkage. // If the quota is set to the current size, then the very first reservation may potentially be blocking. initialElasticQuota = new MaxSizeQuota((long)(currentSize / CurrentSizeHardLimitRatio)); } // Set the timestamp tick to -1 so that it can consume pre-existing history window upon calibration during construction. var loadQuotaResult = new LoadQuotaResult(initialElasticQuota, -1); SaveQuota(fileSystem, rootPath, loadQuotaResult.Quota, loadQuotaResult.HistoryTimestampInTick).ThrowIfFailure(); return(loadQuotaResult); }
private LoadQuotaResult LoadOrCreateNew(IAbsFileSystem fileSystem, MaxSizeQuota initialElasticQuota, AbsolutePath rootPath) { var filePath = rootPath / BinaryFileName; try { if (!fileSystem.FileExists(filePath)) { return(CreateNew(fileSystem, initialElasticQuota, rootPath)); } using (var stream = fileSystem.OpenReadOnly(filePath, FileShare.Delete)) { using (var reader = new BinaryReader(stream)) { return(new LoadQuotaResult(new MaxSizeQuota(reader.ReadInt64(), reader.ReadInt64()), reader.ReadInt64())); } } } catch (IOException) { return(CreateNew(fileSystem, initialElasticQuota, rootPath)); } }
/// <summary> /// Initializes a new instance of the <see cref="LoadQuotaResult"/> class. /// </summary> public LoadQuotaResult(MaxSizeQuota quota, long historyTimestampInTick) { Quota = quota; HistoryTimestampInTick = historyTimestampInTick; }
/// <inheritdoc /> public override Task <CalibrateResult> CalibrateAsync() { // Calibration should be done in atomic way. return(Task.FromResult( _locker.WithWriteLock(() => { /* * o (Case A) * * --------------------------------------------- Hard limit * * o (Case B) * * --------------------------------------------- Soft limit * * o (Case C) */ bool isCalibrated = false; string reason = string.Empty; if (!IsInsideHardLimit(0, false).Succeeded) { // Case A. _quota = new MaxSizeQuota((long)(_getCurrentSizeFunc() * _calibrationCoefficient)); isCalibrated = true; } else { // Case B & C. // Try bring hard limit closer to the current size based on history of pinned sizes. // Constraint 1: newHardLimit >= currentSize. long newHardLimit = (long)(_getCurrentSizeFunc() / CurrentSizeHardLimitRatio); if (newHardLimit < _initialQuota.Hard) { newHardLimit = _initialQuota.Hard; } // Constraint 2: window.Max * CalibrateUpCoefficient <= newHardLimit. var history = _getPinnedSizeHistoryFunc(_historyWindowSize); if (newHardLimit >= _quota.Hard) { // Candidate new hard limit is larger than or equal to the current hard limit. reason = $"New hard limit, {newHardLimit}, is larger than or equal to the current hard limit, {_quota.Hard}"; } else if (history.Window.Count <= 0) { // No entries in the history window. // Note that we allow to consume history which is smaller than the specified history window size. // This allows smaller history to cause calibration downward. reason = "History window is empty"; } else if (history.TimestampInTick <= _historyTimestampInTick) { // History timestamp is older than the current timestamp. reason = $"History timestamp, {history.TimestampInTick}, is older than the current timestamp, {_historyTimestampInTick}"; } else if (history.Window.Max() * _calibrationCoefficient >= newHardLimit) { // Max value in the calibration window, times the calibration coefficient, is larger than the new hard limit. reason = $"Max history window, {history.Window.Max()}, with calibration coefficient {_calibrationCoefficient}, exceeds candidate hard limit {newHardLimit}"; } else { _quota = new MaxSizeQuota(newHardLimit); _historyTimestampInTick = history.TimestampInTick; isCalibrated = true; } } _isQuotaEnabled = true; if (!isCalibrated) { return new CalibrateResult(reason); } var saveResult = SaveQuota(); return !saveResult.Succeeded ? new CalibrateResult(saveResult.ErrorMessage !) : new CalibrateResult(_quota.Hard); }))); }
private static BoolResult SaveQuota(IAbsFileSystem fileSystem, AbsolutePath rootPath, MaxSizeQuota quota, long historyTimestampInTick) { var filePath = rootPath / BinaryFileName; try { fileSystem.DeleteFile(filePath); using (var stream = fileSystem.Open(filePath, FileAccess.Write, FileMode.CreateNew, FileShare.Delete)) { using (var writer = new BinaryWriter(stream)) { writer.Write(quota.Hard); writer.Write(quota.Soft); writer.Write(historyTimestampInTick); } } return(BoolResult.Success); } catch (Exception e) { // When failed, clean up so that it is not used in the next load. fileSystem.DeleteFile(filePath); return(new BoolResult(e)); } }