Skip to content

FinOps: storage growth for a database with no history shows n/a instead of 0, in both apps - #4902

Merged
erikdarlingdata merged 7 commits into
devfrom
fix/storage-growth-no-history
Oct 1, 2026
Merged

erikdarlingdata merged 7 commits into
devfrom
fix/storage-growth-no-history

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

What a user saw

  • No history: in Storage Growth, a database with no past size row (new, or collected for less than 7 or 30 days) showed 0.00 growth, a 0.00 daily rate and 0.0 percent. That reads as "did not grow". It means "no history to compare".
  • A stale server: a server whose collection stopped more than a window ago compared its last snapshot with itself and showed 0 growth.
  • The daily rate: it divided by 7 or 30 whatever the real gap between the two snapshots, so a collection gap overstated or understated it.
  • The per-table drill: a table with one snapshot, or a new table, showed 0 growth, rate and percent.

What changed

  • Both apps' StorageGrowthSql: growth is current - past with no COALESCE, so a missing past is NULL.
    • A past snapshot must be before the latest one (Lite: collection_time < latest; the Viewer nulls a past at or after the latest), so a stale server reads n/a.
    • The daily rate keeps its order (30-day past, else 7-day past) and divides by the real days between the two snapshots. It's NULL when neither past exists.
    • The percent is NULL when there is no 30-day past (or it is 0).
    • The order is growth_30d_mb DESC NULLS LAST, growth_7d_mb DESC NULLS LAST, database_name.
  • StorageGrowthRow (Lite and Viewer): Growth7dMb, Growth30dMb, DailyGrowthRateMb and GrowthPct30d are decimal?, read as null.
  • The per-table drill: ObjectSizeGrowthRow's Growth30dMb, DailyGrowthRateMb and GrowthPct30d are decimal? in both apps, null when the table has no earlier sample. The drill's rate still divides by its window.
  • Both Storage Growth grids and both drill grids bind those columns with TargetNullValue='n/a', keeping the format.
  • A database with history reads exactly as before, except that its rate now uses the real days between snapshots.

Consumers

Changed: both grids, both readers, both SQL constants, the plan-shape live oracle. Left alone: the per-table drill and heatmap use a different row type (ObjectSizeGrowthRow) and the MCP table-size tool already returns null for missing baselines. No MCP tool emits these per-database fields. No other code sorts or charts the four properties.

Pins

  • Viewer, live (StorageGrowthNoHistoryLiveTests): no-history, 7-day-only and full-history databases; a server last collected 20 days ago reads all null; a 12-day gap gives 120/12; the drill is null for a single snapshot and for a new table; the order.
  • Lite: StorageGrowth_ADatabaseWithNoPastRowReadsGrowthAsUnknown_NotZero, plus the stale server, the gap and the order in AzureSiblingGrowthTests, and the null drill in IndexObjectStatsTests. The existing rate pins divide by the real days between their seeded snapshots (31 and 8), and pins that expected 0 now expect null.
  • Updated: the plan-shape oracle (its stale server now expects n/a) and a source pin on the reader line.
  • RED: the first pins failed on dev (Assert.Null got 0.00). The stale-server, gap and drill pins came with their fix in one commit; each asserts null or the real-days value where the code before it gives 0.00 or growth/7.

CHANGELOG

SECTION: Fixed
ENTRY: - Storage Growth shows n/a, not 0, when there is no history to compare ([#4902]) - A database with no size row 7 or 30 days back, a server whose collection stopped more than a window ago, and a table in the per-table drill with no earlier sample all showed 0 growth, rate and percent in both the Lite and Viewer grids; they now show n/a. The daily rate also divides by the real days between the two snapshots instead of 7 or 30. Shipped in v3.8.0.
REF: [#4902]: #4902

@erikdarlingdata
erikdarlingdata marked this pull request as ready for review October 1, 2026 17:43
@erikdarlingdata
erikdarlingdata merged commit 5c255f5 into dev Oct 1, 2026
17 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/storage-growth-no-history branch October 1, 2026 17:44
erikdarlingdata added a commit that referenced this pull request Oct 4, 2026
… its whole size (#5077) (#5080)

Storage Growth now counts a table created during the window as growing by its whole size. The object drill compares each table's reserved size at the database's latest snapshot with its earliest snapshot in the window. The grid ranked a table with no earliest row as unknown growth and put it last, while the heatmap ranked it as zero, so the two panels could cut different top-N sets and a large new table could miss the list. All four queries (the summary and daily-series reads, in Darling's DarlingFinOpsStorageGrowthReader and in Lite) now use current - COALESCE(earliest, 0) and sort by growth_mb DESC NULLS LAST, schema, table. That covers the desktop grids and heatmaps in both apps, the web Storage Growth tab and get_finops storage_growth. A new table's percent stays n/a.

This reverses part of #4902: a database with only one snapshot in the window still reads n/a, but a table created since the earliest snapshot now ranks by an earliest size of 0. The get_finops guide no longer says the heatmap's table set can differ from the grid's, and it names both null cases. The database level and get_table_index_sizes keep their own rules.

A source pin checks that all four queries share the growth expression and order, and a live pin around the cut fails on the old SQL. The view's re-rank test now seeds a table of unknown size.

Refs #5077
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