/// <inheritdoc /> public IEnumerable <IReadOnlyList <ContentHashWithLastAccessTimeAndReplicaCount> > GetLruPages( Context context, IReadOnlyList <ContentHashWithLastAccessTimeAndReplicaCount> contentHashesWithInfo) { Contract.Assert(_configuration.HasReadOrWriteMode(ContentLocationMode.LocalLocationStore), "GetLruPages can only be called when local location store is enabled"); if (contentHashesWithInfo.Count != 0) { var first = contentHashesWithInfo[0]; var last = contentHashesWithInfo[contentHashesWithInfo.Count - 1]; context.Debug($"GetLruPages start with contentHashesWithInfo.Count={contentHashesWithInfo.Count}, firstAge={first.Age}, lastAge={last.Age}"); } var operationContext = new OperationContext(context); var pageSize = _configuration.EvictionWindowSize; // Assume that EffectiveLastAccessTime will always have a value. var comparer = Comparer <ContentHashWithLastAccessTimeAndReplicaCount> .Create((c1, c2) => c1.EffectiveLastAccessTime.Value.CompareTo(c2.EffectiveLastAccessTime.Value)); Func <List <ContentHashWithLastAccessTimeAndReplicaCount>, IEnumerable <ContentHashWithLastAccessTimeAndReplicaCount> > query = page => _localLocationStore.GetEffectiveLastAccessTimes(operationContext, page.SelectList(v => new ContentHashWithLastAccessTime(v.ContentHash, v.LastAccessTime))).ThrowIfFailure(); // We make sure that we select a set of the newer content, to ensure that we at least look at newer content to see if it should be // evicted first due to having a high number of replicas. We do this by looking at the start as well as at middle of the list. var localOldest = contentHashesWithInfo.Take(contentHashesWithInfo.Count / 2).QueryAndOrderInPages(pageSize, comparer, query); var localMid = contentHashesWithInfo.SkipOptimized(contentHashesWithInfo.Count / 2).QueryAndOrderInPages(pageSize, comparer, query); var mergedEnumerables = NuCacheCollectionUtilities.MergeOrdered(localOldest, localMid, comparer); return(mergedEnumerables.GetPages(pageSize)); }
/// <inheritdoc /> public IEnumerable <ContentHashWithLastAccessTimeAndReplicaCount> GetHashesInEvictionOrder( Context context, IReadOnlyList <ContentHashWithLastAccessTimeAndReplicaCount> contentHashesWithInfo) { Contract.Assert(_configuration.HasReadOrWriteMode(ContentLocationMode.LocalLocationStore), "GetLruPages can only be called when local location store is enabled"); // Counter for successful eviction candidates. Different than total number of eviction candidates, because this only increments when candidate is above minimum eviction age int evictionCount = 0; // contentHashesWithInfo is literally all data inside the content directory. The Purger wants to remove // content until we are within quota. Here we return batches of content to be removed. // contentHashesWithInfo is sorted by (local) LastAccessTime in descending order (Least Recently Used). if (contentHashesWithInfo.Count != 0) { var first = contentHashesWithInfo[0]; var last = contentHashesWithInfo[contentHashesWithInfo.Count - 1]; context.Debug($"{nameof(GetHashesInEvictionOrder)} start with contentHashesWithInfo.Count={contentHashesWithInfo.Count}, firstAge={first.Age(_clock)}, lastAge={last.Age(_clock)}"); } var operationContext = new OperationContext(context); // Ideally, we want to remove content we know won't be used again for quite a while. We don't have that // information, so we use an evictability metric. Here we obtain and sort by that evictability metric. // Assume that EffectiveLastAccessTime will always have a value. var comparer = Comparer <ContentHashWithLastAccessTimeAndReplicaCount> .Create((c1, c2) => c1.EffectiveLastAccessTime.Value.CompareTo(c2.EffectiveLastAccessTime.Value)); Func <List <ContentHashWithLastAccessTimeAndReplicaCount>, IEnumerable <ContentHashWithLastAccessTimeAndReplicaCount> > intoEffectiveLastAccessTimes = page => _localLocationStore.GetEffectiveLastAccessTimes( operationContext, page.SelectList(v => new ContentHashWithLastAccessTime(v.ContentHash, v.LastAccessTime))).ThrowIfFailure(); // We make sure that we select a set of the newer content, to ensure that we at least look at newer content to see if it should be // evicted first due to having a high number of replicas. We do this by looking at the start as well as at middle of the list. var oldestByEvictability = contentHashesWithInfo.Take(contentHashesWithInfo.Count / 2).ApproximateSort(comparer, intoEffectiveLastAccessTimes, _configuration.EvictionPoolSize, _configuration.EvictionWindowSize, _configuration.EvictionRemovalFraction); var youngestByEvictability = contentHashesWithInfo.SkipOptimized(contentHashesWithInfo.Count / 2).ApproximateSort(comparer, intoEffectiveLastAccessTimes, _configuration.EvictionPoolSize, _configuration.EvictionWindowSize, _configuration.EvictionRemovalFraction); return(NuCacheCollectionUtilities.MergeOrdered(oldestByEvictability, youngestByEvictability, comparer) .Where((candidate, index) => IsPassEvictionAge(context, candidate, _configuration.EvictionMinAge, index, ref evictionCount))); }