Browse Source

Add global limit and a way to change gateway limits

pull/1537/head
Paulo 5 years ago
parent
commit
491df1c206
8 changed files with 108 additions and 10 deletions
  1. +23
    -0
      src/Discord.Net.Rest/Entities/Gateway/GatewayLimit.cs
  2. +29
    -0
      src/Discord.Net.Rest/Entities/Gateway/GatewayLimits.cs
  3. +21
    -9
      src/Discord.Net.Rest/Net/Queue/GatewayBucket.cs
  4. +14
    -1
      src/Discord.Net.Rest/Net/Queue/RequestQueue.cs
  5. +9
    -0
      src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs
  6. +1
    -0
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  7. +3
    -0
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  8. +8
    -0
      src/Discord.Net.WebSocket/DiscordSocketConfig.cs

+ 23
- 0
src/Discord.Net.Rest/Entities/Gateway/GatewayLimit.cs View File

@@ -0,0 +1,23 @@
namespace Discord.Rest
{
/// <summary>
/// Represents the limits for a gateway request.
/// </summary>
public struct GatewayLimit
{
/// <summary>
/// The maximum amount of this type of request in a time window, that is set by <see cref="Seconds"/>.
/// </summary>
public int Count { get; set; }
/// <summary>
/// The amount of seconds until the rate limiter resets the remaining requests <see cref="Count"/>.
/// </summary>
public int Seconds { get; set; }

internal GatewayLimit(int count, int seconds)
{
Count = count;
Seconds = seconds;
}
}
}

+ 29
- 0
src/Discord.Net.Rest/Entities/Gateway/GatewayLimits.cs View File

@@ -0,0 +1,29 @@
namespace Discord.Rest
{
/// <summary>
/// Contains the rate limits for the gateway.
/// </summary>
public class GatewayLimits
{
/// <summary>
/// Gets or sets the global limits for the gateway rate limiter.
/// </summary>
/// <remarks>
/// It includes all the other limits, like Identify.
/// </remarks>
public GatewayLimit Global { get; set; }
/// <summary>
/// Gets or sets the limits of Identify requests.
/// </summary>
public GatewayLimit Identify { get; set; }

public GatewayLimits()
{
Global = new GatewayLimit(120, 60);
Identify = new GatewayLimit(1, 5);
}

internal static GatewayLimits GetOrCreate(GatewayLimits limits)
=> limits ?? new GatewayLimits();
}
}

+ 21
- 9
src/Discord.Net.Rest/Net/Queue/GatewayBucket.cs View File

@@ -1,3 +1,4 @@
using Discord.Rest;
using System.Collections.Immutable; using System.Collections.Immutable;


