Handling Azure SQL Serverless Auto-Pause in Connection Pools

This guide is part of Azure SQL Connection Management. The serverless compute tier of Azure SQL Database pauses a database that has had no sessions and no user CPU for the configured auto-pause delay, and bills only for storage while it is paused. Connection pools interfere with that in both directions. A pool that keeps idle connections open — or a readiness probe that runs SELECT 1 every ten seconds — counts as a session, so the database never pauses and the cost saving never materialises. And when the database has paused, the first login that wakes it is rejected while it resumes, so the first request after a quiet period fails:

Microsoft.Data.SqlClient.SqlException (0x80131904): Database 'orders' on server 'orders-sql' is not currently
available. Please retry the connection later. If the problem persists, contact customer support, and provide
them the session tracing ID of '7c1e0c9a-…'.
   Error Number:40613,State:1,Class:20

The objective is to let the database pause when the application is genuinely idle, and to make the resume invisible to users by retrying the connection within a bounded time.

Rapid incident diagnosis

Is the database pausing at all? The activity log records pause and resume operations. If a database on the serverless tier shows no pauses over a weekend with no traffic, something is holding sessions:

az monitor activity-log list --resource-id "$(az sql db show -g rg-orders -s orders-sql -n orders --query id -o tsv)" \
  --start-time 2026-09-12T00:00:00Z --query "[?contains(operationName.value, 'pause') || contains(operationName.value, 'resume')].{op:operationName.value, at:eventTimestamp}" -o table

Who holds sessions while the application is idle? Query the database during a quiet period:

SELECT s.program_name, s.host_name, s.login_name, COUNT(*) AS sessions,
       MAX(DATEDIFF(SECOND, s.last_request_end_time, SYSDATETIME())) AS max_idle_seconds
FROM sys.dm_exec_sessions AS s
WHERE s.is_user_process = 1
GROUP BY s.program_name, s.host_name, s.login_name
ORDER BY sessions DESC;

Typical findings are pools with a non-zero minimum (Min Pool Size, HikariCP minimumIdle), long idle timeouts, monitoring agents, and application health endpoints that query the database.

Are errors caused by resume? Error 40613 clustered at the first requests after idle periods, with successes a few seconds to a minute later, is the resume signature. Correlate application errors with resume events from the activity log. If 40613 appears during steady traffic, it is a reconfiguration or failover instead — still transient, but not caused by auto-pause, and covered in Configuring Retry Policies for Azure SQL Transient Faults.

Is the retry budget too short? If the application does retry but still fails, compare its total retry window with the observed resume time. A policy of three attempts one second apart gives up after about three seconds; resume usually takes longer.

What keeps a serverless Azure SQL database awake Traffic stops at 18:00. With a pool minimum of 4 idle connections and a 10 second readiness probe the database never pauses. With a pool minimum of 0, a short idle timeout and a probe that does not touch the database, it pauses after the delay and bills only storage. After traffic stops at 18:00 minimumIdle 4, DB probe every 10 s sessions never reach zero: compute billed all night minimumIdle 0, idle timeout 5 min delay paused: storage billing only 18:00 08:00 the probe must check the process, not open a database session
Two conditions must hold for auto-pause: no sessions and no user CPU for the whole delay. A single idle pool connection or a periodic probe resets the clock.

Mathematical sizing or parameter formula

When can the database pause? The last pool connection closes after the pool’s idle timeout; the database then needs a full auto-pause delay with no sessions:

earliest_pause = last_request + pool_idle_timeout + autopause_delay

Any periodic database access with an interval shorter than pool_idle_timeout + autopause_delay prevents pausing entirely. With a 5-minute idle timeout and a 60-minute delay, a probe or scheduled job that queries the database more often than every 65 minutes keeps the database awake.

What does resume cost the first request? The first login fails, and the client must retry until the database is online. The retry window has to exceed the resume time:

retry_window = Σ_{i=1..n} min( base × 2^(i−1), max_delay )  ≥  p99_resume_time

With a base delay of 2 seconds, a maximum delay of 15 seconds and 6 attempts: 2 + 4 + 8 + 15 + 15 + 15 = 59 seconds of waiting, enough for a typical resume measured in seconds to tens of seconds, with margin. Compare this window with the request deadline: an interactive request with a 30-second gateway timeout cannot wait 59 seconds, so either accept a failed first request after long idle periods or disable auto-pause for user-facing databases.

