Skip to content

fix(gcp/computeengine): normalize recommender savings by the projection duration - #386

Merged
cristim merged 3 commits into
mainfrom
fix/384-gcp-compute-savings-duration
Oct 13, 2026
Merged

cristim merged 3 commits into
mainfrom
fix/384-gcp-compute-savings-duration

Conversation

@cristim

@cristim cristim commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

What

extractCostImpactFromRecommendation treated the Recommender CostProjection amount as monthly savings and never read CostProjection.Duration. It now scales the amount to a 730-hour month (monthly = amount x 730h / Duration; 730h = 8760/12, the month length the other providers use). A non-zero cost with a missing, zero or negative Duration returns an error wrapping errBadAmount, so GetRecommendations skips that recommendation with a log instead of reporting a window-less number as monthly. A zero cost needs no Duration.

Evidence is fixture-based (recommenderpb-shaped; the account has no live Recommender data). Existing cost fixtures gained a 730h Duration, including providers/gcp/recommendations_sdk_test.go (test file only; recommendations.go is untouched).

Tests

savings_window_test.go: 120 USD over 730h = 120/month; over 365 days = 10/month; over 15 days = 243.33/month; missing, zero and negative Duration error; zero cost with no Duration is fine; a rec without Duration is skipped by convertOrSkipBadAmount. Against main the new tests do not compile (the extractor returned nothing and ignored Duration, so a 365-day projection would be reported as 120 monthly); after the change GOWORK=off go vet/test ./... in providers/gcp passes.

Disjoint from go#307 (term resolution in convertGCPRecommendation): this PR touches only the cost-impact extractor and its call site.

Closes #384

🤖 Generated with Claude Code

…on duration

EstimatedSavings is monthly, but the CostProjection amount applies over its
Duration, which was never read. Scale to a 30-day month and skip a
recommendation whose cost has no positive duration.

Closes #384

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@cristim cristim added priority/p2 Backlog-worthy severity/medium Moderate harm type/bug Defect labels Oct 13, 2026
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 4 billable files and costs up to $1.00.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Or wait 20 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 61 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: LeanerCloud/cloud-commitments-go/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 16775463-d01f-4c54-8b58-0927c0a24f98
📥 Commits

Reviewing files that changed from the base of the PR and between 309cb5c and 586ad53.

📒 Files selected for processing: 4
  • providers/gcp/recommendations_sdk_test.go
  • providers/gcp/services/computeengine/client.go
  • providers/gcp/services/computeengine/client_test.go
  • providers/gcp/services/computeengine/savings_window_test.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@cristim

cristim commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Independent review (claude-opus-5-5) of 444f456: CHANGES REQUESTED

Normalising by CostProjection.Duration is the right fix. A missing, zero or negative Duration with a non-zero cost makes the rec go through convertOrSkipBadAmount, which logs and drops it. That is consistent with the existing errBadAmount handling and does not fabricate a figure, so it is fail-loud enough for this path.

Blocking

  1. client.go const savingsWindow = 30 * 24 * time.Hour (720 h) does not match the month the rest of the repo uses: pkg/exchange and pkg/insurance use 730 h; AWS parser_sp.go hoursPerMonth = 730.0; GCP cloudstorage/client.go averageHoursPerMonth = 8760.0 / 12; Azure DaysPerMonth = 30.4375 (= 730.5 h); the platform's Archera DTO is monthly_730h_*. go#385 also sets RecurringMonthlyCost on the same rec to hourly x 730, so EstimatedSavings and MonthlyCost on one row would use different month lengths, and savings would be understated by 1.4%. The comment "GCP bills a month as 30 days" is not supported: GCP bills calendar months, and 30 days is the usual Recommender projection window, not a billing month. Suggested fix: const savingsWindow = 8760 * time.Hour / 12 (730 h, matching cloudstorage), and update the test expectations.

Non-blocking
2. Cloud SQL (cloudsql/client.go:489) and Memorystore (memorystore/client.go:457) read CostProjection the same way and also ignore Duration. Needs a follow-up issue.
3. A skipped rec shows up only as a log line. Fine for now; a count in the returned result would make it visible to callers.

Evidence

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@cristim

cristim commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Independent review (claude-opus-5-5) of dc3ee68: APPROVE

This re-review covers dc3ee68. The blocking item from my earlier review is fixed.

Checked

Non-blocking

  1. The SDK fixtures now use a 730 h Duration. Real Recommender CUD projections are usually 30 days (2592000s). A 30-day case (120 becomes 121.67) would show the effect on real data.
  2. Cloud SQL and Memorystore still ignore Duration. Tracked in fix(gcp): Cloud SQL and Memorystore still use term-total cost fields and ignore the savings Duration #399.

@cristim

cristim commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Independent review (claude-opus-5-5) of 586ad53: APPROVE
Update-branch of the approved dc3ee68: only main's commits came in, as clean merges with no conflict resolution; the PR's own diff content is unchanged; GOWORK=off vet and tests pass in: providers/gcp.

@cristim
cristim merged commit a4fc226 into main Oct 13, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority/p2 Backlog-worthy severity/medium Moderate harm type/bug Defect

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(gcp/computeengine): recommender savings are not normalized by the CostProjection duration

1 participant