/* Updates the the cached item with an incremented call-count. * Returns whether our incremented count puts us over the limit for the token * If so, boolean indicates whether we should short-circuit our conputation and return early. */ private bool CacheUpdate(ActionExecutingContext context, string id, LimitCounter oldCounter, string message) { LimitCounter updatedCounter = new LimitCounter(oldCounter.FirstCallTimestamp, oldCounter.Window, oldCounter.CallCount + 1, oldCounter.Limit); /* Cache will periodically prune itself, but if we happen to pull one that expired, we'll take care of it. */ if (oldCounter.FirstCallTimestamp + oldCounter.Window <= DateTimeOffset.UtcNow) { Cache.Remove(id); return(false); } /* Due to the nature of distributed or off-site storage, limits may be incremented past their theoretical max. * We account for this with a greater than check, rather than a strict equals check */ if (updatedCounter.CallCount > updatedCounter.Limit) { TimeSpan availableAt = (updatedCounter.FirstCallTimestamp + updatedCounter.Window) - DateTimeOffset.UtcNow; context.Result = new ContentResult { Content = message }; context.HttpContext.Response.StatusCode = 429; /* Too Many Requests */ context.HttpContext.Response.Headers.Add("Retry-After", availableAt.TotalSeconds.ToString()); return(true); } else { Cache.Set(id, updatedCounter, new MemoryCacheEntryOptions().SetAbsoluteExpiration(updatedCounter.FirstCallTimestamp + updatedCounter.Window)); return(false); } }
public void Increase_WhenLimitExceeds_ThrowsExceptionDuringWaitTime() { int nrOfRequests = 1; int allowedTime = 1; int secondsSuspended = 3; LimitCounter counter = new LimitCounter(nrOfRequests, TimeSpan.FromSeconds(allowedTime), TimeSpan.FromSeconds(secondsSuspended)); counter.Increase(); try { //t1 = DateTime.Now; counter.Increase(); //t2 = DateTime.Now; Assert.Fail("Did not throw exception after exceeding limit"); } catch (RateLimitExceededException) { } System.Threading.Thread.Sleep(1000); try { counter.Increase(); Assert.Fail("Did not throw exception after 1 second of waiting"); } catch (RateLimitExceededException) { } System.Threading.Thread.Sleep(1000); try { counter.Increase(); Assert.Fail("Did not throw exception after 2 seconds of waiting"); } catch (RateLimitExceededException) { } System.Threading.Thread.Sleep(985); try { //t3 = DateTime.Now; counter.Increase(); Assert.Fail("Did not throw exception after 3 seconds of waiting"); } catch (RateLimitExceededException) { } }
public virtual void CountAvailabilityCheck(DispatcherAvailability availability) { if (availability == DispatcherAvailability.NotChecked) { return; } //count check as additional message dispatched AvailableLimitCapacity--; LimitCounter.InsertTime(); }
public void Increase_WhenLimitDidNotExceed_AllowsAfterAllowedTime() { int nrOfRequests = 2; int allowedTime = 1; int secondsSuspended = 5; LimitCounter counter = new LimitCounter(nrOfRequests, TimeSpan.FromSeconds(allowedTime), TimeSpan.FromSeconds(secondsSuspended)); counter.Increase(); counter.Increase(); System.Threading.Thread.Sleep(1000); counter.Increase(); }
static void TestLimitCounter() { int nrOfRequests = 90000; int allowedTime = 100; int secondsSuspended = 1; LimitCounter counter = new LimitCounter(nrOfRequests, TimeSpan.FromSeconds(allowedTime), TimeSpan.FromSeconds(secondsSuspended)); List <System.Threading.Thread> threads = new List <System.Threading.Thread>(); for (int x = 0; x < 100; ++x) { var thread = new System.Threading.Thread(() => { var id = System.Threading.Thread.CurrentThread.ManagedThreadId; for (int y = 0; y < 1000; ++y) { try { counter.Increase(); Console.WriteLine(String.Format("T-{0}-{1}", id, y)); } catch (RateLimitExceededException e) { Console.WriteLine(String.Format("E{0}-{1}-{2}", id, y, e.Message)); System.Threading.Thread.Sleep(1000); } System.Threading.Thread.Yield(); } }); threads.Add(thread); } Console.WriteLine("Starting..."); foreach (var t in threads) { t.Start(); } while (threads.TrueForAll(p => p.IsAlive == false)) { System.Threading.Thread.Yield(); System.Threading.Thread.Sleep(1000); } }
//limitation methods public virtual void CountSendAttempt(SignalDispatch <TKey> dispatch, ProcessingResult result, DispatcherAvailability availability) { if (result == ProcessingResult.Success) { AvailableLimitCapacity--; LimitCounter.InsertTime(); Interrupter.Success(dispatch); } else if (result == ProcessingResult.Fail) { AvailableLimitCapacity--; LimitCounter.InsertTime(); Interrupter.Fail(dispatch, availability); } }
void UseLimitCounter() { LimitCounter limitCounter = new LimitCounter(); limitCounter.SetLimit(3); Debug.Log("at limit: " + limitCounter.AtLimit()); // "at limit: False" limitCounter.IncrementCounter();//already implemented in BaseLimitCounter // "Counter: 1" limitCounter.IncrementCounter(); // "Counter: 2" limitCounter.IncrementCounter(); // "Counter: 3" Debug.Log("at limit: " + limitCounter.AtLimit()); // "at limit: True" }
//IDisposable public virtual void Dispose() { if (Dispatcher != null) { Dispatcher.Dispose(); } if (LimitCounter != null) { LimitCounter.Dispose(); } if (Interrupter != null) { Interrupter.Dispose(); } }
/* Inserts the limit as a LimitCounter struct tied to the given id, and expiring when the Window lapses. * The boolean returned indictes whether this item was whitelisted, and therefore if we should * short-circuit our computation and return early. */ private bool InsertLimit(string id, RateLimit limit) { DateTimeOffset now = DateTimeOffset.UtcNow; /* Nulls in these spots mean this item is whitelisted. Add it to our whitelist. */ if (limit.Window == null || limit.Limit == null) { /* White-listing lasts one hour, so that if whitelisting status changes, server won't require a restart to clear the cache. */ WhiteList.Set(id, true, new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromHours(1))); return(true); } else { LimitCounter counter = new LimitCounter(now, limit.Window.Value, 1, limit.Limit); Cache.Set(id, counter, new MemoryCacheEntryOptions().SetAbsoluteExpiration(limit.Window.Value)); // Passed a null check above, value exists return(false); } }
public void Increase_WhenLimitExceeds_ThrowsException() { int nrOfRequests = 1; int allowedTime = 1; int secondsSuspended = 5; LimitCounter counter = new LimitCounter(nrOfRequests, TimeSpan.FromSeconds(allowedTime), TimeSpan.FromSeconds(secondsSuspended)); counter.Increase(); try { counter.Increase(); Assert.Fail("Did not throw exception"); } catch (RateLimitExceededException) { } }
public void Increase_WhenLimitExceeds_AllowsAfterWaitTime() { int nrOfRequests = 1; int allowedTime = 1; int secondsSuspended = 2; LimitCounter counter = new LimitCounter(nrOfRequests, TimeSpan.FromSeconds(allowedTime), TimeSpan.FromSeconds(secondsSuspended)); counter.Increase(); try { counter.Increase(); Assert.Fail("Did not throw first time"); } catch (RateLimitExceededException) { } System.Threading.Thread.Sleep(2000); counter.Increase(); }
/// <summary> /// Create Get Log state reply object /// </summary> /// <param name="reply">Raw 9 or 21-bit Get Log State reply</param> public LogState(byte[] reply) { if (!((9 == reply.Length) || (20 == reply.Length))) { throw new ArgumentOutOfRangeException("GetLogState replies must be 9 or 20 bytes in length"); } int offset = 0; _LimitCount = new LimitCounter(reply, offset); offset += 4; _SystemStat = new SystemStatus(reply, offset); offset += 4; if (20 == reply.Length) { _SetShelLifeBlock0 = new ShelfLifeBlock0(reply, offset); offset += 4; _SetShelLifeBlock1 = new ShelfLifeBlock1(reply, offset); offset += 4; _remainingShelfLife = GetRemainingShelfLife(reply, offset); offset += 3; } _StatFlags = new StatusFlags(reply[offset]); offset += 1; }
public virtual void SetRestrictionsDuration() { _nextLimitsEndUtc = LimitCounter.GetLimitsEndTimeUtc(); _nextTimeoutEndUtc = Interrupter.GetTimeoutEndUtc(); }
public virtual void SetLimitsCapacity() { AvailableLimitCapacity = LimitCounter.GetLimitCapacity(); }