/// <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));
        }
Ejemplo n.º 2
0
        /// <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");

            var pageSize = PageSize;

            // Priority queue orders by least first. So we compare by last access time to get the least last access time (i.e. oldest) first.
            var priorityQueue = new PriorityQueue <ContentHashWithLastAccessTimeAndReplicaCount>(
                pageSize * 2,
                // Assume that EffectiveLastAccessTime will always have a value.
                Comparer <ContentHashWithLastAccessTimeAndReplicaCount> .Create((c1, c2) => c1.EffectiveLastAccessTime.Value.CompareTo(c2.EffectiveLastAccessTime.Value)));

            var operationContext = new OperationContext(context);

            var subPageSize = Math.Max(1, pageSize / 10);

            // Content is retrieved in batches of size, PageSize, and then returned in pages of subPageSize=PageSize/10 as long as the queue has >= PageSize elements.
            // This ensures that we have PageSize amount of lookahead
            // NOTE: We use RandomSplitInterleave to ensure we select a random set of the newer content. This ensure we at least look at newer content to see if it should be
            // evicted first due to having a high number of replicas
            foreach (var page in contentHashesWithInfo.RandomSplitInterleave().GetPages(pageSize))
            {
                var pageWithEffectiveLastAccessTimeResult = _localLocationStore.GetEffectiveLastAccessTimes(operationContext, page.SelectList(v => new ContentHashWithLastAccessTime(v.ContentHash, v.LastAccessTime)));
                IReadOnlyList <ContentHashWithLastAccessTimeAndReplicaCount> lastAccessTimes = pageWithEffectiveLastAccessTimeResult.ThrowIfFailure();

                for (int i = 0; i < page.Count; i++)
                {
                    priorityQueue.Push(lastAccessTimes[i]);
                }

                while (priorityQueue.Count >= pageSize)
                {
                    yield return(GetPriorityPage(priorityQueue, subPageSize));
                }
            }

            while (priorityQueue.Count != 0)
            {
                yield return(GetPriorityPage(priorityQueue, subPageSize));
            }
        }
        /// <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)));
        }