Skip to content

Commit

Permalink
uptimerobot/api: wait and retry when API responds with 429 TooManyReq…
Browse files Browse the repository at this point in the history
…uests

To make provider more robust against recently introduced API calls
limits of 10 calls per minute for free accounts.

Closes louy#106

Signed-off-by: Mateusz Gozdek <[email protected]>
  • Loading branch information
invidian committed Sep 1, 2021
1 parent c8d705c commit bf660c0
Showing 1 changed file with 50 additions and 9 deletions.
59 changes: 50 additions & 9 deletions uptimerobot/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import (
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/logging"
)

func New(apiKey string) UptimeRobotApiClient {
rand.Seed(time.Now().UnixNano())

return UptimeRobotApiClient{apiKey}
}

Expand All @@ -32,17 +36,34 @@ func (client UptimeRobotApiClient) MakeCall(

requestBody := fmt.Sprintf("api_key=%s&format=json&%s", client.apiKey, params)

req, err := http.NewRequest("POST", url, strings.NewReader(requestBody))
if err != nil {
return nil, fmt.Errorf("contructing request: %w", err)
}
var res *http.Response

req.Header.Add("cache-control", "no-cache")
req.Header.Add("content-type", "application/x-www-form-urlencoded")
for {
req, err := http.NewRequest("POST", url, strings.NewReader(requestBody))
if err != nil {
return nil, fmt.Errorf("contructing request: %w", err)
}

res, err := httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("performing API request: %w", err)
req.Header.Add("cache-control", "no-cache")
req.Header.Add("content-type", "application/x-www-form-urlencoded")

resCandidate, err := httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("performing API request: %w", err)
}

switch resCandidate.StatusCode {
case http.StatusTooManyRequests:
log.Printf("[DEBUG] Got response code %d, waiting...", http.StatusTooManyRequests)
waitForRetryAfter(resCandidate)
default:
log.Printf("[DEBUG] Got regular response code %d, proceeding", resCandidate.StatusCode)
res = resCandidate
}

if res != nil {
break
}
}

defer func() {
Expand All @@ -66,3 +87,23 @@ func (client UptimeRobotApiClient) MakeCall(

return result, nil
}

func waitForRetryAfter(res *http.Response) {
retryAfterRaws, ok := res.Header["Retry-After"]
if !ok || len(retryAfterRaws) > 1 {
log.Printf("[DEBUG] Retry-After header is missing, waiting 1 second for next request attempt")
retryAfterRaws = []string{"1"}
}

waitTime, err := time.ParseDuration(retryAfterRaws[0] + "s")
if err != nil {
log.Printf("[DEBUG] Parsing %q as Retry-After header value in seconds, waiting 1 second for next request: %v", retryAfterRaws, err)
waitTime = time.Second
}

waitTime = waitTime + 2*time.Second + time.Duration(rand.Float64())*time.Second

log.Printf("[DEBUG] Rate limit exceeded, waiting %v seconds to send next request", waitTime.Seconds())

time.Sleep(waitTime)
}

0 comments on commit bf660c0

Please sign in to comment.