示例#1
0
        private static void doShrinkCycle()
        {
            var baselines = CalculateBaselines();

            // sort all caches that have existed and not been grown for a full shrink cycle by score ascending
            // score = (1 + impact/baselineImpact) / (1 + cost/baselineCost)
            // calculate forever and this cycle and take the higher (better) answer
            var shrinkCandidates = (from hitInfo in Nrdo.GetCacheHitInfoUnsorted()
                                    where hitInfo.Cache.IsEnabled && hitInfo.lastShrinkCycleStats != null && (hitInfo.lastGrownStats == null || hitInfo.lastGrownStats.LatestGlobalStats.LatestOperationStamp < hitInfo.lastShrinkCycleStats.LatestGlobalStats.LatestOperationStamp)
                                    let stats = hitInfo.CacheStats.ToNow()
                                                let thisCycle = stats.Since(hitInfo.lastShrinkCycleStats)
                                                                let sinceLastShrink = stats.Since(hitInfo.lastShrunkStats)
                                                                                      let globalScore = (1 + Portion.SafeRatio(stats.Impact, baselines.Baseline.Impact)) / (1 + sinceLastShrink.CumulativeCost / baselines.Baseline.Cost)
                                                                                                        let cycleScore = (1 + Portion.SafeRatio(thisCycle.Impact, baselines.CycleBaseline.Impact)) - (1 + thisCycle.CumulativeCost / baselines.CycleBaseline.Cost)
                                                                                                                         let score = Portion.Max(globalScore, cycleScore)
                                                                                                                                     orderby score ascending
                                                                                                                                     select new { hitInfo, score }).ToList();

            // shrink the first 1/4 of the caches in that list as long as their score is strictly < 100%
            shrinkCandidates = shrinkCandidates.Take(shrinkCandidates.Count / 4).Where(c => c.score < Portion.Complete).ToList();

            foreach (var candidate in shrinkCandidates)
            {
                if (candidate.hitInfo.IsList)
                {
                    var listCache = (IListCache)candidate.hitInfo.Cache;
                    if (listCache.Capacity == 0 && listCache.ItemCapacity == 1)
                    {
                        listCache.Clear();
                    }
                    else if (listCache.ItemCapacity == 0 && listCache.Capacity == 1)
                    {
                        listCache.ItemCapacity = (int)candidate.hitInfo.CacheStats.AverageResultItems + 1;
                        listCache.Capacity     = 0;
                    }
                    else
                    {
                        listCache.Capacity     = listCache.Capacity * 3 / 4;
                        listCache.ItemCapacity = listCache.ItemCapacity * 3 / 4;
                    }
                }
                else
                {
                    if (candidate.hitInfo.Cache.Capacity <= 1)
                    {
                        candidate.hitInfo.Cache.Clear();
                    }
                    else
                    {
                        candidate.hitInfo.Cache.Capacity = candidate.hitInfo.Cache.Capacity * 3 / 4;
                    }
                }
                candidate.hitInfo.shrinkCount++;
                candidate.hitInfo.lastShrunkStats = candidate.hitInfo.CacheStats.ToNow();
            }

            // store last shrink cycle info for each individual cache
            lastShrinkCycleStats = globalStats.ToNow();
            foreach (var hitInfo in Nrdo.GetCacheHitInfoUnsorted())
            {
                hitInfo.lastShrinkCycleStats = hitInfo.CacheStats.ToNow();
            }
        }
