forked from pothtonswer/discordmuffin
Slight better rate limit handling
This improves greatly on the previous rate limit handling however still needs review and possible improvement. Please report bugs!
This commit is contained in:
parent
dd69a7e27f
commit
a24f9e3d10
2 changed files with 43 additions and 1 deletions
33
restapi.go
33
restapi.go
|
@ -25,6 +25,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,6 +51,26 @@ func (s *Session) Request(method, urlStr string, data interface{}) (response []b
|
||||||
// request makes a (GET/POST/...) Requests to Discord REST API.
|
// request makes a (GET/POST/...) Requests to Discord REST API.
|
||||||
func (s *Session) request(method, urlStr, contentType string, b []byte) (response []byte, err error) {
|
func (s *Session) request(method, urlStr, contentType string, b []byte) (response []byte, err error) {
|
||||||
|
|
||||||
|
// rate limit mutex for this url
|
||||||
|
// TODO: review for performance improvements
|
||||||
|
// ideally we just ignore endpoints that we've never
|
||||||
|
// received a 429 on. But this simple method works and
|
||||||
|
// is a lot less complex :) It also might even be more
|
||||||
|
// performat due to less checks and maps.
|
||||||
|
var mu *sync.Mutex
|
||||||
|
s.rateLimit.Lock()
|
||||||
|
if s.rateLimit.url == nil {
|
||||||
|
s.rateLimit.url = make(map[string]*sync.Mutex)
|
||||||
|
}
|
||||||
|
|
||||||
|
bu := strings.Split(urlStr, "?")
|
||||||
|
mu, _ = s.rateLimit.url[bu[0]]
|
||||||
|
if mu == nil {
|
||||||
|
mu = new(sync.Mutex)
|
||||||
|
s.rateLimit.url[urlStr] = mu
|
||||||
|
}
|
||||||
|
s.rateLimit.Unlock()
|
||||||
|
|
||||||
if s.Debug {
|
if s.Debug {
|
||||||
log.Printf("API REQUEST %8s :: %s\n", method, urlStr)
|
log.Printf("API REQUEST %8s :: %s\n", method, urlStr)
|
||||||
log.Printf("API REQUEST PAYLOAD :: [%s]\n", string(b))
|
log.Printf("API REQUEST PAYLOAD :: [%s]\n", string(b))
|
||||||
|
@ -77,7 +99,9 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons
|
||||||
|
|
||||||
client := &http.Client{Timeout: (20 * time.Second)}
|
client := &http.Client{Timeout: (20 * time.Second)}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
mu.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -111,13 +135,20 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons
|
||||||
// TODO check for 401 response, invalidate token if we get one.
|
// TODO check for 401 response, invalidate token if we get one.
|
||||||
|
|
||||||
case 429: // TOO MANY REQUESTS - Rate limiting
|
case 429: // TOO MANY REQUESTS - Rate limiting
|
||||||
|
|
||||||
rl := RateLimit{}
|
rl := RateLimit{}
|
||||||
err = json.Unmarshal(response, &rl)
|
err = json.Unmarshal(response, &rl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Request unmarshal rate limit error : %+v", err)
|
s.log(LogError, "rate limit unmarshal error, %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.log(LogInformational, "Rate Limiting %s, retry in %d", urlStr, rl.RetryAfter)
|
||||||
|
mu.Lock()
|
||||||
time.Sleep(rl.RetryAfter)
|
time.Sleep(rl.RetryAfter)
|
||||||
|
mu.Unlock()
|
||||||
|
// we can make the above smarter
|
||||||
|
// this method can cause longer delays then required
|
||||||
|
|
||||||
response, err = s.request(method, urlStr, contentType, b)
|
response, err = s.request(method, urlStr, contentType, b)
|
||||||
|
|
||||||
default: // Error condition
|
default: // Error condition
|
||||||
|
|
11
structs.go
11
structs.go
|
@ -75,6 +75,17 @@ type Session struct {
|
||||||
|
|
||||||
// When nil, the session is not listening.
|
// When nil, the session is not listening.
|
||||||
listening chan interface{}
|
listening chan interface{}
|
||||||
|
|
||||||
|
// used to deal with rate limits
|
||||||
|
// may switch to slices later
|
||||||
|
// TODO: performance test map vs slices
|
||||||
|
rateLimit rateLimitMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type rateLimitMutex struct {
|
||||||
|
sync.Mutex
|
||||||
|
url map[string]*sync.Mutex
|
||||||
|
bucket map[string]*sync.Mutex // TODO :)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A VoiceRegion stores data for a specific voice region server.
|
// A VoiceRegion stores data for a specific voice region server.
|
||||||
|
|
Loading…
Reference in a new issue