From be681150992ca91e2500fb15b815e57f3668855b Mon Sep 17 00:00:00 2001 From: JKorf Date: Tue, 25 Jun 2024 16:14:09 +0200 Subject: [PATCH] Fix for ratelimiting possibly creating negative waits --- .../RateLimiting/Trackers/DecayWindowTracker.cs | 5 ++++- .../Trackers/FixedAfterStartWindowTracker.cs | 5 ++++- .../RateLimiting/Trackers/FixedWindowTracker.cs | 5 ++++- .../RateLimiting/Trackers/SlidingWindowTracker.cs | 10 +++++++++- .../Testing/Comparers/JsonNetComparer.cs | 4 ++++ .../Testing/Comparers/SystemTextJsonComparer.cs | 4 ++++ 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/CryptoExchange.Net/RateLimiting/Trackers/DecayWindowTracker.cs b/CryptoExchange.Net/RateLimiting/Trackers/DecayWindowTracker.cs index d0f6452..20ef53c 100644 --- a/CryptoExchange.Net/RateLimiting/Trackers/DecayWindowTracker.cs +++ b/CryptoExchange.Net/RateLimiting/Trackers/DecayWindowTracker.cs @@ -80,7 +80,10 @@ namespace CryptoExchange.Net.RateLimiting.Trackers private TimeSpan DetermineWaitTime(int requestWeight) { var weightToRemove = Math.Max(Current - (Limit - requestWeight), 0); - return TimeSpan.FromMilliseconds(Math.Ceiling(weightToRemove / DecreaseRate) * TimePeriod.TotalMilliseconds); + var result = TimeSpan.FromMilliseconds(Math.Ceiling(weightToRemove / DecreaseRate) * TimePeriod.TotalMilliseconds); + if (result < TimeSpan.Zero) + return TimeSpan.Zero; + return result; } } } diff --git a/CryptoExchange.Net/RateLimiting/Trackers/FixedAfterStartWindowTracker.cs b/CryptoExchange.Net/RateLimiting/Trackers/FixedAfterStartWindowTracker.cs index 401763f..cbca4e8 100644 --- a/CryptoExchange.Net/RateLimiting/Trackers/FixedAfterStartWindowTracker.cs +++ b/CryptoExchange.Net/RateLimiting/Trackers/FixedAfterStartWindowTracker.cs @@ -97,7 +97,10 @@ namespace CryptoExchange.Net.RateLimiting.Trackers private TimeSpan DetermineWaitTime() { var checkTime = DateTime.UtcNow; - return (_nextReset!.Value - checkTime) + _fixedWindowBuffer; + var result = (_nextReset!.Value - checkTime) + _fixedWindowBuffer; + if (result < TimeSpan.Zero) + return TimeSpan.Zero; + return result; } } } diff --git a/CryptoExchange.Net/RateLimiting/Trackers/FixedWindowTracker.cs b/CryptoExchange.Net/RateLimiting/Trackers/FixedWindowTracker.cs index 534eaa6..61ddcba 100644 --- a/CryptoExchange.Net/RateLimiting/Trackers/FixedWindowTracker.cs +++ b/CryptoExchange.Net/RateLimiting/Trackers/FixedWindowTracker.cs @@ -93,7 +93,10 @@ namespace CryptoExchange.Net.RateLimiting.Trackers var checkTime = DateTime.UtcNow; var startCurrentWindow = checkTime.AddTicks(-(checkTime.Ticks % TimePeriod.Ticks)); var wait = startCurrentWindow.Add(TimePeriod) - checkTime; - return wait.Add(_fixedWindowBuffer); + var result = wait.Add(_fixedWindowBuffer); + if (result < TimeSpan.Zero) + return TimeSpan.Zero; + return result; } } } diff --git a/CryptoExchange.Net/RateLimiting/Trackers/SlidingWindowTracker.cs b/CryptoExchange.Net/RateLimiting/Trackers/SlidingWindowTracker.cs index e7f1514..bb8446f 100644 --- a/CryptoExchange.Net/RateLimiting/Trackers/SlidingWindowTracker.cs +++ b/CryptoExchange.Net/RateLimiting/Trackers/SlidingWindowTracker.cs @@ -16,6 +16,11 @@ namespace CryptoExchange.Net.RateLimiting.Trackers private readonly List _entries; private int _currentWeight = 0; + /// + /// Additional wait time to apply to account for fluctuating request times + /// + private static readonly TimeSpan _slidingWindowBuffer = TimeSpan.FromMilliseconds(1000); + public SlidingWindowTracker(int limit, TimeSpan period) { Limit = limit; @@ -89,7 +94,10 @@ namespace CryptoExchange.Net.RateLimiting.Trackers removedWeight += entry.Weight; if (removedWeight >= weightToRemove) { - return entry.Timestamp + TimePeriod - DateTime.UtcNow; + var result = entry.Timestamp + TimePeriod + _slidingWindowBuffer - DateTime.UtcNow; + if (result < TimeSpan.Zero) + return TimeSpan.Zero; + return result; } } diff --git a/CryptoExchange.Net/Testing/Comparers/JsonNetComparer.cs b/CryptoExchange.Net/Testing/Comparers/JsonNetComparer.cs index 4e72951..30c82da 100644 --- a/CryptoExchange.Net/Testing/Comparers/JsonNetComparer.cs +++ b/CryptoExchange.Net/Testing/Comparers/JsonNetComparer.cs @@ -80,6 +80,10 @@ namespace CryptoExchange.Net.Testing.Comparers else if (jObj.Type == JTokenType.Array) { var resultObj = enumerator.Current; + if (resultObj is string) + // string list + continue; + var resultProps = resultObj.GetType().GetProperties().Select(p => (p, p.GetCustomAttributes(typeof(ArrayPropertyAttribute), true).Cast().SingleOrDefault())); var arrayConverterProperty = resultObj.GetType().GetCustomAttributes(typeof(JsonConverterAttribute), true).FirstOrDefault(); var jsonConverter = ((JsonConverterAttribute)arrayConverterProperty!).ConverterType; diff --git a/CryptoExchange.Net/Testing/Comparers/SystemTextJsonComparer.cs b/CryptoExchange.Net/Testing/Comparers/SystemTextJsonComparer.cs index ae87c81..728e898 100644 --- a/CryptoExchange.Net/Testing/Comparers/SystemTextJsonComparer.cs +++ b/CryptoExchange.Net/Testing/Comparers/SystemTextJsonComparer.cs @@ -82,6 +82,10 @@ namespace CryptoExchange.Net.Testing.Comparers else if (jObj.Type == JTokenType.Array) { var resultObj = enumerator.Current; + if (resultObj is string) + // string list + continue; + var resultProps = resultObj.GetType().GetProperties().Select(p => (p, p.GetCustomAttributes(typeof(ArrayPropertyAttribute), true).Cast().SingleOrDefault())); var arrayConverterProperty = resultObj.GetType().GetCustomAttributes(typeof(JsonConverterAttribute), true).FirstOrDefault(); var jsonConverter = ((JsonConverterAttribute)arrayConverterProperty!).ConverterType;