ccm,ocm: add exponential backoff with cap for poll retry
Replace flat 1-minute poll retry interval with exponential backoff (1m → 2m → 4m → 5m cap). Suppress error logs after reaching the cap.
This commit is contained in:
@@ -529,7 +529,9 @@ func (c *externalCredential) pollUsage(ctx context.Context) {
|
||||
return request, nil
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Error("poll usage for ", c.tag, ": ", err)
|
||||
if !c.isPollBackoffAtCap() {
|
||||
c.logger.Error("poll usage for ", c.tag, ": ", err)
|
||||
}
|
||||
c.incrementPollFailures()
|
||||
return
|
||||
}
|
||||
@@ -609,7 +611,18 @@ func (c *externalCredential) pollBackoff(baseInterval time.Duration) time.Durati
|
||||
if failures <= 0 {
|
||||
return baseInterval
|
||||
}
|
||||
return failedPollRetryInterval
|
||||
backoff := failedPollRetryInterval * time.Duration(1<<(failures-1))
|
||||
if backoff > httpRetryMaxBackoff {
|
||||
return httpRetryMaxBackoff
|
||||
}
|
||||
return backoff
|
||||
}
|
||||
|
||||
func (c *externalCredential) isPollBackoffAtCap() bool {
|
||||
c.stateMutex.RLock()
|
||||
defer c.stateMutex.RUnlock()
|
||||
failures := c.state.consecutivePollFailures
|
||||
return failures > 0 && failedPollRetryInterval*time.Duration(1<<(failures-1)) >= httpRetryMaxBackoff
|
||||
}
|
||||
|
||||
func (c *externalCredential) incrementPollFailures() {
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
const (
|
||||
defaultPollInterval = 60 * time.Minute
|
||||
failedPollRetryInterval = time.Minute
|
||||
httpRetryMaxBackoff = 5 * time.Minute
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -583,7 +584,18 @@ func (c *defaultCredential) pollBackoff(baseInterval time.Duration) time.Duratio
|
||||
if failures <= 0 {
|
||||
return baseInterval
|
||||
}
|
||||
return failedPollRetryInterval
|
||||
backoff := failedPollRetryInterval * time.Duration(1<<(failures-1))
|
||||
if backoff > httpRetryMaxBackoff {
|
||||
return httpRetryMaxBackoff
|
||||
}
|
||||
return backoff
|
||||
}
|
||||
|
||||
func (c *defaultCredential) isPollBackoffAtCap() bool {
|
||||
c.stateMutex.RLock()
|
||||
defer c.stateMutex.RUnlock()
|
||||
failures := c.state.consecutivePollFailures
|
||||
return failures > 0 && failedPollRetryInterval*time.Duration(1<<(failures-1)) >= httpRetryMaxBackoff
|
||||
}
|
||||
|
||||
func (c *defaultCredential) earliestReset() time.Time {
|
||||
@@ -616,7 +628,9 @@ func (c *defaultCredential) pollUsage(ctx context.Context) {
|
||||
|
||||
accessToken, err := c.getAccessToken()
|
||||
if err != nil {
|
||||
c.logger.Error("poll usage for ", c.tag, ": get token: ", err)
|
||||
if !c.isPollBackoffAtCap() {
|
||||
c.logger.Error("poll usage for ", c.tag, ": get token: ", err)
|
||||
}
|
||||
c.incrementPollFailures()
|
||||
return
|
||||
}
|
||||
@@ -638,7 +652,9 @@ func (c *defaultCredential) pollUsage(ctx context.Context) {
|
||||
return request, nil
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Error("poll usage for ", c.tag, ": ", err)
|
||||
if !c.isPollBackoffAtCap() {
|
||||
c.logger.Error("poll usage for ", c.tag, ": ", err)
|
||||
}
|
||||
c.incrementPollFailures()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -568,7 +568,9 @@ func (c *externalCredential) pollUsage(ctx context.Context) {
|
||||
return request, nil
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Error("poll usage for ", c.tag, ": ", err)
|
||||
if !c.isPollBackoffAtCap() {
|
||||
c.logger.Error("poll usage for ", c.tag, ": ", err)
|
||||
}
|
||||
c.incrementPollFailures()
|
||||
return
|
||||
}
|
||||
@@ -648,7 +650,18 @@ func (c *externalCredential) pollBackoff(baseInterval time.Duration) time.Durati
|
||||
if failures <= 0 {
|
||||
return baseInterval
|
||||
}
|
||||
return failedPollRetryInterval
|
||||
backoff := failedPollRetryInterval * time.Duration(1<<(failures-1))
|
||||
if backoff > httpRetryMaxBackoff {
|
||||
return httpRetryMaxBackoff
|
||||
}
|
||||
return backoff
|
||||
}
|
||||
|
||||
func (c *externalCredential) isPollBackoffAtCap() bool {
|
||||
c.stateMutex.RLock()
|
||||
defer c.stateMutex.RUnlock()
|
||||
failures := c.state.consecutivePollFailures
|
||||
return failures > 0 && failedPollRetryInterval*time.Duration(1<<(failures-1)) >= httpRetryMaxBackoff
|
||||
}
|
||||
|
||||
func (c *externalCredential) incrementPollFailures() {
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
const (
|
||||
defaultPollInterval = 60 * time.Minute
|
||||
failedPollRetryInterval = time.Minute
|
||||
httpRetryMaxBackoff = 5 * time.Minute
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -597,7 +598,18 @@ func (c *defaultCredential) pollBackoff(baseInterval time.Duration) time.Duratio
|
||||
if failures <= 0 {
|
||||
return baseInterval
|
||||
}
|
||||
return failedPollRetryInterval
|
||||
backoff := failedPollRetryInterval * time.Duration(1<<(failures-1))
|
||||
if backoff > httpRetryMaxBackoff {
|
||||
return httpRetryMaxBackoff
|
||||
}
|
||||
return backoff
|
||||
}
|
||||
|
||||
func (c *defaultCredential) isPollBackoffAtCap() bool {
|
||||
c.stateMutex.RLock()
|
||||
defer c.stateMutex.RUnlock()
|
||||
failures := c.state.consecutivePollFailures
|
||||
return failures > 0 && failedPollRetryInterval*time.Duration(1<<(failures-1)) >= httpRetryMaxBackoff
|
||||
}
|
||||
|
||||
func (c *defaultCredential) earliestReset() time.Time {
|
||||
@@ -633,7 +645,9 @@ func (c *defaultCredential) pollUsage(ctx context.Context) {
|
||||
|
||||
accessToken, err := c.getAccessToken()
|
||||
if err != nil {
|
||||
c.logger.Error("poll usage for ", c.tag, ": get token: ", err)
|
||||
if !c.isPollBackoffAtCap() {
|
||||
c.logger.Error("poll usage for ", c.tag, ": get token: ", err)
|
||||
}
|
||||
c.incrementPollFailures()
|
||||
return
|
||||
}
|
||||
@@ -663,7 +677,9 @@ func (c *defaultCredential) pollUsage(ctx context.Context) {
|
||||
return request, nil
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Error("poll usage for ", c.tag, ": ", err)
|
||||
if !c.isPollBackoffAtCap() {
|
||||
c.logger.Error("poll usage for ", c.tag, ": ", err)
|
||||
}
|
||||
c.incrementPollFailures()
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user