Diagnose Azure SQL Database DTU and vCore Throttling
Sustained 100% DTU or vCore use, timeouts, and errors such as 40501, 10929, 40544, or 40549 indicate Azure SQL Database resource-governor throttling. Confirm the saturated resource before scaling.
11 Jul 2026, 11:27 UTC

The problem and the useful takeaway
Your application begins timing out, and Azure SQL Database returns errors such as 40501, 10929, 40544, or 40549. The database is not necessarily down; the resource governor is likely queuing, slowing, or rejecting requests because the workload has reached the configured limit.
Takeaway: do not scale blindly. First confirm which resource dimension is saturated—CPU, data IO, log IO, or workers—then apply the matching fix. Scaling the wrong dimension increases cost without reducing latency.
Terms used in this guide
- DTU (database transaction unit): a blended measure of CPU, memory, and IO used by the DTU purchase model for single databases and elastic pools.
- vCore: a virtual CPU core used by the vCore purchase model.
- Resource governor: the engine component that enforces service-objective or elastic-pool limits by queuing or rejecting requests.
- Elastic pool: a shared pool of eDTUs or vCores that multiple databases can draw from.
Recognizable condition and likely cause
| Observed symptom | Likely saturated dimension | Where to check first |
|---|---|---|
| Steady 100% DTU or vCore percentage with rising latency | CPU | sys.dm_db_resource_stats |
| High log write percentage with long-running writes | Log IO | sys.dm_db_resource_stats, sys.dm_db_wait_stats |
| Errors 40544 or 40549, many concurrent sessions | Workers or sessions | sys.dm_exec_sessions, error message context |
| Throttling across several databases in one pool | Elastic pool limit | Pool-level metrics and sys.dm_db_resource_stats per database |
Ordered diagnostic checks
Check 1: Identify the saturated resource dimension
Run this query in the affected user database, not in master. It requires VIEW DATABASE STATE permission.
-- Run in: the affected user database (e.g., [SalesDB])
-- Required permission: VIEW DATABASE STATE
-- Placeholder: replace [IncidentStart] and [IncidentEnd] with actual times
SELECT TOP (20) end_time,
avg_cpu_percent,
avg_data_io_percent,
avg_log_write_percent,
avg_worker_percent,
avg_session_percent
FROM sys.dm_db_resource_stats
WHERE end_time BETWEEN '[IncidentStart]' AND '[IncidentEnd]'
ORDER BY end_time DESC;
Expected check: one or more percentage columns sit near 100 during the incident window. If CPU is high but data IO and log IO are low, the bottleneck is compute. If log write percentage is high, transactions are waiting on log throughput. Verify current metric retention and permission names for your tier before relying on this query in production.
Risk: read-only query; no data is changed. Results reflect recent history only and differ by purchase model and tier.
Check 2: Confirm throttle-related waits
-- Run in: the affected user database
-- Required permission: VIEW DATABASE STATE
SELECT wait_type,
wait_time_ms,
signal_wait_time_ms,
waiting_tasks_count
FROM sys.dm_db_wait_stats
WHERE wait_type IN ('RESOURCE_SEMAPHORE','THROTTLE','IO_THROTTLE')
ORDER BY wait_time_ms DESC;
Expected check: RESOURCE_SEMAPHORE indicates memory grant contention; THROTTLE indicates resource-governor rejection or delay; IO_THROTTLE indicates IO governance. Correlate wait_time_ms growth with the incident timestamps from Check 1.
Check 3: Find blocked or suspended requests
-- Run in: the affected user database
-- Required permission: VIEW DATABASE STATE
SELECT r.session_id,
r.status,
r.command,
r.wait_type,
r.wait_time_ms,
r.blocking_session_id,
s.host_name,
s.program_name
FROM sys.dm_exec_requests AS r
JOIN sys.dm_exec_sessions AS s ON r.session_id = s.session_id
WHERE r.status IN ('suspended','runnable')
OR r.blocking_session_id <> 0
ORDER BY r.wait_time_ms DESC;
Expected check: a non-zero blocking_session_id identifies a leading session. Use sys.dm_tran_locks to follow the blocking chain if multiple sessions are involved.
Risk: read-only; output can be large on busy systems.
Check 4: Read the error message context
Error 40501 can reflect a single-database DTU limit, a server-level limit, or an elastic-pool limit. Errors 40544 (session limit) and 40549 (pool limit) point to different boundaries. Capture the full error message and the database name before deciding whether to scale the database or the pool.
Fixes matched to the diagnostic finding
- CPU saturation: identify top queries through Query Store, add missing indexes, or parameterize ad-hoc batches. If queries are already tuned, scale up the service objective.
- Log IO saturation: reduce transaction batch size, avoid large implicit transactions, or move to a higher service tier with more log throughput.
- Worker or session saturation: enable connection pooling in the application, shorten open connections, or scale up. For an elastic pool, move hot databases out or raise pool eDTUs or vCores.
- Elastic pool saturation: raise the pool eDTU or vCore limit, or relocate the highest-utilization databases to separate service objectives.
Scaling is state-changing: rollback and risk
Changing a service objective or elastic-pool size can cause brief connection drops and immediate billing changes. Perform the change in a maintenance window and confirm the application uses retry logic for transient errors.
Rollback: if the new service objective does not reduce saturation or the cost is unacceptable, scale back to the previous service objective or pool size. Expect another brief disconnect during the rollback, and validate connections afterward.
Limitations
- Behavior differs by purchase model and tier: DTU, vCore, Hyperscale, and serverless expose different metrics and scaling characteristics.
- Query Store must be enabled and set to read-write before the incident; it does not provide retroactive plan history.
- sys.dm_db_resource_stats is database-scoped and does not directly reveal server-level or pool-level pressure.
- This draft requires current verification: confirm DMV permissions, metric retention, and tier-specific wait types in the current Azure documentation before production use.
Practical way to check the result
After applying a fix, rerun Check 1 and Check 2. The saturated percentage should remain below your alert threshold—for example, under 80 percent—during a representative workload, and throttle-related wait_time_ms should stop growing. Create Azure Monitor alerts on DTU percentage or vCore percentage at 80–90 percent to catch saturation before users see timeouts.
Escalation criteria
- Throttling continues after scaling the database or pool.
- Errors occur during low-utilization off-peak periods.
- Many databases in one pool or on one server are affected at the same time.
If any criterion applies, open a support case and attach resource metrics, wait stats, and the full error logs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.