Skip to content

The tempdb Space alert's percentage has no size floor, and on Azure SQL Database that makes it fire on one temp table #2515

Closed

Description

@erikdarlingdata

Found while removing the Azure SQL Database gate on TempDbStatsCollector (#2512). The collection is right and ships; this is the consequence downstream, and it is a policy call rather than a bug fix, so it is filed rather than folded in.

The mechanism

TempDbSpaceInfo.UsedPercent is

TotalReservedMb / (TotalReservedMb + UnallocatedMb) * 100

and AlertEngine.CheckTempDbSpaceAsync fires when that clears TempDbSpaceThresholdPercent (default 80, in both SKUs).

Both halves of that denominator come from tempdb.sys.dm_db_file_space_usage, which reports the tempdb files as currently allocated. On a box with a pre-sized tempdb that denominator is effectively the ceiling, so the ratio is real headroom and the alert means what it says.

On Azure SQL Database it is not. The platform creates tempdb small and autogrows it toward the tier cap: 4 files, ~62 MB total on both 2-vCore tiers measured (GP_S_Gen5_2 and HS_S_Gen5_2, EngineEdition 5). So the ratio measures distance to the next autogrow, not distance to the ceiling.

The measurement

From #2512, on GP_S_Gen5_2, allocating ~57 MB into one #temp table:

total_reserved_mb unallocated_mb UsedPercent
idle 1.88 60.69 3.0%
after one #temp 59.75 2.69 95.7%

One ordinary temp table, and the server is over the 80% default with 60 MB at stake. That is not a tempdb running out of room; that is a tempdb that has not grown yet. Every Azure SQL Database target doing real work would page someone on its first busy minute.

The second threshold, failing the other way

FactScorer.ScoreTempDbVersionStore scores max_version_store_mb at absolute bars — >5000 MB CRITICAL, >2000 HIGH, >1000 MEDIUM — inherited from install/47_create_reporting_views.sql. Those are unreachable while tempdb is at its initial ~62 MB, so the arm scores 0.

This one is self-consistent (a >1 GB version store implies a >1 GB tempdb) and produces no false alerts, so it needs no change. It is worth knowing about anyway, because RCSI is on by default on Azure SQL Database, which makes the version store the tempdb consumer most likely to matter there — and the space-fraction arm is the only thing covering it until tempdb has grown past a gigabyte.

Options

  1. Size floor on the alert, mirroring ScorePlanCacheBloat, which already solves exactly this shape: if (fact.Metadata.GetValueOrDefault("single_use_size_mb") < 100.0) return 0.0; — "a high % on a trivially small footprint is noise". Cheapest, platform-neutral, one constant.
  2. Ceiling-aware denominator on Azure: compare against the tier's documented max tempdb data size instead of the current file size. Most correct, and the number is not in any DMV the collector reads today — it would need the service objective and a lookup table, which dates.
  3. Leave it, document it, let operators tune per fleet. Cheapest of all and the worst, because the default is what almost everyone runs.

What should decide it

Option 1 is the obvious move, and the whole question is the floor value — which is why this is not in #2512. It suppresses alerts existing on-prem and RDS targets get today, and I cannot measure what those customers rely on from here. Note that AlertEngineTests.TempDb_FiresAtThreshold_AndResolutionCarriesTheCurrentPercent uses a fixture totalling exactly 1000 MB, so a 1 GB floor flips an existing pin: the fixture is itself evidence that any floor silently redefines "small" for everyone.

The measurement that should settle it: the distribution of total_reserved_mb + unallocated_mb at the moment UsedPercent >= 80 across collected tempdb_stats history. If real on-prem fires cluster well above the candidate floor, the floor is free.

Whatever floor lands, it belongs on TempDbSpaceInfo next to UsedPercent so Lite and Darling cannot diverge — they share the type and the engine.

Activity

  1. erikdarlingdata commented on Aug 22, 2026

    @erikdarlingdata
    OwnerAuthor

    The ceiling is discoverable, so this is a denominator bug rather than a policy call. Probed the same GP_S_Gen5_2 live:

    tempdb.sys.database_files

    file type size max_size (pages) =
    tempdev ROWS 16 MB 2,097,152 16 GB
    tempdev2 ROWS 16 MB 2,097,152 16 GB
    tempdev3 ROWS 16 MB 2,097,152 16 GB
    tempdev4 ROWS 16 MB 2,097,152 16 GB
    templog LOG 16 MB 3,460,096 ~26 GB

    The ROWS ceiling is 4 × 16 GB = 65,536 MB. Current allocation is 62.44 MB — a thousandth of it.

    So the 57 MB #temp that reads as 95.7% full against the allocated size is 0.09% full against the cap the platform will actually let it grow to. The alert is not mistuned; it is measuring the wrong thing, and it happens to be nearly right on a box only because a pre-sized tempdb has already grown to its ceiling.

    What this changes

    The fix is not a size floor. A floor would suppress a real Azure alert (a genuinely full 64 GB tempdb still reads small at the moment it starts growing) while still firing on the autogrow boundary, and — as noted here — any floor value silently redefines "small" for every existing on-prem and RDS target.

    The fix is the denominator:

    • Where max_size is a real value, headroom is TotalReservedMb / SUM(max_size for ROWS files).
    • Where it is unlimited (-1), there is no ceiling to measure against and the current allocation is the only honest denominator — which is today's behaviour, so on-prem targets that grow without limit are unaffected.

    That makes UsedPercent mean the same thing on every engine: distance to the point where tempdb cannot grow any further, which is the thing worth paging someone about. Today it means "distance to the next autogrow", which is worth paging nobody.

    It also removes the reason #2512 needed this caveat at all: with the right denominator, enabling Azure collection does not arm an alert that fires on the first busy minute.

    Worth checking when implementing

    • LOG files are excluded above deliberately — dm_db_file_space_usage reports ROWS allocation, so mixing the log's cap into the denominator would understate usage. Confirm that against the collector's actual columns.
    • On-prem with a fixed max_size will start reporting a lower percentage than today wherever tempdb has not grown to its cap. That is a correction, not a regression, but it will move existing numbers and should be called out in the changelog.
    • AlertEngineTests' fixture sits at exactly 1000 MB total; check whether it implies a cap.
  2. added a commit that references this issue on Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions