As a Data Analyst, I recently faced a frustrating issue while automating a daily data processing task in Azure.
The goal was simple: run a scheduled job every morning to process data and sync it to an Azure SQL Database. When I ran the code manually, it worked perfectly. But when the scheduled job (via Azure Functions or Synapse) triggered at 6:00 AM, it crashed immediately.
Here is the solution to fixing the "Database not available" error without increasing your Azure bill.
The Problem
The job failed consistently with Error 40613:
(pyodbc.Error) ('HY000', "[HY000] [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Database 'xxxxxxx' on server 'xxxxxxxxxxxxxxxxxx' is not currently available. Please retry the connection later. If the problem persists, contact customer support, and provide them the session tracing ID of '{...}'. (40613) (SQLDriverConnect)") (Background on this error at: https://sqlalche.me/e/20/dbapi)
Why this happens
I am using the Azure SQL Database Serverless tier. To save costs, this tier features Auto-pause. If no one uses the database for a set period (e.g., 1 hour), Azure puts it to sleep.
When my scheduled job runs in the morning, the database is cold. It takes approximately 60 to 90 seconds for Azure to spin the compute back up. The default Python connection string gives up before the database is ready.
The Expensive Fix (Don't do this)
My first instinct was to disable Auto-pause.
- Go to Azure Portal > SQL Database.
- Click Compute + storage.
- Uncheck Enable auto-pause
The result: The error stopped, but my costs tripled. I was paying for compute 24/7 for a job that only runs for 10 minutes a day. This is not efficient.
The Smart Fix: Intelligent Retry Logic
Instead of keeping the server running all night, we should write code that is patient enough to wait for the server to wake up.
I wrote a custom wrapper for the SQLAlchemy engine that handles the specific behavior of Azure Serverless cold starts.
The Code
Here is the robust connection function. It attempts to connect, and if it detects the database is sleeping, it waits and retries until the server is back online.
import time
from sqlalchemy import create_engine, text
from sqlalchemy.exc import OperationalError, InterfaceError
def connect_sql_engine(max_retries=10, delay_seconds=30):
"""
Attempts to connect to the database.
If the database is in serverless pause state, it retries until it wakes up.
max_retries: Default 10. Covers ~5 minutes of startup time.
delay_seconds: Default 30s. Wait time between attempts.
"""
# Replace with your credentials or use Environment Variables (Recommended)
server = 'your-server.database.windows.net'
database = 'your-database'
username = 'your-username'
password = 'your-password'
# LoginTimeout=30 gives the driver time to negotiate the handshake
connection_string = (
f'mssql+pyodbc://{username}:{password}@{server}/{database}'
f'?driver=ODBC+Driver+18+for+SQL+Server&LoginTimeout=30'
)
# Create the engine with connection pooling enabled
engine = create_engine(
connection_string,
fast_executemany=True, # Optimized for bulk inserts
pool_pre_ping=True, # Checks connection health before usage
pool_recycle=1800
)
print(f"Attempting to connect to {database}...")
for attempt in range(1, max_retries + 1):
try:
# Try to execute a simple query to wake the DB
with engine.connect() as conn:
conn.execute(text("SELECT 1"))
print(">>> Success: Database is connected and awake!")
return engine
except (OperationalError, InterfaceError) as e:
print(f"Attempt {attempt}/{max_retries} failed. Database might be auto-paused.")
print(f"Error details: {e}")
print(f"Waiting {delay_seconds} seconds for wake-up...")
time.sleep(delay_seconds)
# If we reach here, the database is genuinely down or credentials are wrong
raise Exception(">>> Failed to wake up the database after multiple attempts.")
How it works
-
The Loop: It tries to run
SELECT 1. This is a lightweight query that forces Azure to trigger the resume process. -
The Trap: If it catches an
OperationalError(which covers the 40613 code), it pauses the script for 30 seconds usingtime.sleep(). -
The Success: Once Azure allocates the compute (usually after attempt 2 or 3), the connection succeeds, and the function returns the active
engineobject for your pipeline to use.
Summary
Don't change your infrastructure to fit your code; change your code to fit the infrastructure. By handling the "cold start" in Python, you keep the cost benefits of Serverless architecture while maintaining the reliability of a Production environment.
Happy coding!
Explore more
Thank you for taking the time to explore data-related insights with me. I appreciate your engagement.
Top comments (0)