From 1774d98793f4b3e5cbfa98689387b97dccc391cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 26 Mar 2026 14:01:24 +0800 Subject: [PATCH] fix(ccm,ocm): restore fixed usage polling Remove the poll_interval config surface from CCM and OCM so both services fall back to the built-in 1h polling cadence again. Also isolate CCM credential lock mocking per test instance so the access-token refresh tests stop racing on shared global state. --- docs/configuration/service/ccm.md | 13 +++---------- docs/configuration/service/ccm.zh.md | 13 +++---------- docs/configuration/service/ocm.md | 13 +++---------- docs/configuration/service/ocm.zh.md | 13 +++---------- option/ccm.go | 10 ++++------ option/ocm.go | 10 ++++------ service/ccm/credential_builder.go | 3 +-- service/ccm/credential_default.go | 10 +++++++--- service/ccm/credential_default_test.go | 6 +----- service/ccm/credential_external.go | 11 ++--------- service/ccm/credential_provider.go | 11 +++-------- service/ccm/test_helpers_test.go | 1 + service/ocm/credential_builder.go | 3 +-- service/ocm/credential_external.go | 11 ++--------- service/ocm/credential_provider.go | 11 +++-------- 15 files changed, 41 insertions(+), 98 deletions(-) diff --git a/docs/configuration/service/ccm.md b/docs/configuration/service/ccm.md index 582390139..d037fb52a 100644 --- a/docs/configuration/service/ccm.md +++ b/docs/configuration/service/ccm.md @@ -104,8 +104,7 @@ A single OAuth credential file. The `type` field can be omitted (defaults to `de "tag": "pool", "type": "balancer", "strategy": "", - "credentials": ["a", "b"], - "poll_interval": "60s" + "credentials": ["a", "b"] } ``` @@ -113,7 +112,6 @@ Assigns sessions to default credentials based on the selected strategy. Sessions - `strategy`: Selection strategy. One of `least_used` `round_robin` `random` `fallback`. `least_used` will be used by default. - `credentials`: ==Required== List of default credential tags. -- `poll_interval`: How often to poll upstream usage API. Default `60s`. ##### Fallback Strategy @@ -122,15 +120,13 @@ Assigns sessions to default credentials based on the selected strategy. Sessions "tag": "backup", "type": "balancer", "strategy": "fallback", - "credentials": ["a", "b"], - "poll_interval": "30s" + "credentials": ["a", "b"] } ``` A balancer with `strategy: "fallback"` uses credentials in order. It falls through to the next when the current one is exhausted. - `credentials`: ==Required== Ordered list of default credential tags. -- `poll_interval`: How often to poll upstream usage API. Default `60s`. ##### External Credential @@ -144,8 +140,7 @@ A balancer with `strategy: "fallback"` uses credentials in order. It falls throu "token": "", "reverse": false, "detour": "", - "usages_path": "", - "poll_interval": "30m" + "usages_path": "" } ``` @@ -158,7 +153,6 @@ Proxies requests through a remote CCM instance instead of using a local OAuth cr - `reverse`: Enable connector mode. Requires `url`. A connector dials out to `/ccm/v1/reverse` on the remote instance and cannot serve local requests directly. When `url` is set without `reverse`, the credential proxies requests through the remote instance normally and prefers an established reverse connection when one is available. - `detour`: Outbound tag for connecting to the remote instance. - `usages_path`: Optional usage tracking file. -- `poll_interval`: How often to poll the remote status endpoint. Default `30m`. #### usages_path @@ -290,7 +284,6 @@ claude { "tag": "pool", "type": "balancer", - "poll_interval": "60s", "credentials": ["a", "b"] } ], diff --git a/docs/configuration/service/ccm.zh.md b/docs/configuration/service/ccm.zh.md index 586cb5bb1..8fe2e1373 100644 --- a/docs/configuration/service/ccm.zh.md +++ b/docs/configuration/service/ccm.zh.md @@ -104,8 +104,7 @@ Claude Code OAuth 凭据文件的路径。 "tag": "pool", "type": "balancer", "strategy": "", - "credentials": ["a", "b"], - "poll_interval": "60s" + "credentials": ["a", "b"] } ``` @@ -113,7 +112,6 @@ Claude Code OAuth 凭据文件的路径。 - `strategy`:选择策略。可选值:`least_used` `round_robin` `random` `fallback`。默认使用 `least_used`。 - `credentials`:==必填== 默认凭据标签列表。 -- `poll_interval`:轮询上游使用 API 的间隔。默认 `60s`。 ##### 回退策略 @@ -122,15 +120,13 @@ Claude Code OAuth 凭据文件的路径。 "tag": "backup", "type": "balancer", "strategy": "fallback", - "credentials": ["a", "b"], - "poll_interval": "30s" + "credentials": ["a", "b"] } ``` 将 `strategy` 设为 `fallback` 的均衡凭据会按顺序使用凭据。当前凭据耗尽后切换到下一个。 - `credentials`:==必填== 有序的默认凭据标签列表。 -- `poll_interval`:轮询上游使用 API 的间隔。默认 `60s`。 ##### 外部凭据 @@ -144,8 +140,7 @@ Claude Code OAuth 凭据文件的路径。 "token": "", "reverse": false, "detour": "", - "usages_path": "", - "poll_interval": "30m" + "usages_path": "" } ``` @@ -158,7 +153,6 @@ Claude Code OAuth 凭据文件的路径。 - `reverse`:启用连接器模式。要求设置 `url`。启用后,此凭据会主动拨出到远程实例的 `/ccm/v1/reverse`,且不能直接为本地请求提供服务。当设置了 `url` 但未启用 `reverse` 时,此凭据会正常通过远程实例转发请求,并在反向连接建立后优先使用该反向连接。 - `detour`:用于连接远程实例的出站标签。 - `usages_path`:可选的使用跟踪文件。 -- `poll_interval`:轮询远程状态端点的间隔。默认 `30m`。 #### usages_path @@ -290,7 +284,6 @@ claude { "tag": "pool", "type": "balancer", - "poll_interval": "60s", "credentials": ["a", "b"] } ], diff --git a/docs/configuration/service/ocm.md b/docs/configuration/service/ocm.md index 43027d9db..e47dfdc33 100644 --- a/docs/configuration/service/ocm.md +++ b/docs/configuration/service/ocm.md @@ -100,8 +100,7 @@ A single OAuth credential file. The `type` field can be omitted (defaults to `de "tag": "pool", "type": "balancer", "strategy": "", - "credentials": ["a", "b"], - "poll_interval": "60s" + "credentials": ["a", "b"] } ``` @@ -109,7 +108,6 @@ Assigns sessions to default credentials based on the selected strategy. Sessions - `strategy`: Selection strategy. One of `least_used` `round_robin` `random` `fallback`. `least_used` will be used by default. - `credentials`: ==Required== List of default credential tags. -- `poll_interval`: How often to poll upstream usage API. Default `60s`. ##### Fallback Strategy @@ -118,15 +116,13 @@ Assigns sessions to default credentials based on the selected strategy. Sessions "tag": "backup", "type": "balancer", "strategy": "fallback", - "credentials": ["a", "b"], - "poll_interval": "30s" + "credentials": ["a", "b"] } ``` A balancer with `strategy: "fallback"` uses credentials in order. It falls through to the next when the current one is exhausted. - `credentials`: ==Required== Ordered list of default credential tags. -- `poll_interval`: How often to poll upstream usage API. Default `60s`. ##### External Credential @@ -140,8 +136,7 @@ A balancer with `strategy: "fallback"` uses credentials in order. It falls throu "token": "", "reverse": false, "detour": "", - "usages_path": "", - "poll_interval": "30m" + "usages_path": "" } ``` @@ -154,7 +149,6 @@ Proxies requests through a remote OCM instance instead of using a local OAuth cr - `reverse`: Enable connector mode. Requires `url`. A connector dials out to `/ocm/v1/reverse` on the remote instance and cannot serve local requests directly. When `url` is set without `reverse`, the credential proxies requests through the remote instance normally and prefers an established reverse connection when one is available. - `detour`: Outbound tag for connecting to the remote instance. - `usages_path`: Optional usage tracking file. -- `poll_interval`: How often to poll the remote status endpoint. Default `30m`. #### usages_path @@ -342,7 +336,6 @@ codex --profile ocm { "tag": "pool", "type": "balancer", - "poll_interval": "60s", "credentials": ["a", "b"] } ], diff --git a/docs/configuration/service/ocm.zh.md b/docs/configuration/service/ocm.zh.md index 2d06206f0..76a5067dd 100644 --- a/docs/configuration/service/ocm.zh.md +++ b/docs/configuration/service/ocm.zh.md @@ -100,8 +100,7 @@ OpenAI OAuth 凭据文件的路径。 "tag": "pool", "type": "balancer", "strategy": "", - "credentials": ["a", "b"], - "poll_interval": "60s" + "credentials": ["a", "b"] } ``` @@ -109,7 +108,6 @@ OpenAI OAuth 凭据文件的路径。 - `strategy`:选择策略。可选值:`least_used` `round_robin` `random` `fallback`。默认使用 `least_used`。 - `credentials`:==必填== 默认凭据标签列表。 -- `poll_interval`:轮询上游使用 API 的间隔。默认 `60s`。 ##### 回退策略 @@ -118,15 +116,13 @@ OpenAI OAuth 凭据文件的路径。 "tag": "backup", "type": "balancer", "strategy": "fallback", - "credentials": ["a", "b"], - "poll_interval": "30s" + "credentials": ["a", "b"] } ``` 将 `strategy` 设为 `fallback` 的均衡凭据会按顺序使用凭据。当前凭据耗尽后切换到下一个。 - `credentials`:==必填== 有序的默认凭据标签列表。 -- `poll_interval`:轮询上游使用 API 的间隔。默认 `60s`。 ##### 外部凭据 @@ -140,8 +136,7 @@ OpenAI OAuth 凭据文件的路径。 "token": "", "reverse": false, "detour": "", - "usages_path": "", - "poll_interval": "30m" + "usages_path": "" } ``` @@ -154,7 +149,6 @@ OpenAI OAuth 凭据文件的路径。 - `reverse`:启用连接器模式。要求设置 `url`。启用后,此凭据会主动拨出到远程实例的 `/ocm/v1/reverse`,且不能直接为本地请求提供服务。当设置了 `url` 但未启用 `reverse` 时,此凭据会正常通过远程实例转发请求,并在反向连接建立后优先使用该反向连接。 - `detour`:用于连接远程实例的出站标签。 - `usages_path`:可选的使用跟踪文件。 -- `poll_interval`:轮询远程状态端点的间隔。默认 `30m`。 #### usages_path @@ -343,7 +337,6 @@ codex --profile ocm { "tag": "pool", "type": "balancer", - "poll_interval": "60s", "credentials": ["a", "b"] } ], diff --git a/option/ccm.go b/option/ccm.go index 481068e61..1079bad0f 100644 --- a/option/ccm.go +++ b/option/ccm.go @@ -89,16 +89,14 @@ type CCMDefaultCredentialOptions struct { type CCMBalancerCredentialOptions struct { Strategy string `json:"strategy,omitempty"` Credentials badoption.Listable[string] `json:"credentials"` - PollInterval badoption.Duration `json:"poll_interval,omitempty"` RebalanceThreshold float64 `json:"rebalance_threshold,omitempty"` } type CCMExternalCredentialOptions struct { URL string `json:"url,omitempty"` ServerOptions - Token string `json:"token"` - Reverse bool `json:"reverse,omitempty"` - Detour string `json:"detour,omitempty"` - UsagesPath string `json:"usages_path,omitempty"` - PollInterval badoption.Duration `json:"poll_interval,omitempty"` + Token string `json:"token"` + Reverse bool `json:"reverse,omitempty"` + Detour string `json:"detour,omitempty"` + UsagesPath string `json:"usages_path,omitempty"` } diff --git a/option/ocm.go b/option/ocm.go index af937560b..aeb1f75e7 100644 --- a/option/ocm.go +++ b/option/ocm.go @@ -88,16 +88,14 @@ type OCMDefaultCredentialOptions struct { type OCMBalancerCredentialOptions struct { Strategy string `json:"strategy,omitempty"` Credentials badoption.Listable[string] `json:"credentials"` - PollInterval badoption.Duration `json:"poll_interval,omitempty"` RebalanceThreshold float64 `json:"rebalance_threshold,omitempty"` } type OCMExternalCredentialOptions struct { URL string `json:"url,omitempty"` ServerOptions - Token string `json:"token"` - Reverse bool `json:"reverse,omitempty"` - Detour string `json:"detour,omitempty"` - UsagesPath string `json:"usages_path,omitempty"` - PollInterval badoption.Duration `json:"poll_interval,omitempty"` + Token string `json:"token"` + Reverse bool `json:"reverse,omitempty"` + Detour string `json:"detour,omitempty"` + UsagesPath string `json:"usages_path,omitempty"` } diff --git a/service/ccm/credential_builder.go b/service/ccm/credential_builder.go index 8ffd42b0d..4650ef1f5 100644 --- a/service/ccm/credential_builder.go +++ b/service/ccm/credential_builder.go @@ -2,7 +2,6 @@ package ccm import ( "context" - "time" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -48,7 +47,7 @@ func buildCredentialProviders( if err != nil { return nil, nil, err } - providers[credentialOption.Tag] = newBalancerProvider(subCredentials, credentialOption.BalancerOptions.Strategy, time.Duration(credentialOption.BalancerOptions.PollInterval), credentialOption.BalancerOptions.RebalanceThreshold, logger) + providers[credentialOption.Tag] = newBalancerProvider(subCredentials, credentialOption.BalancerOptions.Strategy, credentialOption.BalancerOptions.RebalanceThreshold, logger) } } diff --git a/service/ccm/credential_default.go b/service/ccm/credential_default.go index 5467d3d8d..60a02c55b 100644 --- a/service/ccm/credential_default.go +++ b/service/ccm/credential_default.go @@ -26,8 +26,6 @@ import ( "github.com/sagernet/sing/common/observable" ) -var acquireCredentialLockFunc = acquireCredentialLock - type claudeProfileSnapshot struct { OAuthAccount *claudeOAuthAccount AccountType string @@ -56,6 +54,7 @@ type defaultCredential struct { capWeekly float64 usageTracker *AggregatedUsage forwardHTTPClient *http.Client + acquireLock func(string) (func(), error) logger log.ContextLogger watcher *fswatch.Watcher watcherRetryAt time.Time @@ -122,6 +121,7 @@ func newDefaultCredential(ctx context.Context, tag string, options option.CCMDef cap5h: cap5h, capWeekly: capWeekly, forwardHTTPClient: httpClient, + acquireLock: acquireCredentialLock, logger: logger, requestContext: requestContext, cancelRequests: cancelRequests, @@ -363,7 +363,11 @@ func (c *defaultCredential) tryRefreshCredentials(force bool) bool { if !c.shouldAttemptRefresh(currentCredentials, force) { return false } - release, err := acquireCredentialLockFunc(c.configDir) + acquireLock := c.acquireLock + if acquireLock == nil { + acquireLock = acquireCredentialLock + } + release, err := acquireLock(c.configDir) if err != nil { c.logger.Debug("acquire credential lock for ", c.tag, ": ", err) return false diff --git a/service/ccm/credential_default_test.go b/service/ccm/credential_default_test.go index a5535aa26..9435f3db7 100644 --- a/service/ccm/credential_default_test.go +++ b/service/ccm/credential_default_test.go @@ -31,13 +31,9 @@ func TestGetAccessTokenReturnsExistingTokenWhenLockFails(t *testing.T) { t.Fatal(err) } - originalLockFunc := acquireCredentialLockFunc - acquireCredentialLockFunc = func(string) (func(), error) { + credential.acquireLock = func(string) (func(), error) { return nil, errors.New("locked") } - t.Cleanup(func() { - acquireCredentialLockFunc = originalLockFunc - }) token, err := credential.getAccessToken() if err != nil { diff --git a/service/ccm/credential_external.go b/service/ccm/credential_external.go index e8a0cf1ea..be1bb7f5b 100644 --- a/service/ccm/credential_external.go +++ b/service/ccm/credential_external.go @@ -38,7 +38,6 @@ type externalCredential struct { state credentialState stateAccess sync.RWMutex pollAccess sync.Mutex - pollInterval time.Duration usageTracker *AggregatedUsage logger log.ContextLogger @@ -113,18 +112,12 @@ func externalCredentialReversePath(parsedURL *url.URL, endpointPath string) stri } func newExternalCredential(ctx context.Context, tag string, options option.CCMExternalCredentialOptions, logger log.ContextLogger) (*externalCredential, error) { - pollInterval := time.Duration(options.PollInterval) - if pollInterval <= 0 { - pollInterval = 30 * time.Minute - } - requestContext, cancelRequests := context.WithCancel(context.Background()) reverseContext, reverseCancel := context.WithCancel(context.Background()) credential := &externalCredential{ tag: tag, token: options.Token, - pollInterval: pollInterval, logger: logger, requestContext: requestContext, cancelRequests: cancelRequests, @@ -355,9 +348,9 @@ func (c *externalCredential) markRateLimited(resetAt time.Time) { } func (c *externalCredential) markUpstreamRejected() { - c.logger.Warn("upstream rejected credential ", c.tag, ", marking unavailable for ", log.FormatDuration(c.pollInterval)) + c.logger.Warn("upstream rejected credential ", c.tag, ", marking unavailable for ", log.FormatDuration(defaultPollInterval)) c.stateAccess.Lock() - c.state.upstreamRejectedUntil = time.Now().Add(c.pollInterval) + c.state.upstreamRejectedUntil = time.Now().Add(defaultPollInterval) c.state.setAvailability(availabilityStateTemporarilyBlocked, availabilityReasonUpstreamRejected, c.state.upstreamRejectedUntil) shouldInterrupt := c.checkTransitionLocked() c.stateAccess.Unlock() diff --git a/service/ccm/credential_provider.go b/service/ccm/credential_provider.go index 640ced702..4f5f2ad32 100644 --- a/service/ccm/credential_provider.go +++ b/service/ccm/credential_provider.go @@ -112,7 +112,6 @@ type balancerProvider struct { credentials []Credential strategy string roundRobinIndex atomic.Uint64 - pollInterval time.Duration rebalanceThreshold float64 sessionAccess sync.RWMutex sessions map[string]sessionEntry @@ -121,14 +120,10 @@ type balancerProvider struct { logger log.ContextLogger } -func newBalancerProvider(credentials []Credential, strategy string, pollInterval time.Duration, rebalanceThreshold float64, logger log.ContextLogger) *balancerProvider { - if pollInterval <= 0 { - pollInterval = defaultPollInterval - } +func newBalancerProvider(credentials []Credential, strategy string, rebalanceThreshold float64, logger log.ContextLogger) *balancerProvider { return &balancerProvider{ credentials: credentials, strategy: strategy, - pollInterval: pollInterval, rebalanceThreshold: rebalanceThreshold, sessions: make(map[string]sessionEntry), credentialInterrupts: make(map[credentialInterruptKey]credentialInterruptEntry), @@ -383,14 +378,14 @@ func (p *balancerProvider) pollIfStale() { p.interruptAccess.Unlock() for _, credential := range p.credentials { - if time.Since(credential.lastUpdatedTime()) > credential.pollBackoff(p.pollInterval) { + if time.Since(credential.lastUpdatedTime()) > credential.pollBackoff(defaultPollInterval) { credential.pollUsage() } } } func (p *balancerProvider) pollCredentialIfStale(credential Credential) { - if time.Since(credential.lastUpdatedTime()) > credential.pollBackoff(p.pollInterval) { + if time.Since(credential.lastUpdatedTime()) > credential.pollBackoff(defaultPollInterval) { credential.pollUsage() } } diff --git a/service/ccm/test_helpers_test.go b/service/ccm/test_helpers_test.go index 175dc71ad..2f5259159 100644 --- a/service/ccm/test_helpers_test.go +++ b/service/ccm/test_helpers_test.go @@ -85,6 +85,7 @@ func newTestDefaultCredential(t *testing.T, credentialPath string, transport htt cap5h: 99, capWeekly: 99, forwardHTTPClient: &http.Client{Transport: transport}, + acquireLock: acquireCredentialLock, logger: log.NewNOPFactory().Logger(), requestContext: requestContext, cancelRequests: cancelRequests, diff --git a/service/ocm/credential_builder.go b/service/ocm/credential_builder.go index fb69c62c9..57a34fb7c 100644 --- a/service/ocm/credential_builder.go +++ b/service/ocm/credential_builder.go @@ -2,7 +2,6 @@ package ocm import ( "context" - "time" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -48,7 +47,7 @@ func buildOCMCredentialProviders( if err != nil { return nil, nil, err } - providers[credentialOption.Tag] = newBalancerProvider(subCredentials, credentialOption.BalancerOptions.Strategy, time.Duration(credentialOption.BalancerOptions.PollInterval), credentialOption.BalancerOptions.RebalanceThreshold, logger) + providers[credentialOption.Tag] = newBalancerProvider(subCredentials, credentialOption.BalancerOptions.Strategy, credentialOption.BalancerOptions.RebalanceThreshold, logger) } } diff --git a/service/ocm/credential_external.go b/service/ocm/credential_external.go index 7553cdf43..765c3a27b 100644 --- a/service/ocm/credential_external.go +++ b/service/ocm/credential_external.go @@ -40,7 +40,6 @@ type externalCredential struct { state credentialState stateAccess sync.RWMutex pollAccess sync.Mutex - pollInterval time.Duration usageTracker *AggregatedUsage logger log.ContextLogger @@ -131,18 +130,12 @@ func externalCredentialReversePath(parsedURL *url.URL, endpointPath string) stri } func newExternalCredential(ctx context.Context, tag string, options option.OCMExternalCredentialOptions, logger log.ContextLogger) (*externalCredential, error) { - pollInterval := time.Duration(options.PollInterval) - if pollInterval <= 0 { - pollInterval = 30 * time.Minute - } - requestContext, cancelRequests := context.WithCancel(context.Background()) reverseContext, reverseCancel := context.WithCancel(context.Background()) credential := &externalCredential{ tag: tag, token: options.Token, - pollInterval: pollInterval, logger: logger, requestContext: requestContext, cancelRequests: cancelRequests, @@ -377,9 +370,9 @@ func (c *externalCredential) markRateLimited(resetAt time.Time) { } func (c *externalCredential) markUpstreamRejected() { - c.logger.Warn("upstream rejected credential ", c.tag, ", marking unavailable for ", log.FormatDuration(c.pollInterval)) + c.logger.Warn("upstream rejected credential ", c.tag, ", marking unavailable for ", log.FormatDuration(defaultPollInterval)) c.stateAccess.Lock() - c.state.upstreamRejectedUntil = time.Now().Add(c.pollInterval) + c.state.upstreamRejectedUntil = time.Now().Add(defaultPollInterval) c.state.setAvailability(availabilityStateTemporarilyBlocked, availabilityReasonUpstreamRejected, c.state.upstreamRejectedUntil) shouldInterrupt := c.checkTransitionLocked() c.stateAccess.Unlock() diff --git a/service/ocm/credential_provider.go b/service/ocm/credential_provider.go index 714e44ab7..b296543cc 100644 --- a/service/ocm/credential_provider.go +++ b/service/ocm/credential_provider.go @@ -112,7 +112,6 @@ type balancerProvider struct { credentials []Credential strategy string roundRobinIndex atomic.Uint64 - pollInterval time.Duration rebalanceThreshold float64 sessionAccess sync.RWMutex sessions map[string]sessionEntry @@ -125,14 +124,10 @@ func compositeCredentialSelectable(credential Credential) bool { return !credential.ocmIsAPIKeyMode() } -func newBalancerProvider(credentials []Credential, strategy string, pollInterval time.Duration, rebalanceThreshold float64, logger log.ContextLogger) *balancerProvider { - if pollInterval <= 0 { - pollInterval = defaultPollInterval - } +func newBalancerProvider(credentials []Credential, strategy string, rebalanceThreshold float64, logger log.ContextLogger) *balancerProvider { return &balancerProvider{ credentials: credentials, strategy: strategy, - pollInterval: pollInterval, rebalanceThreshold: rebalanceThreshold, sessions: make(map[string]sessionEntry), credentialInterrupts: make(map[credentialInterruptKey]credentialInterruptEntry), @@ -410,14 +405,14 @@ func (p *balancerProvider) pollIfStale() { p.interruptAccess.Unlock() for _, credential := range p.credentials { - if time.Since(credential.lastUpdatedTime()) > credential.pollBackoff(p.pollInterval) { + if time.Since(credential.lastUpdatedTime()) > credential.pollBackoff(defaultPollInterval) { credential.pollUsage() } } } func (p *balancerProvider) pollCredentialIfStale(credential Credential) { - if time.Since(credential.lastUpdatedTime()) > credential.pollBackoff(p.pollInterval) { + if time.Since(credential.lastUpdatedTime()) > credential.pollBackoff(defaultPollInterval) { credential.pollUsage() } }