Skip to content

Commit 9711f99

Browse files
committed
Add DISABLE_GLOBAL_RATELIMIT_DETECTION flag, return a 429 not a 500 when /users/@me or /gateway/bot calls 429
1 parent 3a912d0 commit 9711f99

File tree

6 files changed

+57
-31
lines changed

6 files changed

+57
-31
lines changed

CONFIG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ Allows you to define custom global request limits for one or multiple bots. The
6565
Format: Command separated list of **user id** and limit combo, separated by `:` and with no spaces at all. Don't use application ids.
6666
Example: `392827169497284619:100,227115752396685313:80`
6767

68+
69+
##### DISABLE_GLOBAL_RATELIMIT_DETECTION
70+
Disables the optimistic global rest limit detection. This detection uses the /gateway/bot endpoint, which has a low ratelimit and can cause issues with requests being dropped/delayed as cluster size grows.
71+
72+
You probably want to set BOT_RATELIMIT_OVERRIDES if you set this to true.
73+
74+
Default: false
75+
76+
In the future, this will be the only possible behavior.
77+
6878
## Unstable env vars
6979
Collection of env vars that may be removed at any time, mainly used for Discord introducing new behaviour on their edge api versions
7080

README.md

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,24 @@ The proxy sits between the client and discord. Instead of pointing to discord.co
2424

2525
Configuration options are
2626

27-
| Variable | Value | Default |
28-
|-----------------|-----------------------------------------------|-------------------------|
29-
| LOG_LEVEL | panic, fatal, error, warn, info, debug, trace | info |
30-
| PORT | number | 8080 |
31-
| METRICS_PORT | number | 9000 |
32-
| ENABLE_METRICS | boolean | true |
33-
| ENABLE_PPROF | boolean | false |
34-
| BUFFER_SIZE | number | 50 |
35-
| OUTBOUND_IP | string | "" |
36-
| BIND_IP | string | 0.0.0.0 |
37-
| REQUEST_TIMEOUT | number (milliseconds) | 5000 |
38-
| CLUSTER_PORT | number | 7946 |
39-
| CLUSTER_MEMBERS | string list (comma separated) | "" |
40-
| CLUSTER_DNS | string | "" |
41-
| MAX_BEARER_COUNT| number | 1024 |
42-
| DISABLE_HTTP_2 | bool | true |
43-
| BOT_RATELIMIT_OVERRIDES | string list (comma separated) | "" |
27+
| Variable | Value | Default |
28+
|-----------------|---------------------------------------------|---------|
29+
| LOG_LEVEL | panic, fatal, error, warn, info, debug, trace | info |
30+
| PORT | number | 8080 |
31+
| METRICS_PORT | number | 9000 |
32+
| ENABLE_METRICS | boolean | true |
33+
| ENABLE_PPROF | boolean | false |
34+
| BUFFER_SIZE | number | 50 |
35+
| OUTBOUND_IP | string | "" |
36+
| BIND_IP | string | 0.0.0.0 |
37+
| REQUEST_TIMEOUT | number (milliseconds) | 5000 |
38+
| CLUSTER_PORT | number | 7946 |
39+
| CLUSTER_MEMBERS | string list (comma separated) | "" |
40+
| CLUSTER_DNS | string | "" |
41+
| MAX_BEARER_COUNT| number | 1024 |
42+
| DISABLE_HTTP_2 | bool | true |
43+
| BOT_RATELIMIT_OVERRIDES | string list (comma separated) | "" |
44+
| DISABLE_GLOBAL_RATELIMIT_DETECTION | boolean | false |
4445

