1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-07 07:56:12 +00:00

Add support for passing weight to apply to an individual ratelimit guard

This commit is contained in:
Jkorf 2025-01-07 08:35:06 +01:00
parent ff8759409b
commit 2b9fda985e
4 changed files with 50 additions and 12 deletions

View File

@ -154,6 +154,7 @@ namespace CryptoExchange.Net.Clients
/// <param name="cancellationToken">Cancellation token</param>
/// <param name="additionalHeaders">Additional headers for this request</param>
/// <param name="weight">Override the request weight for this request definition, for example when the weight depends on the parameters</param>
/// <param name="weightSingleLimiter">Specify the weight to apply to the individual rate limit guard for this request</param>
/// <returns></returns>
protected virtual Task<WebCallResult<T>> SendAsync<T>(
string baseAddress,
@ -161,7 +162,8 @@ namespace CryptoExchange.Net.Clients
ParameterCollection? parameters,
CancellationToken cancellationToken,
Dictionary<string, string>? additionalHeaders = null,
int? weight = null) where T : class
int? weight = null,
int? weightSingleLimiter = null) where T : class
{
var parameterPosition = definition.ParameterPosition ?? ParameterPositions[definition.Method];
return SendAsync<T>(
@ -171,7 +173,8 @@ namespace CryptoExchange.Net.Clients
parameterPosition == HttpMethodParameterPosition.InBody ? parameters : null,
cancellationToken,
additionalHeaders,
weight);
weight,
weightSingleLimiter);
}
/// <summary>
@ -185,6 +188,7 @@ namespace CryptoExchange.Net.Clients
/// <param name="cancellationToken">Cancellation token</param>
/// <param name="additionalHeaders">Additional headers for this request</param>
/// <param name="weight">Override the request weight for this request definition, for example when the weight depends on the parameters</param>
/// <param name="weightSingleLimiter">Specify the weight to apply to the individual rate limit guard for this request</param>
/// <returns></returns>
protected virtual async Task<WebCallResult<T>> SendAsync<T>(
string baseAddress,
@ -193,7 +197,8 @@ namespace CryptoExchange.Net.Clients
ParameterCollection? bodyParameters,
CancellationToken cancellationToken,
Dictionary<string, string>? additionalHeaders = null,
int? weight = null) where T : class
int? weight = null,
int? weightSingleLimiter = null) where T : class
{
string? cacheKey = null;
if (ShouldCache(definition))
@ -217,7 +222,7 @@ namespace CryptoExchange.Net.Clients
currentTry++;
var requestId = ExchangeHelpers.NextId();
var prepareResult = await PrepareAsync(requestId, baseAddress, definition, cancellationToken, additionalHeaders, weight).ConfigureAwait(false);
var prepareResult = await PrepareAsync(requestId, baseAddress, definition, cancellationToken, additionalHeaders, weight, weightSingleLimiter).ConfigureAwait(false);
if (!prepareResult)
return new WebCallResult<T>(prepareResult.Error!);
@ -258,6 +263,7 @@ namespace CryptoExchange.Net.Clients
/// <param name="cancellationToken">Cancellation token</param>
/// <param name="additionalHeaders">Additional headers for this request</param>
/// <param name="weight">Override the request weight for this request</param>
/// <param name="weightSingleLimiter">Specify the weight to apply to the individual rate limit guard for this request</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
protected virtual async Task<CallResult> PrepareAsync(
@ -266,10 +272,9 @@ namespace CryptoExchange.Net.Clients
RequestDefinition definition,
CancellationToken cancellationToken,
Dictionary<string, string>? additionalHeaders = null,
int? weight = null)
int? weight = null,
int? weightSingleLimiter = null)
{
var requestWeight = weight ?? definition.Weight;
// Time sync
if (definition.Authenticated)
{
@ -295,6 +300,7 @@ namespace CryptoExchange.Net.Clients
}
// Rate limiting
var requestWeight = weight ?? definition.Weight;
if (requestWeight != 0)
{
if (definition.RateLimitGate == null)
@ -316,7 +322,8 @@ namespace CryptoExchange.Net.Clients
if (ClientOptions.RateLimiterEnabled)
{
var limitResult = await definition.RateLimitGate.ProcessSingleAsync(_logger, requestId, definition.LimitGuard, RateLimitItemType.Request, definition, baseAddress, AuthenticationProvider?._credentials.Key, ClientOptions.RateLimitingBehaviour, cancellationToken).ConfigureAwait(false);
var singleRequestWeight = weightSingleLimiter ?? 1;
var limitResult = await definition.RateLimitGate.ProcessSingleAsync(_logger, requestId, definition.LimitGuard, RateLimitItemType.Request, definition, baseAddress, AuthenticationProvider?._credentials.Key, singleRequestWeight, ClientOptions.RateLimitingBehaviour, cancellationToken).ConfigureAwait(false);
if (!limitResult)
return new CallResult(limitResult.Error!);
}

View File

@ -58,9 +58,38 @@ namespace CryptoExchange.Net.Objects
HttpMethodParameterPosition? parameterPosition = null,
ArrayParametersSerialization? arraySerialization = null,
bool? preventCaching = null)
=> GetOrCreate(method + path, method, path, rateLimitGate, weight, authenticated, limitGuard, requestBodyFormat, parameterPosition, arraySerialization, preventCaching);
/// <summary>
/// Get a definition if it is already in the cache or create a new definition and add it to the cache
/// </summary>
/// <param name="identifier">Request identifier</param>
/// <param name="method">The HttpMethod</param>
/// <param name="path">Endpoint path</param>
/// <param name="rateLimitGate">The rate limit gate</param>
/// <param name="limitGuard">The rate limit guard for this specific endpoint</param>
/// <param name="weight">Request weight</param>
/// <param name="authenticated">Endpoint is authenticated</param>
/// <param name="requestBodyFormat">Request body format</param>
/// <param name="parameterPosition">Parameter position</param>
/// <param name="arraySerialization">Array serialization type</param>
/// <param name="preventCaching">Prevent request caching</param>
/// <returns></returns>
public RequestDefinition GetOrCreate(
string identifier,
HttpMethod method,
string path,
IRateLimitGate? rateLimitGate,
int weight,
bool authenticated,
IRateLimitGuard? limitGuard = null,
RequestBodyFormat? requestBodyFormat = null,
HttpMethodParameterPosition? parameterPosition = null,
ArrayParametersSerialization? arraySerialization = null,
bool? preventCaching = null)
{
if (!_definitions.TryGetValue(method + path, out var def))
if (!_definitions.TryGetValue(identifier, out var def))
{
def = new RequestDefinition(path, method)
{
@ -73,7 +102,7 @@ namespace CryptoExchange.Net.Objects
ParameterPosition = parameterPosition,
PreventCaching = preventCaching ?? false
};
_definitions.TryAdd(method + path, def);
_definitions.TryAdd(identifier, def);
}
return def;

View File

@ -68,8 +68,9 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
/// <param name="baseAddress">The host address</param>
/// <param name="apiKey">The API key</param>
/// <param name="behaviour">Behaviour when rate limit is hit</param>
/// <param name="requestWeight">The weight to apply to the limit guard</param>
/// <param name="ct">Cancelation token</param>
/// <returns>Error if RateLimitingBehaviour is Fail and rate limit is hit</returns>
Task<CallResult> ProcessSingleAsync(ILogger logger, int itemId, IRateLimitGuard guard, RateLimitItemType type, RequestDefinition definition, string baseAddress, string? apiKey, RateLimitingBehaviour behaviour, CancellationToken ct);
Task<CallResult> ProcessSingleAsync(ILogger logger, int itemId, IRateLimitGuard guard, RateLimitItemType type, RequestDefinition definition, string baseAddress, string? apiKey, int requestWeight, RateLimitingBehaviour behaviour, CancellationToken ct);
}
}

View File

@ -69,6 +69,7 @@ namespace CryptoExchange.Net.RateLimiting
RequestDefinition definition,
string host,
string? apiKey,
int requestWeight,
RateLimitingBehaviour rateLimitingBehaviour,
CancellationToken ct)
{
@ -77,7 +78,7 @@ namespace CryptoExchange.Net.RateLimiting
_waitingCount++;
try
{
return await CheckGuardsAsync(new IRateLimitGuard[] { guard }, logger, itemId, type, definition, host, apiKey, 1, rateLimitingBehaviour, ct).ConfigureAwait(false);
return await CheckGuardsAsync(new IRateLimitGuard[] { guard }, logger, itemId, type, definition, host, apiKey, requestWeight, rateLimitingBehaviour, ct).ConfigureAwait(false);
}
catch (TaskCanceledException)
{