mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-20 06:28:13 +00:00
fix(proxy): stop retrying non-retryable 4xx responses
doProxyRequestWithTimeout returns an error for every non-200 status and executeProxyAttempt returned it as a plain, retryable error, so 4xx client errors (400/404/405/...) were retried with exponential backoff and the 4xx branch of isRetryableStatusCode was dead code. Additionally, retry-go's finite-attempt loop only consults the custom RetryIf, which overrode the default IsRecoverable gate, so retry. Unrecoverable errors (non-retryable 4xx AND timeouts) were still retried. Honor IsRecoverable in RetryIf so these never retry. Now 5xx/429 are retried, other 4xx and timeouts fail fast. Adds Test_proxyRetryPolicyByStatusCode locking in the policy.
This commit is contained in:
@@ -510,10 +510,17 @@ func executeProxyAttempt(c fiber.Ctx, proxyURL string) error {
|
||||
return retry.Unrecoverable(proxyErr)
|
||||
}
|
||||
|
||||
// Check if this is a retryable HTTP error (e.g., 503)
|
||||
// These indicate the server responded but with an error status
|
||||
// The server responded with an HTTP status code. Decide retry policy
|
||||
// from the status: 5xx and 429 are retryable; other 4xx client
|
||||
// errors fail fast instead of burning backoff retries.
|
||||
// doProxyRequestWithTimeout already copied the status into c.Response().
|
||||
if strings.Contains(proxyErr.Error(), "non-200 response") {
|
||||
// Track as a failure for retryable HTTP errors
|
||||
statusCode := c.Response().StatusCode()
|
||||
shouldRetry, _ := isRetryableStatusCode(statusCode)
|
||||
if !shouldRetry {
|
||||
return retry.Unrecoverable(proxyErr)
|
||||
}
|
||||
// Track as a failure for retryable HTTP errors (5xx, 429)
|
||||
if poolMgr != nil {
|
||||
poolMgr.RecordConnectionFailure()
|
||||
}
|
||||
@@ -600,6 +607,12 @@ func performProxyRequestWithEnhancedRetries(c fiber.Ctx, proxyURL string, backen
|
||||
}),
|
||||
retry.LastErrorOnly(true),
|
||||
retry.RetryIf(func(err error) bool {
|
||||
// Unrecoverable errors (non-retryable 4xx, timeouts) must not be
|
||||
// retried. retry-go's finite-attempt loop only consults RetryIf,
|
||||
// so without this the default IsRecoverable gate is lost.
|
||||
if !retry.IsRecoverable(err) {
|
||||
return false
|
||||
}
|
||||
// Don't retry if context is cancelled or context is nil
|
||||
if c == nil {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user