Is pausing worth it? Compute billing on serverless accrues per second while the database is online, subject to a minimum vCore and memory charge. A rough monthly saving is:

saving ≈ paused_hours × min_vcores × price_per_vcore_hour

A development database idle 16 hours a day saves roughly two-thirds of its compute cost; a production database idle only a few minutes at a time saves nothing and pays for resume latency.

Connection retry window against resume time Six connection attempts with delays of 2, 4, 8, 15, 15 and 15 seconds give a cumulative retry window of 59 seconds, covering a resume that completes at about 25 seconds. Connection attempts after a paused database receives a login (0 to 60 s) 40613 attempts 1 to 4 fail while resuming database online at ~25 s attempt 5 at 29 s succeeds attempt 6 and the rest of the window are never needed 0 s 59 s total window delays: 2, 4, 8, 15, 15, 15 seconds
Exponential backoff with a cap turns six attempts into nearly a minute of retry window, long enough to cover a typical resume without retrying in a tight loop.

Exact remediation & configuration

Let the pool empty when idle. For SqlClient, set a zero minimum and let the pool’s own pruning close idle connections; SqlClient removes connections that have been idle for roughly four to eight minutes when the pool is above its minimum:

Server=tcp:orders-sql.database.windows.net,1433;Database=orders;Authentication=Active Directory Default;
Min Pool Size=0;Max Pool Size=40;Connect Timeout=30;ConnectRetryCount=3;ConnectRetryInterval=10;Encrypt=True

For HikariCP against the Microsoft JDBC driver:

spring:
  datasource:
    url: jdbc:sqlserver://orders-sql.database.windows.net:1433;database=orders;encrypt=true;connectRetryCount=3;connectRetryInterval=10;loginTimeout=30
    hikari:
      minimum-idle: 0            # allow the pool to empty
      maximum-pool-size: 20
      idle-timeout: 300000       # close idle connections after 5 minutes
      connection-timeout: 45000  # longer than a typical resume
      initialization-fail-timeout: -1   # start even if the database is paused

initialization-fail-timeout: -1 lets the application start while the database is paused instead of failing its first connection attempt at boot.

Retry the connection, with backoff, for 40613. In .NET, configure SqlClient’s retry logic so Open retries transient errors, including 40613, with exponential backoff:

var retry = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(new SqlRetryLogicOption
{
    NumberOfTries   = 6,
    DeltaTime       = TimeSpan.FromSeconds(2),
    MaxTimeInterval = TimeSpan.FromSeconds(15),
    TransientErrors = new[] { 40613, 40197, 40501, 49918, 49919, 49920, 4221 }
});

await using var conn = new SqlConnection(connectionString) { RetryLogicProvider = retry };
await conn.OpenAsync(cancellationToken);

With Entity Framework Core, the execution strategy covers both connection and command failures:

options.UseSqlServer(connectionString, sql => sql.EnableRetryOnFailure(
    maxRetryCount: 6, maxRetryDelay: TimeSpan.FromSeconds(15), errorNumbersToAdd: new[] { 40613 }));

Keep health checks off the database. A liveness or readiness probe should check that the process can serve, not that the database is awake. Expose database health on a separate endpoint used by monitoring at a long interval, or not at all for serverless databases:

readinessProbe:
  httpGet: { path: /health/ready, port: 8080 }    # process-only checks
  periodSeconds: 10

Decide per database. For user-facing production databases where the first request after idle must not wait, set auto-pause off (--auto-pause-delay -1) and keep the serverless tier only for its compute autoscaling. For development, test and batch databases, keep auto-pause on and accept the resume delay:

az sql db update -g rg-orders -s orders-sql -n orders-dev --auto-pause-delay 60
az sql db update -g rg-orders -s orders-sql -n orders      --auto-pause-delay -1
Where resume retries fit in the request path A request asks the pool for a connection. The pool opens a new physical connection, which retries error 40613 with backoff for up to 59 seconds, bounded by a 45 second pool connection timeout and the request deadline. HTTP request deadline 30 s interactive Pool acquire connection-timeout 45 s Open connection 40613 while resuming Database online after resume retry batch jobs can wait out a resume; interactive requests often cannot so pause development and batch databases, not user-facing ones
Connection retries sit inside the pool's acquisition timeout, which sits inside the request deadline. For interactive paths the full resume may not fit, which is the argument for disabling auto-pause there.

