1 min read
Azure SQL Database Serverless: Auto-Scaling and Auto-Pause for Variable Workloads
I wrote “Azure SQL Database Serverless: Auto-Scaling and Auto-Pause for Variable Workloads” to share practical, production-minded guidance on this topic.
How Serverless Differs from Provisioned
Unlike provisioned compute tiers where you pay for fixed resources 24/7, serverless:
- Automatically scales compute within a configured range
- Can auto-pause during periods of inactivity
- Bills only for compute used (storage is always billed)
Creating a Serverless Database
# Create serverless database with Azure CLI
az sql db create \
--resource-group myResourceGroup \
--server myserver \
--name ServerlessDB \
--edition GeneralPurpose \
--compute-model Serverless \
--family Gen5 \
--min-capacity 0.5 \
--capacity 4 \
--auto-pause-delay 60
Using T-SQL:
-- Create serverless database
CREATE DATABASE ServerlessDB
(
EDITION = 'GeneralPurpose',
SERVICE_OBJECTIVE = 'GP_S_Gen5_4',
MAXSIZE = 32 GB
);
-- Configure auto-pause delay (minutes)
ALTER DATABASE ServerlessDB
SET AUTO_PAUSE_DELAY = 60;
Understanding Auto-Pause Behavior
// C# handling for auto-pause warm-up
public class ServerlessDbConnection
{
private readonly string _connectionString;
public ServerlessDbConnection(string connectionString)
{
_connectionString = connectionString;
}
public async Task<SqlConnection> GetConnectionAsync()
{
var connection = new SqlConnection(_connectionString);
// Set longer timeout for potential auto-resume
connection.ConnectionTimeout = 120;
var stopwatch = Stopwatch.StartNew();
try
{
await connection.OpenAsync();
if (stopwatch.ElapsedMilliseconds > 10000)
{
// Database was likely paused and resumed
Console.WriteLine($"Database resumed in {stopwatch.ElapsedMilliseconds}ms");
}
return connection;
}
catch (SqlException ex) when (ex.Number == 40613)
{
// Database is resuming, retry with exponential backoff
return await RetryConnectionAsync();
}
}
private async Task<SqlConnection> RetryConnectionAsync()
{
var maxRetries = 5;
var delay = TimeSpan.FromSeconds(5);
for (int i = 0; i < maxRetries; i++)
{
await Task.Delay(delay);
delay = TimeSpan.FromSeconds(delay.TotalSeconds * 2);
try
{
var connection = new SqlConnection(_connectionString);
await connection.OpenAsync();
return connection;
}
catch (SqlException ex) when (ex.Number == 40613)
{
continue;
}
}
throw new Exception("Failed to connect after maximum retries");
}
}
Keeping the Database Warm
For applications that need predictable response times, you can prevent auto-pause:
# Python keep-alive service
import schedule
import time
import pyodbc
def keep_database_warm():
connection_string = (
"Driver={ODBC Driver 17 for SQL Server};"
"Server=myserver.database.windows.net;"
"Database=ServerlessDB;"
"Authentication=ActiveDirectoryMsi;"
)
try:
with pyodbc.connect(connection_string, timeout=30) as conn:
cursor = conn.cursor()
cursor.execute("SELECT 1")
cursor.fetchone()
print(f"Keep-alive ping successful at {time.strftime('%Y-%m-%d %H:%M:%S')}")
except Exception as e:
print(f"Keep-alive failed: {e}")
# Schedule ping every 30 minutes (before 60-min auto-pause)
schedule.every(30).minutes.do(keep_database_warm)
while True:
schedule.run_pending()
time.sleep(60)
Monitoring Serverless Costs
-- Check serverless billing metrics
SELECT
database_name,
start_time,
end_time,
sku,
avg_cpu_percent,
avg_memory_usage_percent,
avg_instance_cpu_percent,
DATEDIFF(SECOND, start_time, end_time) AS duration_seconds
FROM sys.server_resource_stats
WHERE database_name = 'ServerlessDB'
ORDER BY end_time DESC;
-- Calculate approximate compute cost
SELECT
CAST(SUM(
DATEDIFF(SECOND, start_time, end_time) *
(avg_cpu_percent / 100.0) *
vcores
) / 3600.0 AS DECIMAL(10,2)) AS approximate_vcore_hours
FROM sys.dm_db_resource_stats
CROSS APPLY (SELECT 4 AS vcores) AS config;
Configuring Min/Max vCores
# Update serverless configuration
az sql db update \
--resource-group myResourceGroup \
--server myserver \
--name ServerlessDB \
--min-capacity 1 \
--capacity 8 \
--auto-pause-delay 120
Best Practices for Serverless
- Set appropriate min vCores: Higher min reduces resume latency
- Configure auto-pause wisely: Balance cost savings vs. cold start impact
- Use connection retry logic: Always handle resume scenarios
- Monitor actual usage: Ensure workload matches serverless economics
// Entity Framework Core with retry policy
services.AddDbContext<MyDbContext>(options =>
{
options.UseSqlServer(connectionString, sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: new[] { 40613 }
);
sqlOptions.CommandTimeout(120);
});
});
Serverless is perfect for dev/test environments, infrequently used applications, and workloads with predictable quiet periods. For consistent high-utilization workloads, provisioned compute remains more cost-effective.\n\n## Takeaways\n\nAdd a concise, personal takeaway and recommended next steps here.\n