Skip to content

Commitment discount eligibility columns swap the FOCUS Spend and Usage categories #2279

Description

🐛 Problem

The columns x_CommitmentDiscountSpendEligibility and x_CommitmentDiscountUsageEligibility carry the wrong values. Their meanings are swapped.

FOCUS defines CommitmentDiscountCategory with two values:

  • Usage means the commitment is based on a usage quantity. A reservation is a usage commitment.
  • Spend means the commitment is based on an amount of money. A savings plan is a spend commitment.

The toolkit sets the eligibility columns the other way round:

  • x_CommitmentDiscountSpendEligibility reports reservation pricing.
  • x_CommitmentDiscountUsageEligibility reports savings plan pricing.

A user who reads these columns with the FOCUS definition gets the opposite answer.

The toolkit also disagrees with itself. Two components use the FOCUS definition. Three components use the swapped one. The list is in the table below.

Where the swap occurs

Component Behavior Source
Open data script Swapped Update-CommitmentDiscountEligibility.ps1#L642-L643
Open data documentation Swapped open-data.md#L170-L171
Hub ingestion, FOCUS 1.0 Swapped IngestionSetup_v1_0.kql#L99-L100
Hub ingestion, FOCUS 1.2 Swapped IngestionSetup_v1_2.kql#L98-L99
Best practice query Correct compute.md#L186-L188

The clearest example sits inside one file. IngestionSetup_v1_2.kql sets CommitmentDiscountCategory correctly on line 89. It then sets the two eligibility columns with the opposite mapping on line 98. Ten lines separate the two blocks.

// Line 89 to 93 -- correct, matches FOCUS
| extend CommitmentDiscountCategory = case(
    x_SkuPriceType == 'ReservedInstance', 'Usage',
    x_SkuPriceType == 'SavingsPlan', 'Spend',
    ''
)
//
// Line 96 to 99 -- swapped
// TODO: Check this to ensure it's correct
| extend x_CommitmentDiscountSpendEligibility = iff(x_SkuMeterId in (riMeters) and x_SkuPriceType != 'ReservedInstance', 'Eligible', 'Not Eligible')
| extend x_CommitmentDiscountUsageEligibility = iff(x_SkuMeterId in (spMeters), 'Eligible', 'Not Eligible')

Line 97 already carries the comment // TODO: Check this to ensure it's correct.

👣 Repro steps

Meter 00020329-e657-5687-9d1a-0be9876e5116 shows the effect. It is a Azure_Redis_Cache_Enterprise_E100 SKU. The open data documentation uses this same meter as its sample row.

  1. Query the Azure Retail Prices API for that meter. Include the preview API version. The default version omits the savingsPlan field, and every meter then looks ineligible.

    https://prices.azure.com/api/retail/prices?api-version=2023-01-01-preview&meterRegion='primary'&$filter=meterId eq '00020329-e657-5687-9d1a-0be9876e5116'
    
  2. Read the response. The meter has two Reservation items. The meter has one Consumption item, and that item has an empty savingsPlan array. The meter therefore has reservation pricing and no savings plan pricing.

  3. Open CommitmentDiscountEligibility.csv and find the same meter.

  4. Compare the two results.

Column Value in the file What FOCUS says it should be
x_CommitmentDiscountSpendEligibility Eligible Not Eligible — the meter has no savings plan pricing
x_CommitmentDiscountUsageEligibility Not Eligible Eligible — the meter has reservation pricing

I checked six meters this way. All six show the same swap.

🤔 Expected

The columns follow the FOCUS definition:

  • x_CommitmentDiscountSpendEligibility reports savings plan pricing.
  • x_CommitmentDiscountUsageEligibility reports reservation pricing.

All five components then agree with each other and with FOCUS.

ℹ️ Additional context

This is not a new defect. The behavior is present since the dataset shipped in v14. PR #2251 rewrote the fetch logic in the open data script. It did not change this mapping. PR #2278 refreshes the data with the same mapping.

A fix breaks existing consumers. The column names stay the same and the values change. Any report, query, or dashboard that reads these columns today produces different results after the fix. The change therefore needs a release note and a version boundary.

The scope is larger than the open data file. A fix must cover the four swapped components in the table above. The best practice query already uses the correct mapping and needs no change.

Sources

Claim Source
FOCUS defines Usage and Spend for CommitmentDiscountCategory FOCUS 1.2 specification, commitmentdiscountcategory.md
Reservation maps to Usage, savings plan maps to Spend docs-mslearn/focus/mapping.md#L80-L81
The same mapping stated through benefit resource IDs docs-mslearn/focus/convert.md#L46
Column definition in the FOCUS metadata reference docs-mslearn/focus/metadata.md#L51
Retail Prices API reference Azure Retail Prices API

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: Triage 🔍Untriaged issue needs to be reviewedTool: FOCUSFOCUS feedback and documentationTool: FinOps hubsData pipeline solutionTool: Open dataDatasets to normalize and enhance FinOps reportsType: Bug 🐛Something isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions