Skip to content

Fix Cost Management 429 retry logic and reduce query weight#2

Open
bychkov wants to merge 1 commit into
mainfrom
fix/cost-management-429-retry
Open

Fix Cost Management 429 retry logic and reduce query weight#2
bychkov wants to merge 1 commit into
mainfrom
fix/cost-management-429-retry

Conversation

@bychkov

@bychkov bychkov commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • 429 retry was dead codeInvoke-AzRestMethod returns 429 as a normal response object (no exception thrown), so the catch block retry path never ran. The script always failed immediately on the first throttle. Fixed by detecting 429/5xx from StatusCode inside the loop.
  • Retry-After now honored — Added Get-RetryAfterSeconds that reads the typed RetryAfter property on HttpResponseHeaders (Delta/Date) and falls back to raw Retry-After / x-ms-retry-after-ms header enumeration, so the script waits exactly as long as Azure requests instead of guessing.
  • Lighter queries — Switched Cost Management granularity from Daily (~365 rows/yr, aggregated in script) to Monthly (12 rows/yr, aggregated by Azure). Reduces per-call weight against the small per-subscription rate budget.
  • Per-year chunking kept — The Query API hard-caps each request to 1 year, so multi-year ranges still require one request per year.

Test plan

  • Single year: pwsh -File .\Get-AzureMonthlyCosts.ps1 -YearRange 2025 -SubscriptionId <id> — should return 12 monthly rows
  • Multi-year: -YearRange 2025:2026 — should return rows for both years (2 API calls)
  • Throttle recovery: if 429 occurs, warnings should show Retrying in N seconds... and eventually succeed rather than failing immediately
  • Current year partial: verify only months up to the current UTC month are returned

- The retry/backoff for HTTP 429 was dead code: Invoke-AzRestMethod
  returns 429 as a normal response (no exception thrown), so the catch
  block never fired and the script always failed immediately on the
  first 429 with no retry.

- Move throttling detection inside the retry loop, checking StatusCode
  directly so 429 and 5xx responses are retried correctly.

- Add Get-RetryAfterSeconds helper that reads the typed RetryAfter
  property on HttpResponseHeaders (Delta / Date) and falls back to
  raw Retry-After / x-ms-retry-after-ms header enumeration, so the
  script waits exactly as long as Azure requests instead of guessing.

- Switch Cost Management query granularity from Daily (~365 rows/yr)
  to Monthly (12 rows/yr). The script already aggregated daily rows to
  months in PowerShell; letting the API do it is strictly lighter and
  reduces consumption of the small per-subscription rate budget.

- Keep per-calendar-year chunking: the Query API hard-caps each
  request to a 1-year time range, so multi-year ranges still need
  one request per year.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant