GCP Cloud SQL Excessive Backup Retention for Dev/Test
What does ZopNight detect here?
Dev/test Cloud SQL instances retaining backups beyond 7 days are the target, with savings of (retention − 7)/retention times chargeable backup GiB times a cloudsql-backup rate over 730 hours. That rate is not yet ingested by the pricing pipeline, so the rule currently abstains on every candidate.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-147 |
| Category | rightsizing |
| Severity | low |
| Metric | none — pure configuration read |
| Source | cloudsql_backup_retention.go |
Where it applies
Retention days and the dev/test name
- resource.Metadata: backup_retention_days (via MetadataString; producer asset_inventory.go:2641). The prior tag: backup_retention_days was empty for GCP (item T0345, read-path fix).
- resource.Name: dev/test pattern (dev, test, qa, staging, sandbox)
- state history: StateEntry[] for time-in-state analysis
Where the 7-day retention floor sits
cloudsqlBackupRetentionThreshold=7, backup_retention_days=7Why the missing backup SKU forces an abstain
Concrete or abstain (cost-driven rightsizing). The prior hardcoded 0.30 fraction is removed. removedFraction = (retention−7)/retention, backupGiB = backup_chargeable_bytes / 2^30, savings = removedFraction × backupGiB × rate × 730, capped at the instance cost. The rate is looked up under the cloudsql-backup SKU key (the dedicated, separately-metered Cloud SQL backup-storage meter, roughly half the cloudsql-ssd SSD data-disk storage rate). A prior version priced against cloudsql-ssd, which overstated the saving ~1.5-2x versus GCP’s real backup pricing; that has been corrected. The model.LookupSKURate rate is per GB-hour (the aggregator divides the GiBy.mo storage SKU by 730 at ingest, see pricing.go:524 isGCPStorageRate / fallback pricing.go:1599), so the ×730 (cloudsqlBackupHoursPerMonth) is mandatory for a monthly figure. As of this fix, the aggregator’s GCP pricing does not yet ingest a cloudsql-backup SKU (only cloudsql-ssd/cloudsql-hdd data-disk SKUs are routed), so LookupSKURate currently always misses for this key and the rule abstains on every candidate until that producer-side plumbing (extractCloudSQLStorageType + a cloudsql-backup DocumentedGCPRates fallback) is added, which is intentional fail-closed behavior, not a regression. Prod-suppression first (isProdName/isProdEnvTag), then positive dev/test evidence. The rule abstains (returns nil) when backup_chargeable_bytes is absent, the live cloudsql-backup rate is missing, or cost is unknown, and it never emits a fabricated fraction or a $0/advisory cost rec. Producer wired the bytes branch (Cloud SQL backupRuns.list → maxChargeableBytes); the rate branch is not yet wired (see above).
Trimming retention in the console
- Go to Cloud SQL Console → select the instance