Repository navigation
fix(aws/recs): RateLimiter.Wait skips ctx.Err() check on first attempt (retryCount=0) #16
Description
Affected file
providers/aws/recommendations/ratelimiter.go:62-65
Evidence
// Wait implements exponential backoff delay
func (r *RateLimiter) Wait(ctx context.Context) error {
if r.retryCount == 0 {
// No delay for first attempt
return nil
}
// ... select { case <-ctx.Done(): return ctx.Err() } only on retries
}When retryCount == 0 (the common case: first call into fetchRIPageWithRetry / fetchSPPageWithRetry / fetchCoveragePage / fetchUtilizationPage), Wait returns nil immediately without consulting ctx.Err(). A context already cancelled before the call is invisible to the rate-limiter layer.
Impact
Two surfaces:
-
Hidden test scaffolding dependency. The PERF-02 regression tests in
concurrency_cap_test.go(PR fix(aws/recs): cap coverage/utilization CE calls with shared semaphore cloud-commitments-cli#1218) rely on the cancelled-ctx survivingWaitso thatconcurrency.Acquirecatches it. If anyone reorders the retry loop (Wait→Acquire→ SDK call) so the Acquire moves below the SDK call or is swapped to a non-blocking variant, a cancelled-from-the-start context would NOT be caught at the rate-limiter layer and the SDK call would fire on a dead ctx. The test still asserts onmock.coverageCalls == 0, so it would catch the regression — but only because of an architectural accident, not by explicit contract. -
Wasted work on early cancellation. If the scheduler cancels the parent ctx between batch dispatch and the first per-tuple call (e.g. global shutdown signal, request timeout), the rate-limiter passes the cancelled ctx through to Acquire and the SDK without an early bail. Today this works only because Acquire (when a semaphore is attached) catches it. Without an attached semaphore (CLI ambient calls, unit tests), the SDK call would run on a cancelled ctx.
Recommendation
Short-circuit on ctx.Err() even when retryCount == 0:
func (r *RateLimiter) Wait(ctx context.Context) error {
if err := ctx.Err(); err != nil {
return err
}
if r.retryCount == 0 {
return nil
}
// ... existing backoff logic
}Aligns with feedback_ctx_cancel_terminal.md ("context cancellation is terminal in loops") and makes the rate-limiter respect cancellation as a first-class concern at every entry point, not only when there's a delay to interrupt.
Verifier verdict
Confirmed pre-existing in ratelimiter.go; pre-dates PR LeanerCloud/cloud-commitments-cli#1218 (LeanerCloud/cloud-commitments-cli#1144 / PERF-02). PR LeanerCloud/cloud-commitments-cli#1218 does not regress it. Filed as an independent improvement so the rate-limiter no longer leaks early-cancelled contexts past its boundary.
Activity
IAmJSD commentedon Oct 16, 2026
Claiming: working on this
fix(aws/recs): honor canceled context on RateLimiter.Wait first attem…