namespace Discord.Net.Queue namespace Discord.Net.Queue
@@ -9,15 +10,29 @@ namespace Discord.Net.Queue
} }
internal struct GatewayBucket internal struct GatewayBucket
{ {
private static readonly ImmutableDictionary<GatewayBucketType, GatewayBucket> DefsByType;
private static readonly ImmutableDictionary<string, GatewayBucket> DefsById;
private static ImmutableDictionary<GatewayBucketType, GatewayBucket> DefsByType;
private static ImmutableDictionary<string, GatewayBucket> DefsById;


static GatewayBucket() static GatewayBucket()
{ {
SetLimits(GatewayLimits.GetOrCreate(null));
}

public static GatewayBucket Get(GatewayBucketType type) => DefsByType[type];
public static GatewayBucket Get(string id) => DefsById[id];

public static void SetLimits(GatewayLimits limits)
{
limits = GatewayLimits.GetOrCreate(limits);
Preconditions.GreaterThan(limits.Global.Count, 0, nameof(limits.Global.Count), "Global count must be greater than zero.");
Preconditions.GreaterThan(limits.Global.Seconds, 0, nameof(limits.Global.Seconds), "Global seconds must be greater than zero.");
Preconditions.GreaterThan(limits.Identify.Count, 0, nameof(limits.Identify.Count), "Identify count must be greater than zero.");
Preconditions.GreaterThan(limits.Identify.Seconds, 0, nameof(limits.Identify.Seconds), "Identify seconds must be greater than zero.");

var buckets = new[] var buckets = new[]
{ {
new GatewayBucket(GatewayBucketType.Unbucketed, "<gateway-unbucketed>", 120, 60),
new GatewayBucket(GatewayBucketType.Identify, "<gateway-identify>", 1, 5)
new GatewayBucket(GatewayBucketType.Unbucketed, "<gateway-unbucketed>", limits.Global.Count, limits.Global.Seconds),
new GatewayBucket(GatewayBucketType.Identify, "<gateway-identify>", limits.Identify.Count, limits.Identify.Seconds)
}; };


var builder = ImmutableDictionary.CreateBuilder<GatewayBucketType, GatewayBucket>(); var builder = ImmutableDictionary.CreateBuilder<GatewayBucketType, GatewayBucket>();
@@ -31,13 +46,10 @@ namespace Discord.Net.Queue
DefsById = builder2.ToImmutable(); DefsById = builder2.ToImmutable();
} }


public static GatewayBucket Get(GatewayBucketType type) => DefsByType[type];
public static GatewayBucket Get(string id) => DefsById[id];

public GatewayBucketType Type { get; } public GatewayBucketType Type { get; }
public string Id { get; } public string Id { get; }
public int WindowCount { get; }
public int WindowSeconds { get; }
public int WindowCount { get; set; }
public int WindowSeconds { get; set; }


public GatewayBucket(GatewayBucketType type, string id, int count, int seconds) public GatewayBucket(GatewayBucketType type, string id, int count, int seconds)
{ {


+ 14
- 1
src/Discord.Net.Rest/Net/Queue/RequestQueue.cs View File

@@ -103,7 +103,7 @@ namespace Discord.Net.Queue
createdTokenSource?.Dispose(); createdTokenSource?.Dispose();
} }


internal async Task EnterGlobalAsync(int id, IRequest request)
internal async Task EnterGlobalAsync(int id, RestRequest request)
{ {
int millis = (int)Math.Ceiling((_waitUntil - DateTimeOffset.UtcNow).TotalMilliseconds); int millis = (int)Math.Ceiling((_waitUntil - DateTimeOffset.UtcNow).TotalMilliseconds);
if (millis > 0) if (millis > 0)
@@ -118,6 +118,19 @@ namespace Discord.Net.Queue
{ {
_waitUntil = DateTimeOffset.UtcNow.AddMilliseconds(info.RetryAfter.Value + (info.Lag?.TotalMilliseconds ?? 0.0)); _waitUntil = DateTimeOffset.UtcNow.AddMilliseconds(info.RetryAfter.Value + (info.Lag?.TotalMilliseconds ?? 0.0));
} }
internal async Task EnterGlobalAsync(int id, WebSocketRequest request)
{
var requestBucket = GatewayBucket.Get(request.Options.BucketId);
if (requestBucket.Type == GatewayBucketType.Unbucketed)
return;

var globalBucketType = GatewayBucket.Get(GatewayBucketType.Unbucketed);
var options = RequestOptions.CreateOrClone(request.Options);
options.BucketId = globalBucketType.Id;
var globalRequest = new WebSocketRequest(null, null, false, options);
var globalBucket = GetOrCreateBucket(globalBucketType.Id, globalRequest);
await globalBucket.TriggerAsync(id, globalRequest);
}


private RequestBucket GetOrCreateBucket(string id, IRequest request) private RequestBucket GetOrCreateBucket(string id, IRequest request)
{ {


+ 9
- 0
src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs View File

@@ -203,6 +203,15 @@ namespace Discord.Net.Queue
} }
} }


internal async Task TriggerAsync(int id, IRequest request)
{
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Trigger Bucket");
#endif
await EnterAsync(id, request).ConfigureAwait(false);
UpdateRateLimit(id, request, default(RateLimitInfo), false);
}

private async Task EnterAsync(int id, IRequest request) private async Task EnterAsync(int id, IRequest request)
{ {
int windowCount; int windowCount;


+ 1
- 0
src/Discord.Net.WebSocket/DiscordShardedClient.cs View File

@@ -67,6 +67,7 @@ namespace Discord.WebSocket
config.DisplayInitialLog = false; config.DisplayInitialLog = false;
_baseConfig = config; _baseConfig = config;
_connectionGroupLock = new SemaphoreSlim(1, 1); _connectionGroupLock = new SemaphoreSlim(1, 1);
GatewayBucket.SetLimits(GatewayLimits.GetOrCreate(config.GatewayLimits));


if (config.TotalShards == null) if (config.TotalShards == null)
_automaticShards = true; _automaticShards = true;


+ 3
- 0
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -2,6 +2,7 @@ using Discord.API;
using Discord.API.Gateway; using Discord.API.Gateway;
using Discord.Logging; using Discord.Logging;
using Discord.Net.Converters; using Discord.Net.Converters;
using Discord.Net.Queue;
using Discord.Net.Udp; using Discord.Net.Udp;
using Discord.Net.WebSockets; using Discord.Net.WebSockets;
using Discord.Rest; using Discord.Rest;
@@ -120,6 +121,8 @@ namespace Discord.WebSocket
#pragma warning disable IDISP004 #pragma warning disable IDISP004
public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null, null) public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null, null)
{ {
GatewayBucket.SetLimits(GatewayLimits.GetOrCreate(config.GatewayLimits));

ApiClient.WebSocketRequestQueue.RateLimitTriggered += async (id, info) => ApiClient.WebSocketRequestQueue.RateLimitTriggered += async (id, info) =>
{ {
if (info == null) if (info == null)


+ 8
- 0
src/Discord.Net.WebSocket/DiscordSocketConfig.cs View File

@@ -125,6 +125,14 @@ namespace Discord.WebSocket
/// </summary> /// </summary>
public bool GuildSubscriptions { get; set; } = true; public bool GuildSubscriptions { get; set; } = true;


/// <summary>
/// Gets or sets the gateway limits.
/// <note type="warning">
/// It should only be changed for bots that have special limits provided by Discord.
/// </note>
/// </summary>
public GatewayLimits GatewayLimits { get; set; } = new GatewayLimits();

internal RequestQueue _websocketRequestQueue; internal RequestQueue _websocketRequestQueue;


/// <summary> /// <summary>


Loading…
Cancel
Save