Skip to content

Commit

Permalink
Add DISABLE_GLOBAL_RATELIMIT_DETECTION flag, return a 429 not a 500 w…
Browse files Browse the repository at this point in the history
…hen /users/@me or /gateway/bot calls 429
  • Loading branch information
germanoeich committed Dec 9, 2022
1 parent 3a912d0 commit 9711f99
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 31 deletions.
10 changes: 10 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ Allows you to define custom global request limits for one or multiple bots. The
Format: Command separated list of **user id** and limit combo, separated by `:` and with no spaces at all. Don't use application ids.
Example: `392827169497284619:100,227115752396685313:80`


##### DISABLE_GLOBAL_RATELIMIT_DETECTION
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.

You probably want to set BOT_RATELIMIT_OVERRIDES if you set this to true.

Default: false

In the future, this will be the only possible behavior.

## Unstable env vars
Collection of env vars that may be removed at any time, mainly used for Discord introducing new behaviour on their edge api versions

Expand Down
35 changes: 18 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,24 @@ The proxy sits between the client and discord. Instead of pointing to discord.co

Configuration options are

| Variable | Value | Default |
|-----------------|-----------------------------------------------|-------------------------|
| LOG_LEVEL | panic, fatal, error, warn, info, debug, trace | info |
| PORT | number | 8080 |
| METRICS_PORT | number | 9000 |
| ENABLE_METRICS | boolean | true |
| ENABLE_PPROF | boolean | false |
| BUFFER_SIZE | number | 50 |
| OUTBOUND_IP | string | "" |
| BIND_IP | string | 0.0.0.0 |
| REQUEST_TIMEOUT | number (milliseconds) | 5000 |
| CLUSTER_PORT | number | 7946 |
| CLUSTER_MEMBERS | string list (comma separated) | "" |
| CLUSTER_DNS | string | "" |
| MAX_BEARER_COUNT| number | 1024 |
| DISABLE_HTTP_2 | bool | true |
| BOT_RATELIMIT_OVERRIDES | string list (comma separated) | "" |
| Variable | Value | Default |
|-----------------|---------------------------------------------|---------|
| LOG_LEVEL | panic, fatal, error, warn, info, debug, trace | info |
| PORT | number | 8080 |
| METRICS_PORT | number | 9000 |
| ENABLE_METRICS | boolean | true |
| ENABLE_PPROF | boolean | false |
| BUFFER_SIZE | number | 50 |
| OUTBOUND_IP | string | "" |
| BIND_IP | string | 0.0.0.0 |
| REQUEST_TIMEOUT | number (milliseconds) | 5000 |
| CLUSTER_PORT | number | 7946 |
| CLUSTER_MEMBERS | string list (comma separated) | "" |
| CLUSTER_DNS | string | "" |
| MAX_BEARER_COUNT| number | 1024 |
| DISABLE_HTTP_2 | bool | true |
| BOT_RATELIMIT_OVERRIDES | string list (comma separated) | "" |
| DISABLE_GLOBAL_RATELIMIT_DETECTION | boolean | false |

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

Expand Down
25 changes: 16 additions & 9 deletions lib/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ var contextTimeout time.Duration

var globalOverrideMap = make(map[string]uint)

var disableRestLimitDetection = false

type BotGatewayResponse struct {
SessionStartLimit map[string]int `json:"session_start_limit"`
}

type BotUserResponse struct {
Id string `json:"id"`
Id string `json:"id"`
Username string `json:"username"`
Discrim string `json:"discriminator"`
Discrim string `json:"discriminator"`
}

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

dialer := &net.Dialer{
LocalAddr: addr,
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
LocalAddr: addr,
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}

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

func ConfigureDiscordHTTPClient(ip string, timeout time.Duration, disableHttp2 bool, globalOverrides string) {
func ConfigureDiscordHTTPClient(ip string, timeout time.Duration, disableHttp2 bool, globalOverrides string, disableRestDetection bool) {
transport := createTransport(ip, disableHttp2)
client = &http.Client{
Transport: transport,
Timeout: 90 * time.Second,
Timeout: 90 * time.Second,
}

contextTimeout = timeout

disableRestLimitDetection = disableRestDetection

parseGlobalOverrides(globalOverrides)
}

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

if disableRestLimitDetection {
return 50, nil
}

bot, err := doDiscordReq(context.Background(), "/api/v9/gateway/bot", "GET", nil, map[string][]string{"Authorization": {token}}, "")

if err != nil {
Expand Down Expand Up @@ -205,7 +213,7 @@ func GetBotUser(token string) (*BotUserResponse, error) {
}

func doDiscordReq(ctx context.Context, path string, method string, body io.ReadCloser, header http.Header, query string) (*http.Response, error) {
discordReq, err := http.NewRequestWithContext(ctx, method, "https://discord.com" + path + "?" + query, body)
discordReq, err := http.NewRequestWithContext(ctx, method, "https://discord.com"+path+"?"+query, body)
if err != nil {
return nil, err
}
Expand All @@ -231,7 +239,6 @@ func doDiscordReq(ctx context.Context, path string, method string, body io.ReadC
status = "429 Shared"
}
}


RequestHistogram.With(map[string]string{"route": route, "status": status, "method": method, "clientId": identifier.(string)}).Observe(elapsed)
}
Expand Down
1 change: 1 addition & 0 deletions lib/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func NewRequestQueue(processor func(ctx context.Context, item *QueueItem) (*http
botLimit: limit,
}, nil
}

return nil, err
}

Expand Down
13 changes: 9 additions & 4 deletions lib/queue_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,15 @@ func (m *QueueManager) fulfillRequest(resp *http.ResponseWriter, req *http.Reque
}

if err != nil {
(*resp).WriteHeader(500)
(*resp).Write([]byte(err.Error()))
ErrorCounter.Inc()
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Error(err)
if strings.HasPrefix(err.Error(), "429") {
Generate429(resp)
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Warn(err)
} else {
(*resp).WriteHeader(500)
(*resp).Write([]byte(err.Error()))
ErrorCounter.Inc()
logEntry.WithFields(logrus.Fields{"function": "getOrCreateQueue", "queueType": queueType}).Error(err)
}
return
}

Expand Down
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ func main() {

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

lib.ConfigureDiscordHTTPClient(outboundIp, time.Duration(timeout)*time.Millisecond, disableHttp2, globalOverrides)
disableGlobalRatelimitDetection := lib.EnvGetBool("DISABLE_GLOBAL_RATELIMIT_DETECTION", false)

lib.ConfigureDiscordHTTPClient(outboundIp, time.Duration(timeout)*time.Millisecond, disableHttp2, globalOverrides, disableGlobalRatelimitDetection)

port := lib.EnvGet("PORT", "8080")
bindIp := lib.EnvGet("BIND_IP", "0.0.0.0")
Expand Down

0 comments on commit 9711f99

Please sign in to comment.