Skip to content

INFRA-0011: Azure SQL on vCore Serverless (GP_S_Gen5), Sized from Measured Load

Status

Proposed — 2026-06-24 (rebuild-estate IaC staged; live-estate conversion awaiting go)

Date

2026-06-24

Context

eSHARS production Azure SQL databases run on the DTU purchasing model at fixed Premium tiers — db-eshars-prod and db-eshars-prod-audit on P6 (1000 DTU), db-eshars-uat-data on P2 (250 DTU) — provisioned 24×7. A 7-day, 5-minute telemetry pull (investigation 2026-06-24) showed:

  • The DBs are idle ~98% of the time (median CPU 0.2%, only 1.8% of samples > 40% CPU).
  • Load is CPU-bound at every spike — data-read IO peaks 27%, log-write 23%, even when CPU hits 99%.
  • Spikes are short and patterned: a daily 05:00 UTC batch and weekday US business-hours interactive load.
  • A scale-up/down runbook (ScaleAzureSQLDB) and schedules already exist in aa-eshars-prod-azmodule but are all disabled — so the P6 sits at full capacity continuously.
  • Memory sits at a flat 88%, verified via DMVs to be benign buffer-pool caching (87.3% buffer pool, PLE 1334s), not working-set pressure.

This profile — deep idle punctuated by short CPU bursts — is the textbook fit for vCore serverless, which auto-scales compute per-second and bills only what’s used.

Decision

Standardise eSHARS Azure SQL on the vCore General Purpose Serverless tier (GP_S_Gen5), sized to measured peak load, replacing the DTU Premium model.

Rejected alternatives:

  • Business Critical — adds local-SSD + HA replicas + low-latency. Avg utilization is near zero; no latency/HA-replica requirement justifies the cost. Overkill.
  • Hyperscale — one-way conversion, oriented at multi-TB DBs and fast restore; doesn’t fit the BACPAC import/rebuild model and the DBs aren’t large enough. Skip.
  • DTU model (current) — legacy, opaque, and the source of the 24×7-at-full-tier waste. vCore is the strategic, transparent path and aligns with the rebuild estate, which is already GP_S.
  • Provisioned vCore GP — viable for the always-on core, but the idle profile means serverless wins even with auto-pause off (the core draws only ~13% of its possible vCore-hours).

Sizing rules

RoleSKUmin vCoreauto-pauseRationale
Core (always-serving)GP_S_Gen5_102off (-1)Max 10 covers the ~7.9-vCore CPU peak with headroom; min 2 keeps batch/business-hours load off a cold floor; pause off avoids cold-start latency on a live prod core.
Log / Audit (near-idle)GP_S_Gen5_20.5on (60 min)Tiny ceiling, low floor, pause reclaims idle compute.
UAT data (intermittent)GP_S_Gen5_40.5on (360 min)UAT genuinely idles nights/weekends; generous pause delay given 350 GB resume cost.

Auto-pause is governed by size + duty cycle, not by tier: enable it only where the DB both genuinely idles and is cheap to resume. Storage cost is identical across compute models, so DB size never argues against serverless — it only argues against auto-pause on a large always-on DB.

Consequences

  • Cost: compute ≈ $8,367/mo → ~$573/mo (≈ $93k/yr saved) across the three measured DBs. The single largest line is db-eshars-prod-audit on a P6 at 0% utilization.
  • Rebuild estate (workload-env): the sql-database module gains per-DB min_capacity / auto_pause_delay inputs (defaults preserve prior 0.5 / 60 behaviour, so other envs are unaffected); prod.tfvars moves from DTU (P1/S1) to the GP_S design above. Applied via PR + just per repo rules.
  • Live estate (rg-eshars-prod, not in IaC): converted by a one-time online az sql db update --compute-model Serverless SLO change — no data movement, no downtime. Rollback = --edition Premium --capacity 1000 (back to P6).
  • The dormant ScaleAzureSQLDB runbook + 4 schedules become redundant and should be decommissioned once serverless is live.
  • Watch item: first-week PLE/CPU monitoring on the core DB after conversion to confirm the 30 GB RAM (vs ~64 GB on P6) holds the working set — DMV evidence says it will.