Validation & verification

Pausing happens. Stop traffic to a test environment and confirm the database pauses after pool_idle_timeout + autopause_delay. The database state is visible directly:

az sql db show -g rg-orders -s orders-sql -n orders-dev --query "{status:status, pausedDelay:autoPauseDelay}" -o table

status should become Paused. If it does not, rerun the sys.dm_exec_sessions query from the diagnosis to find the remaining session.

Resume is absorbed. With the database paused, send a single request that needs the database and record its latency and outcome. It must succeed, with latency roughly equal to the resume time. Application logs should show the retried 40613 attempts at debug level, not as request failures.

No retry storm. During resume, many requests may arrive at once. Confirm total login attempts stay bounded — each pool opens at most maximum-pool-size connections and each retries at most six times:

SELECT COUNT(*) AS failed_logins_last_hour
FROM sys.event_log
WHERE event_type = 'connection_failed' AND start_time > DATEADD(HOUR, -1, SYSUTCDATETIME());

Run it in the master database of the logical server; the count should be small and proportional to instances, not to requests.

Probes are silent. With the application running but idle, sys.dm_exec_sessions should show no sessions from the application’s program_name once the pool idle timeout has passed.

Designing around resume latency

When a user-facing database must stay on the serverless tier with auto-pause enabled, the application can be designed so that resume is rarely visible.

Warm before users arrive. If traffic follows a working-day pattern, a scheduled job that issues one query a few minutes before the day starts resumes the database while nobody is waiting. The job must then stop, so the database can pause again in the evening:

# Azure Container Apps job, weekdays 07:50 UTC
schedule: "50 7 * * 1-5"
command: ["sqlcmd", "-S", "orders-sql.database.windows.net", "-d", "orders-dev", "-G", "-Q", "SELECT 1"]

Return a friendly response while resuming. For internal tools, catching a 40613 that outlasts the retry window and returning a page that says the database is starting, with automatic refresh, is better than a generic error. The request can trigger resume and the user retries seconds later.

Separate always-on paths from pausable ones. A service whose authentication data lives in the same database as its reporting data cannot let either pause. Moving small, frequently read data to a separate always-on database or a cache lets the large, occasionally used database pause without affecting login.

Pattern Resume visibility Cost effect
Auto-pause off None Minimum compute billed continuously
Scheduled warm-up before business hours Rare, outside the window Pauses overnight and at weekends
Retry window covering resume Slow first request Pauses whenever idle
Friendly “starting” response Visible but understandable Pauses whenever idle
Split hot and cold data Hot path never waits Hot database always on, cold one pauses

Measure resume time for your database rather than relying on typical figures. Pause it deliberately, send a single login, and record the time until a query succeeds; repeat several times at different sizes of data and different times of day. The p99 of those measurements is the number the retry window and any user-facing timeout must cover.

Frequently Asked Questions

Does a paused database lose pooled connections?
The pool should already be empty when the database pauses, because pausing requires zero sessions. If a pool somehow held a connection, that connection is broken by the pause and fails on next use; pools with validation discard it and open a new one, which triggers the resume.
Will ConnectRetryCount alone handle resume?
It helps, but its window — ConnectRetryCount × ConnectRetryInterval — is modest by default and fixed rather than exponential. Configurable retry logic or an execution strategy gives better control over the total window and which errors are retried.
Does elastic pool or Hyperscale behave the same?
Auto-pause applies to the serverless compute tier of single databases. Databases in elastic pools do not auto-pause individually; Hyperscale serverless has its own auto-pause behaviour and limits, so check the tier’s current documentation before relying on it.
Can a pool minimum keep the database warm cheaply?
Keeping one connection open prevents auto-pause entirely, so it costs the full minimum compute charge around the clock. If that is acceptable, disabling auto-pause explicitly is clearer than relying on a pool setting to do it implicitly.
Does a failed login during resume count against any limit?
Failed logins are recorded but do not consume session or worker limits. The concern is volume: thousands of clients retrying in tight loops during resume add load at the worst moment, which is why retries need backoff and a cap.