//----------------------------------------------------------------------------- // // Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. // A copy of the License is located at // // http://aws.amazon.com/apache2.0 // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. // //----------------------------------------------------------------------------- using Amazon.Runtime.Internal.Util; using System.Collections.Generic; using System.Linq; using System.Threading; namespace Amazon.XRay.Recorder.Core.Sampling { /// /// Cache sampling rules and quota retrieved by /// and . It will not return anything if the cache expires. /// public class RuleCache { private static readonly Logger _logger = Logger.GetLogger(typeof(RuleCache)); private IDictionary _cache; private readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim(); /// /// Stores timestamp for the last refreshed cache. /// public TimeStamp LastUpdated { get; set; } /// /// Cache expiry TTL. /// public const int TTL = 60 * 60; // cache expires 1 hour after the refresh (in sec) public RuleCache() { _cache = new Dictionary(); LastUpdated = new TimeStamp(); } /// /// Returns matched rule for the given . /// /// Instance of . /// Current time. /// Instance of . public SamplingRule GetMatchedRule(SamplingInput input, TimeStamp time) { if (IsExpired(time)) { return null; } SamplingRule matchedRule = null; _cacheLock.EnterReadLock(); try { foreach (var item in _cache) { var rule = item.Value; if (matchedRule != null) { break; } if (rule.Match(input) || rule.IsDefault()) { matchedRule = rule; } } } finally { _cacheLock.ExitReadLock(); } return matchedRule; } private bool IsExpired(TimeStamp current) { if (LastUpdated.Time == 0) // The cache is treated as expired if never loaded { return true; } return current.Time > LastUpdated.Time + TTL; } /// /// Returns list of rules present in the cache. /// /// List of . public IList GetRules() { _cacheLock.EnterReadLock(); try { return _cache.Values.ToList(); } finally { _cacheLock.ExitReadLock(); } } /// /// Updates Targets in the cache /// /// Targets returned by GetSamplingTargets. public void LoadTargets(IList targets) { _cacheLock.EnterReadLock(); try { TimeStamp now = TimeStamp.CurrentTime(); foreach (var t in targets) { if (_cache.TryGetValue(t.RuleName, out SamplingRule rule)) { rule.Reservior.LoadQuota(t, now); rule.SetRate(t.FixedRate); } } } finally { _cacheLock.ExitReadLock(); } } /// /// Adds new rules to cache, clearing old rules not present in the newRules. /// /// Rules returned by GetSampling API. public void LoadRules(List newRules) { IDictionary oldRules; IDictionary tempCache = new Dictionary(); newRules.Sort((x, y) => x.CompareTo(y)); // Sort by priority and rule name _cacheLock.EnterReadLock(); try { oldRules = new Dictionary(_cache); } finally { _cacheLock.ExitReadLock(); } foreach (var newRule in newRules) { var key = newRule.RuleName; tempCache[key] = newRule; if (oldRules.TryGetValue(key, out SamplingRule oldRule)) { newRule.Merge(oldRule); } } _cacheLock.EnterWriteLock(); try { _cache = tempCache; // update cache } finally { _cacheLock.ExitWriteLock(); } } } }