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 inaa-eshars-prod-azmodulebut 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
| Role | SKU | min vCore | auto-pause | Rationale |
|---|---|---|---|---|
| Core (always-serving) | GP_S_Gen5_10 | 2 | off (-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_2 | 0.5 | on (60 min) | Tiny ceiling, low floor, pause reclaims idle compute. |
| UAT data (intermittent) | GP_S_Gen5_4 | 0.5 | on (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-auditon a P6 at 0% utilization. - Rebuild estate (
workload-env): thesql-databasemodule gains per-DBmin_capacity/auto_pause_delayinputs (defaults preserve prior 0.5 / 60 behaviour, so other envs are unaffected);prod.tfvarsmoves from DTU (P1/S1) to the GP_S design above. Applied via PR +justper repo rules. - Live estate (
rg-eshars-prod, not in IaC): converted by a one-time onlineaz sql db update --compute-model ServerlessSLO change — no data movement, no downtime. Rollback =--edition Premium --capacity 1000(back to P6). - The dormant
ScaleAzureSQLDBrunbook + 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.