//-----------------------------------------------------------------------------
//
// 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.Threading;
namespace Amazon.XRay.Recorder.Core.Sampling
{
///
/// Thread safe reservior which holds fixed sampling quota, borrowed count and TTL.
///
public class Reservior
{
private TimeStamp _thisSec = new TimeStamp();
private TimeStamp _refereshedAt = new TimeStamp();
private int _takenThisSec;
private int _borrowedThisSec;
private readonly int _defaultInterval = 10; // 10 seconds
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private static readonly Logger _logger = Logger.GetLogger(typeof(Reservior));
public TimeStamp TTL { get; set; }
public int? Quota { get; set; }
public int? Interval { get; set; }
internal Reservior()
{
Quota = null;
TTL = null;
Interval = _defaultInterval;
_refereshedAt = TimeStamp.CurrentTime();
}
///
/// If quota is valid, is either take or no else 1 request/sec is borrowed.
///
/// Current timestamp.
/// If true, and quota not valid, single request is borrowed in the current sec for the given .
/// The reservior decision.
internal ReserviorDecision BorrowOrTake(TimeStamp current, bool canBorrow)
{
_lock.EnterWriteLock();
try
{
CalibrateThisSec(current);
// Don't borrow if the quota is available and fresh
if (Quota != null && Quota >= 0 && TTL != null && TTL.Time >= current.Time)
{
if(_takenThisSec >= Quota)
{
return ReserviorDecision.No;
}
_takenThisSec += 1;
return ReserviorDecision.Take;
}
if (canBorrow) // Otherwise try to borrow if the quota is not present or expired.
{
if(_borrowedThisSec >= 1)
{
return ReserviorDecision.No;
}
_borrowedThisSec += 1;
return ReserviorDecision.Borrow;
}
return ReserviorDecision.No;
}
finally
{
_lock.ExitWriteLock();
}
}
private void CalibrateThisSec(TimeStamp current) // caller has write lock
{
if (_thisSec.Time != current.Time)
{
_takenThisSec = 0;
_borrowedThisSec = 0;
_thisSec = current;
}
}
///
/// Load quota, ttl and interval into current reservior.
///
/// Instance of .
/// Current timestamp.
internal void LoadQuota(Target t, TimeStamp now)
{
_lock.EnterWriteLock();
try
{
if (t.ReserviorQuota != null)
{
Quota = t.ReserviorQuota;
}
if (t.TTL != null)
{
TTL = t.TTL;
}
if (t.Interval != null)
{
Interval = t.Interval;
}
_refereshedAt = now;
}
finally
{
_lock.ExitWriteLock();
}
}
internal bool ShouldReport(TimeStamp now)
{
_lock.EnterReadLock();
try
{
return now.IsAfter(_refereshedAt.PlusSeconds(Interval));
}
finally
{
_lock.ExitReadLock();
}
}
internal void CopyFrom(Reservior r)
{
_lock.EnterWriteLock();
try
{
if (r.Quota != null)
{
Quota = r.Quota.Value;
}
if (r.TTL != null)
{
TTL = new TimeStamp();
TTL.CopyFrom(r.TTL);
}
if (r.Interval != null)
{
Interval = r.Interval.Value;
}
if (r._refereshedAt != null)
{
_refereshedAt = new TimeStamp();
_refereshedAt.CopyFrom(r._refereshedAt);
}
}
finally
{
_lock.ExitWriteLock();
}
}
}
}