Adrian_Santamaria | 2024-01-12 10:49:43 UTC | #1
Hello
I am trying to configure the GO SDK to retry 429 errors as it is explained in the documentation
However, it is not working, and I don't know if I am doing something wrong, if it is an error in the docs, if there is a bug in the SDK...
The test I've made is an app that makes 350 GET /api/v2/tokens/me requests. As it has a limit of 300 requests per minute, the behaviour I expected was to have 300 successful requests and 50 failures. Then they should wait whatever the "Retry-After" header said, and after that I should get the remaining 50 successes.
However, when I run it, I get hundreds of failures, and many of them are retried even 2 or 3 times...
Here you have my code:
package main
import (
"fmt"
"net/http"
"os"
"time"
"github.com/mypurecloud/platform-client-sdk-go/platformclientv2"
)
func getTokenInfo(
config *platformclientv2.Configuration,
routineId int,
done chan<- bool,
) {
tokensApi := platformclientv2.NewTokensApiWithConfig(config)
_, _, err := tokensApi.GetTokensMe()
if err != nil {
fmt.Printf("[Routine #%v] Error: %q\n", routineId, err)
} else {
fmt.Printf("[Routine #%v] Success!\n", routineId)
}
done <- true
}
func main() {
config := platformclientv2.NewConfiguration()
config.BasePath = "https://api.mypurecloud.ie"
config.AccessToken = os.Getenv("ACCESS_TOKEN")
config.RetryConfiguration = &platformclientv2.RetryConfiguration{
RetryWaitMin: 5 * time.Second,
RetryWaitMax: 60 * time.Second,
RetryMax: 20,
RequestLogHook: func(req *http.Request, retryNumber int) {
fmt.Printf("%v %v request failed. Retry count: %v\n", req.Method, req.URL, retryNumber)
},
}
numRequests := 350
done := make(chan bool)
for i := 1; i <= numRequests; i++ {
go getTokenInfo(config, i, done)
}
for i := 1; i <= numRequests; i++ {
<-done
}
}
John_Carnell | 2024-01-12 15:11:32 UTC | #2
Hi Adrian,
Thanks for letting us know. I will open a ticket and have someone on my team take a look at it. Your posting on this stuff here is appreciated. We use GO and our GO SDK heavily in our code but we have had very few customers using it in the "wild" so I appreciate the detailed description.
Thanks, John Carnell Director, Developer Engagement
John_Carnell | 2024-01-16 17:59:24 UTC | #3
Hi Adrian,
I took your code and realized why you are getting the long retry-after. Genesys Cloud implements a progressive retry-after logic where if you call an API and it tells you that you need to wait X seconds on a retry, if you do not honor that retry the public API will set the retry-after to an even higher level on progressive calls.
In the code example you provided you are spinning up 350 goroutines. The first 300 go through fine, but then the 301st gets a retry-after. Since the other threads still are hitting Genesys Cloud, you will see other calls failing and because the retry-after is calculated on a single OAuth token, you are going to trip the higher retry-after levels because each goroutine is not aware of the overall retry-after calls and are still hitting the API.
This progressive retry-after logic mainly prevents a multi-threaded application from hitting the endpoints even after a retry-after has been sent back.
As a side note:
- You can see our apiclient here.
- Our apiclient uses the http-retryableclient library.
If you do want to do concurrent application where you do 300 requests concurrently, I would recommend using a bound channel (300 items) in size, push 300 requests to the channel, and then don't push anything else to the channel until the 300 requests have all been processed.
I hope that helps.
Thanks, John Carnell Director, Developer Engagement
Adrian_Santamaria | 2024-01-16 18:10:03 UTC | #4
Oh, ok, thanks for the info John.
BTW, I thought that the first 300 requests were failing because I set up the RequestLogHook exactly as it is explained in your docs. However, I think that is wrong, as the RequestLogHook func is called before making every request, not only for the ones that fail. I would suggest changing it to:
RequestLogHook: func(req *http.Request, retryNumber int) {
if retryNumber == 0 {
return
}
fmt.Printf("%v %v request failed. Retry count: %v\n", req.Method, req.URL, retryNumber)
}
so it would only print it when reattempting.
John_Carnell | 2024-01-16 18:12:38 UTC | #5
Hi Adrian,
Fair enough. I will put a ticket out there to clean up the documentation. I also discovered a response hook so I want to make sure that is documented.
Thanks for the feedback.
Thanks, John
system | 2024-02-15 18:13:15 UTC | #6
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
This post was migrated from the old Developer Forum.
ref: 24088