/// <summary> /// Asynchronously obtains a grant of <paramref name="requestedBytes"/> for the requesting <paramref name="username"/>. /// </summary> /// <remarks> /// This operation completes when any number of bytes can be granted. The amount returned may be smaller than the /// requested amount. /// </remarks> /// <param name="username">The username of the requesting user.</param> /// <param name="requestedBytes">The number of requested bytes.</param> /// <param name="cancellationToken">The token to monitor for cancellation.</param> /// <returns>The operation context, including the number of bytes granted.</returns> public Task <int> GetBytesAsync(string username, int requestedBytes, CancellationToken cancellationToken) { var group = Users.GetGroup(username); var bucket = TokenBuckets.GetValueOrDefault(group ?? string.Empty, TokenBuckets[Application.DefaultGroup]); return(bucket.GetAsync(requestedBytes, cancellationToken)); }
public static async Task <ITokenBucket> GetTokenBucketAsync(CustomerThrottle customerThrottle) { return(await TokenBuckets.BucketWithFixedIntervalRefillStrategy( capacity : (long)customerThrottle.MaximumRequestsPerSecond, refillTokens : (long)customerThrottle.MaximumRequestsPerSecond, period : oneSecond).ConfigureAwait(false)); }
public void RateLimitTests() { const int totalConsumes = 500; const int refillRate = 40; var tokenBucket = TokenBuckets.Construct() .WithCapacity(refillRate) .WithYieldingSleepStrategy() .WithFixedIntervalRefillStrategy(1, TimeSpan.FromMilliseconds(1000d / refillRate)) .Build(); var sw = new Stopwatch(); sw.Start(); for (var i = 0; i < totalConsumes; i++) { if (i % 3 == 0) { Thread.Sleep(1000 / refillRate * 2); } tokenBucket.Consume(); } sw.Stop(); Assert.That(totalConsumes / (sw.Elapsed.TotalSeconds + 1), Is.EqualTo(refillRate).Within(0.1)); }
public void SetUp() { _refillStrategy = new MockRefillStrategy(); _sleepStrategy = new Mock <ISleepStrategy>(); _bucket = TokenBuckets.Construct() .WithCapacity(Capacity) .WithRefillStrategy(_refillStrategy) .WithSleepStrategy(_sleepStrategy.Object) .Build(); }
/// <summary> /// Returns wasted bytes for redistribution. /// </summary> /// <param name="username">The username of the user that generated the waste.</param> /// <param name="attemptedBytes">The number of bytes that were attempted to be transferred.</param> /// <param name="grantedBytes">The number of bytes granted by all governors in the system.</param> /// <param name="actualBytes">The actual number of bytes transferred.</param> public void ReturnBytes(string username, int attemptedBytes, int grantedBytes, int actualBytes) { var waste = Math.Max(0, grantedBytes - actualBytes); if (waste == 0) { return; } var group = Users.GetGroup(username); var bucket = TokenBuckets.GetValueOrDefault(group ?? string.Empty, TokenBuckets[Application.DefaultGroup]); // we don't have enough information to tell whether grantedBytes was reduced by the global limiter within // Soulseek.NET, so we just return the bytes that we know for sure that were wasted, which is grantedBytes - actualBytes. // example: we grant 1000 bytes. Soulseek.NET grants only 500. 250 bytes are written. ideally we would return 750 // bytes, but instead we return 250. this discrepancy doesn't really matter because Soulseek.NET is the constraint in // this scenario and the additional tokens we would return would never be used. bucket.Return(waste); }
static void Main(string[] args) { // Create a token bucket with a capacity of 1 token that refills at a fixed interval of 1 token/sec. ITokenBucket bucket = TokenBuckets.Construct() .WithCapacity(100) .WithFixedIntervalRefillStrategy(100, TimeSpan.FromSeconds(1)) .Build(); // ... while (true) { // Consume a token from the token bucket. If a token is not available this method will block until // the refill strategy adds one to the bucket. bucket.Consume(1); Poll(); } }
public void Add(Host host, LimitRule rule) { ITokenBucket ulBucket = null; ITokenBucket dlBucket = null; Remove(host); host.SetLimitRule(rule); if (rule.IsUploadLimited && !rule.IsUploadBlocked) { ulBucket = TokenBuckets.Construct() .WithCapacity(rule.UploadRate.Value + (rule.UploadBurst ?? NetworkUtilities.GetDefaultBurst(rule.UploadRate.Value))) .WithFixedIntervalRefillStrategy(rule.UploadRate.Value, TimeSpan.FromSeconds(1)) .Build(); } if (rule.IsDownloadLimited && !rule.IsDownloadBlocked) { dlBucket = TokenBuckets.Construct() .WithCapacity(rule.DownloadRate.Value + (rule.DownloadBurst ?? NetworkUtilities.GetDefaultBurst(rule.DownloadRate.Value))) .WithFixedIntervalRefillStrategy(rule.DownloadRate.Value, TimeSpan.FromSeconds(1)) .Build(); } if (ulBucket == null && dlBucket == null && !host.LimitRule.IsUploadBlocked && !host.LimitRule.IsDownloadBlocked) { return; } var buckets = new Tuple <ITokenBucket, ITokenBucket>(ulBucket, dlBucket); lock (_hostBucketDictLock) lock (_ipHostDict) lock (_bandwidthUploadDictLock) lock (_bandwidthDownloadDictLock) { _hostBucketDict.Add(host, buckets); _ipHostDict.Add(IPAddress.Parse(host.IpAddress.ToString()), host); _bandwidthUploadDict.Add(host, 0); _bandwidthDownloadDict.Add(host, 0); } }
public async Task RateLimitTests() { const int totalConsumes = 500; const int refillRate = 40; //initial capacity 40 //add 1 token to the bucket ~ every 25ms var tokenBucket = await TokenBuckets.BucketWithFixedIntervalRefillStrategy(refillRate, 1, TimeSpan.FromMilliseconds(1000d / refillRate)) .ConfigureAwait(false); var sw = new Stopwatch(); sw.Start(); for (var i = 0; i < totalConsumes; i++) { await tokenBucket.WaitConsumeAsync().ConfigureAwait(false); } sw.Stop(); // tokens consumed / time = refillRate Assert.That(totalConsumes / (sw.Elapsed.TotalSeconds + 1), Is.EqualTo(refillRate).Within(0.2)); }
public async Task SetUp() { _refillStrategy = new MockRefillStrategy(); _bucket = await TokenBuckets.BucketWithRefillStrategy(Capacity, _refillStrategy).ConfigureAwait(false); }