示例#2
0
        private static void doGrowPulse()
        {
            // - Grow caches where ImpactGainHybrid is "high" both as a percentage of total DB time and in
            //   absolute value as measured over the period *since this cache was last rebalanced*.
            //   Algorithm:
            //   - Take all caches where ImpactGainHybrid[since last grown] is greater than zero
            //     - Sort by ImpactGainHybrid[since last grown]/Nrdo.TotalQueryTime[since last grown]
            //   - Take all caches that existed at last cycle, haven't grown since then, and where ImpactGainHybrid[this cycle] is greater than zero
            //     (using this pulse would be better but we don't have this cache's stats for that)
            //     - Sort by ImpactGainHybrid[this cycle]/Nrdo.TotalQueryTime[this cycle]
            //   - Grow any caches that are in the top 50% of either list and also have ImpactGainHybrid[since last grown] > CacheGrowThreshold
            var growCandidates = (from hitInfo in Nrdo.GetCacheHitInfoUnsorted()
                                  let stats = hitInfo.CacheStats.ToNow()
                                              let sinceGrow = stats.Since(hitInfo.lastGrownStats)
                                                              let gainSinceGrow = sinceGrow.PotentialImpactGainHybrid
                                                                                  where gainSinceGrow > TimeSpan.Zero
                                                                                  let ratioSinceGrow = Portion.SafeRatio(gainSinceGrow, sinceGrow.LatestGlobalStats.TotalQueryTime)

                                                                                                       let thisCycle = hitInfo.lastShrinkCycleStats == null ||
                                                                                                                       (hitInfo.lastGrownStats != null && hitInfo.lastGrownStats.LatestGlobalStats.LatestOperationStamp >= hitInfo.lastShrinkCycleStats.LatestGlobalStats.LatestOperationStamp)
                                      ? null : stats.Since(hitInfo.lastShrinkCycleStats)
                                                                                                                       let gainThisCycle = thisCycle == null ? TimeSpan.Zero : thisCycle.PotentialImpactGainHybrid
                                                                                                                                           let ratioThisCycle = thisCycle == null ? Portion.Zero : Portion.SafeRatio(gainThisCycle, thisCycle.LatestGlobalStats.TotalQueryTime)
                                                                                                                                                                select new
            {
                hitInfo,
                stats,
                sinceGrow,
                gainSinceGrow,
                ratioSinceGrow,
                thisCycle,
                gainThisCycle,
                ratioThisCycle
            }).ToList();

            if (!growCandidates.Any())
            {
                return;
            }

            var sinceGrowRatioLimit = growCandidates.OrderBy(c => c.ratioSinceGrow).ElementAt(growCandidates.Count / 2).ratioSinceGrow;

            var cycleCandidates = growCandidates.Where(c => c.gainThisCycle > TimeSpan.Zero).ToList();
            var cycleRatioLimit = cycleCandidates.Any() ? cycleCandidates.OrderBy(c => c.ratioThisCycle).ElementAt(cycleCandidates.Count / 2).ratioThisCycle : Portion.Complete;

            var growCaches = from cache in growCandidates
                             where cache.gainSinceGrow > CacheGrowThreshold &&
                             (cache.ratioSinceGrow >= sinceGrowRatioLimit ||
                              (cache.gainThisCycle > TimeSpan.Zero && cache.ratioThisCycle >= cycleRatioLimit))
                             select cache;

            foreach (var candidate in growCaches)
            {
                //  - Calculate GrowthFactor: 1 + Max(NonHitsOverCapacity / TotalQueries, 0.2)
                var growthFactor = 1 + Math.Max((double)candidate.sinceGrow.NonHitsOverCapacity / candidate.sinceGrow.TotalQueries, 0.2);

                //  - For non-list caches, Capacity = Max(Capacity * GrowthFactor, Capacity + 2)
                if (!candidate.hitInfo.IsList)
                {
                    var targetCapacity = Math.Max((int)(candidate.hitInfo.Cache.Capacity * growthFactor), candidate.hitInfo.Cache.Capacity + 2);
                    candidate.hitInfo.Cache.Capacity = Math.Min(targetCapacity, Nrdo.MaxCacheCapacity);
                }
                else
                {
                    var listSinceGrow = candidate.sinceGrow.ListStats;
                    var listCache     = (IListCache)candidate.hitInfo.Cache;

                    //  - For list caches,
                    //    - Calculate PotentialCost: Max(PeakItemCount, ItemCapacity, Capacity * WeightedResultItems)
                    var potentialCost = Math.Max(Math.Max(listCache.ItemCapacity, listCache.PeakItemCount), listCache.Capacity * candidate.sinceGrow.WeightedResultItems);

                    //    - Calculate TargetCost = GrowthFactor * Max(PotentialCost, WeightedResultItems)
                    var targetCost = Math.Min(growthFactor * Math.Max(potentialCost, candidate.hitInfo.CacheStats.WeightedResultItems), Nrdo.MaxCacheCapacity);

                    //    - Calculate TargetCapacity = Max(TargetCost / WeightedResultItems, Capacity + 1)
                    var targetCapacity = Math.Max((int)(targetCost / candidate.hitInfo.CacheStats.WeightedResultItems), listCache.Capacity + 1);

                    //      - If ItemCapacity < TargetCapacity * AvgResultItems or (Capacity = 0 and Skipped = 0)
                    if (listCache.ItemCapacity < targetCapacity * candidate.hitInfo.CacheStats.AverageResultItems ||
                        (listCache.Capacity == 0 && listSinceGrow.Skipped == 0))
                    {
                        //        - Set ItemCapacity = Max(TargetCost, ItemCapacity + 2)
                        listCache.ItemCapacity = Math.Max((int)targetCost, listCache.ItemCapacity + 2);
                    }
                    else
                    {
                        //        - Set Capacity = TargetCapacity and ItemCapacity = TargetCapacity * AvgResultItems
                        listCache.Capacity     = targetCapacity;
                        listCache.ItemCapacity = (int)(targetCapacity * candidate.hitInfo.CacheStats.AverageResultItems);
                    }
                }
                candidate.hitInfo.growCount++;
                candidate.hitInfo.lastGrownStats = candidate.hitInfo.CacheStats.ToNow();
            }
        }