4546
Information on each config var can be found [here](https://github.com/germanoeich/nirn-proxy/blob/main/CONFIG.md)
4647

lib/discord.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,16 @@ var contextTimeout time.Duration
2222

2323
var globalOverrideMap = make(map[string]uint)
2424

25+
var disableRestLimitDetection = false
26+
2527
type BotGatewayResponse struct {
2628
SessionStartLimit map[string]int `json:"session_start_limit"`
2729
}
2830

2931
type BotUserResponse struct {
30-
Id string `json:"id"`
32+
Id string `json:"id"`
3133
Username string `json:"username"`
32-
Discrim string `json:"discriminator"`
34+
Discrim string `json:"discriminator"`
3335
}
3436

3537
func createTransport(ip string, disableHttp2 bool) http.RoundTripper {
@@ -56,9 +58,9 @@ func createTransport(ip string, disableHttp2 bool) http.RoundTripper {
5658
}
5759

5860
dialer := &net.Dialer{
59-
LocalAddr: addr,
60-
Timeout: 30 * time.Second,
61-
KeepAlive: 30 * time.Second,
61+
LocalAddr: addr,
62+
Timeout: 30 * time.Second,
63+
KeepAlive: 30 * time.Second,
6264
}
6365

6466
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
@@ -109,15 +111,17 @@ func parseGlobalOverrides(overrides string) {
109111
}
110112
}
111113

112-
func ConfigureDiscordHTTPClient(ip string, timeout time.Duration, disableHttp2 bool, globalOverrides string) {
114+
func ConfigureDiscordHTTPClient(ip string, timeout time.Duration, disableHttp2 bool, globalOverrides string, disableRestDetection bool) {
113115
transport := createTransport(ip, disableHttp2)
114116
client = &http.Client{
115117
Transport: transport,
116-
Timeout: 90 * time.Second,
118+
Timeout: 90 * time.Second,
117119
}
118120

119121
contextTimeout = timeout
120122

123+
disableRestLimitDetection = disableRestDetection
124+
121125
parseGlobalOverrides(globalOverrides)
122126
}
123127

@@ -137,6 +141,10 @@ func GetBotGlobalLimit(token string, user *BotUserResponse) (uint, error) {
137141
return 50, nil
138142
}
139143

144+
if disableRestLimitDetection {
145+
return 50, nil
146+
}
147+
140148
bot, err := doDiscordReq(context.Background(), "/api/v9/gateway/bot", "GET", nil, map[string][]string{"Authorization": {token}}, "")
141149

142150
if err != nil {
@@ -205,7 +213,7 @@ func GetBotUser(token string) (*BotUserResponse, error) {
205213
}
206214

207215
func doDiscordReq(ctx context.Context, path string, method string, body io.ReadCloser, header http.Header, query string) (*http.Response, error) {
208-
discordReq, err := http.NewRequestWithContext(ctx, method, "https://discord.com" + path + "?" + query, body)
216+
discordReq, err := http.NewRequestWithContext(ctx, method, "https://discord.com"+path+"?"+query, body)
209217
if err != nil {
210218
return nil, err
211219
}
@@ -231,7 +239,6 @@ func doDiscordReq(ctx context.Context, path string, method string, body io.ReadC
231239
status = "429 Shared"
232240
}
233241
}
234-
235242

236243
RequestHistogram.With(map[string]string{"route": route, "status": status, "method": method, "clientId": identifier.(string)}).Observe(elapsed)
237244
}

lib/queue.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func NewRequestQueue(processor func(ctx context.Context, item *QueueItem) (*http
7575
botLimit: limit,
7676
}, nil
7777
}
78+
7879
return nil, err
7980
}
8081

lib/queue_manager.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,15 @@ func (m *QueueManager) fulfillRequest(resp *http.ResponseWriter, req *http.Reque
297297
}
298298

299299
if err != nil {
300-
(*resp).WriteHeader(500)
301-
(*resp).Write([]byte(err.Error()))
302-
ErrorCounter.Inc()
303-
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Error(err)
300+
if strings.HasPrefix(err.Error(), "429") {
301+
Generate429(resp)
302+
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Warn(err)
303+
} else {
304+
(*resp).WriteHeader(500)
305+
(*resp).Write([]byte(err.Error()))
306+
ErrorCounter.Inc()
307+
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Error(err)
308+
}
304309
return
305310
}
306311

main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ func main() {
7474

7575
globalOverrides := lib.EnvGet("BOT_RATELIMIT_OVERRIDES", "")
7676

77-
lib.ConfigureDiscordHTTPClient(outboundIp, time.Duration(timeout)*time.Millisecond, disableHttp2, globalOverrides)
77+
disableGlobalRatelimitDetection := lib.EnvGetBool("DISABLE_GLOBAL_RATELIMIT_DETECTION", false)
78+
79+
lib.ConfigureDiscordHTTPClient(outboundIp, time.Duration(timeout)*time.Millisecond, disableHttp2, globalOverrides, disableGlobalRatelimitDetection)
7880

7981
port := lib.EnvGet("PORT", "8080")
8082
bindIp := lib.EnvGet("BIND_IP", "0.0.0.0")

0 commit comments

Comments
 (0)