Skip to content

feat(#223): map logical usageMetrics names to catalog metrics so models price from the catalog - #228

Merged
elecnix merged 2 commits into
mainfrom
feat/223-metric-mapping
Jul 3, 2026
Merged

feat(#223): map logical usageMetrics names to catalog metrics so models price from the catalog#228
elecnix merged 2 commits into
mainfrom
feat/223-metric-mapping

Conversation

@elecnix

@elecnix elecnix commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #223. Connects the pricing catalog to DAG models. Previously the catalog (live Infracost + seed) was unreachable from real models: nodes author usageMetrics with logical names (natHours, keysCount), but the catalog is keyed by usage_metric names (NAT-Gateway-Hour, KMS-Key-Month). The engine queried the catalog with the raw logical key → always missed → silently fell back to embedded pricingRates, contradicting Principle 13.

Changes

  • Per-handler catalog_metrics map (logical → catalog usage_metric) on the resource types that correspond to catalog rows: NAT Gateway, VPC Endpoint, Elastic IP, CloudWatch Logs + Metrics/Alarms, KMS, Secrets Manager, ECR, Route53, Data Transfer. Derived directly from each handler's _*_cost function so the names are exact.
    • Keyed per handler, not per service — resources of the same service reuse a logical name for a different catalog metric: dataProcessedGbNAT-Gateway-DataProcessed vs VPC-Endpoint-DataProcessed; storedGbECR-Storage vs CloudWatch-Log-Storage.
  • ResourceRegistry.resolve_catalog_metric(address, logical) resolves via the handler that owns the node's resourceAddress.
  • Engine (flat + tiered cost paths) retries the catalog query with the mapped name when the raw name misses, before the pricingRates fallback.

Backward compatibility

Fully preserved: the raw metric name is tried first (nodes already using catalog names keep working), and unmapped logical names / non-handler addresses fall back to pricingRates exactly as before.

Test plan

  • resolve_catalog_metric: per-handler resolution incl. shared-logical-name disambiguation (NAT vs VPC dataProcessedGb), unknown address / unmapped name → None.
  • End-to-end CostAggregator: a NAT node authored with natHours prices from the catalog ($32.85 = 730 × $0.045), reachable only if the mapping resolved.
  • Unmapped-metric → pricingRates fallback still applies.
  • Full suite: 908 passed locally.

Note: handlers whose example models use ad-hoc metric names not equal to their valid_metrics (e.g. ALB lcus, Lambda gb_seconds) are not mapped here — those names are ambiguous and can be aligned/added incrementally. This PR establishes the mechanism and covers the handlers with unambiguous catalog metrics.

elecnix and others added 2 commits July 3, 2026 12:20
…ls price from the catalog

Models author usageMetrics with logical names (e.g. "natHours", "keysCount");
the pricing catalog is keyed by usage_metric names ("NAT-Gateway-Hour",
"KMS-Key-Month"). The engine queried the catalog with the raw logical key, which
never matched, so every node silently fell back to embedded pricingRates and the
catalog (live Infracost + seed) was effectively unreachable — contradicting
Principle 13.

- Add a per-handler `catalog_metrics` map (logical -> catalog usage_metric) on
  the resource types that correspond to catalog rows: NAT Gateway, VPC Endpoint,
  Elastic IP, CloudWatch Logs + Metrics/Alarms, KMS, Secrets Manager, ECR,
  Route53, and Data Transfer. Keyed per handler (not per service) because
  resources of the same service reuse a logical name for a different catalog
  metric (dataProcessedGb -> NAT-Gateway-DataProcessed vs VPC-Endpoint-DataProcessed;
  storedGb -> ECR-Storage vs CloudWatch-Log-Storage).
- ResourceRegistry.resolve_catalog_metric(address, logical) resolves via the
  owning handler.
- The engine (flat + tiered cost paths) now retries the catalog query with the
  mapped name when the raw name misses, before falling back to pricingRates.
  Fully backward compatible: raw name tried first; unmapped names and non-handler
  addresses keep using pricingRates.

Tests: per-handler resolution incl. shared-logical-name disambiguation; an
end-to-end aggregate where a NAT node authored with "natHours" prices from the
catalog ($32.85 = 730 x $0.045); unmapped-metric pricingRates fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@elecnix
elecnix marked this pull request as ready for review July 3, 2026 16:26
@elecnix
elecnix merged commit 1a2f8f6 into main Jul 3, 2026
3 checks passed
@elecnix
elecnix deleted the feat/223-metric-mapping branch July 3, 2026 16:26
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.

pricing: DAG models can't reach the catalog — no mapping from logical usageMetrics keys to catalog usage_metric names

1 participant