Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to disable rate-limited request retry #1151

Merged
merged 2 commits into from Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions discord.go
Expand Up @@ -36,6 +36,7 @@ func New(token string) (s *Session, err error) {
StateEnabled: true,
Compress: true,
ShouldReconnectOnError: true,
ShouldRetryOnRateLimit: true,
ShardID: 0,
ShardCount: 1,
MaxRestRetries: 3,
Expand Down
29 changes: 23 additions & 6 deletions restapi.go
Expand Up @@ -73,6 +73,18 @@ func (r RESTError) Error() string {
return "HTTP " + r.Response.Status + ", " + string(r.ResponseBody)
}

// RateLimitError is returned when a request exceeds a rate limit
// and ShouldRetryOnRateLimit is false. The request may be manually
// retried after waiting the duration specified by RetryAfter.
type RateLimitError struct {
*RateLimit
}

// Error returns a rate limit error with rate limited endpoint and retry time.
func (e RateLimitError) Error() string {
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
return "Rate limit exceeded on " + e.URL + ", retry after " + e.RetryAfter.String()
}

// Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr
func (s *Session) Request(method, urlStr string, data interface{}) (response []byte, err error) {
return s.RequestWithBucketID(method, urlStr, data, strings.SplitN(urlStr, "?", 2)[0])
Expand Down Expand Up @@ -186,14 +198,19 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
s.log(LogError, "rate limit unmarshal error, %s", err)
return
}
s.log(LogInformational, "Rate Limiting %s, retry in %v", urlStr, rl.RetryAfter)
s.handleEvent(rateLimitEventType, &RateLimit{TooManyRequests: &rl, URL: urlStr})

time.Sleep(rl.RetryAfter)
// we can make the above smarter
// this method can cause longer delays than required
if s.ShouldRetryOnRateLimit {
s.log(LogInformational, "Rate Limiting %s, retry in %v", urlStr, rl.RetryAfter)
s.handleEvent(rateLimitEventType, &RateLimit{TooManyRequests: &rl, URL: urlStr})

response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence)
time.Sleep(rl.RetryAfter)
// we can make the above smarter
// this method can cause longer delays than required

response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence)
} else {
err = &RateLimitError{&RateLimit{TooManyRequests: &rl, URL: urlStr}}
}
case http.StatusUnauthorized:
if strings.Index(s.Token, "Bot ") != 0 {
s.log(LogInformational, ErrUnauthorized.Error())
Expand Down
3 changes: 3 additions & 0 deletions structs.go
Expand Up @@ -43,6 +43,9 @@ type Session struct {
// Should the session reconnect the websocket on errors.
ShouldReconnectOnError bool

// Should the session retry requests when rate limited.
ShouldRetryOnRateLimit bool

// Identify is sent during initial handshake with the discord gateway.
// https://discord.com/developers/docs/topics/gateway#identify
Identify Identify
Expand Down