//-----------------------------------------------------------------------------
//
// 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();
}
}
}
}