azuresqlalchemyfastapisqlmodel

Azure SQL Database keeps disconnecting using SQLAlchemy Sessionmaker


I'm trying to get an azure sql db working. I am using FastAPI, SQLModel and SQLAlchemy

from sqlmodel import create_engine, SQLModel, Session



def init_db(settings: SqlDBSettings = SqlDBSettings()):
    connection_url = URL.create(
        settings.DRIVER, username=settings.UID, password=settings.PASSWORD,
        host=settings.HOST, port=1433, database=settings.DBNAME,
        query={
            "driver": "ODBC Driver 17 for SQL Server"
        }
    )
    return create_engine(connection_url, echo=True, connect_args={"check_same_thread": False}, pool_recycle=3600, pool_pre_ping=True)

engine = init_db()

Then i am using sessionmaker with the created engine

session_pool = sessionmaker(
class_=Session, bind=engine, expire_on_commit=False, autocommit=False, autoflush=False
)

Finally the following method is passed as dependency to all my endpoints

  def get_db_session():
    with session_pool() as session:
        yield session

E.g.:

@router.get("/files/{id}", response_model=FileTable)
def get_file_by_id(*, session = Depends(get_db_session), id: int):
    file = session.get(FileTable, id)
    return file

Everything works until after a couple of hours the database seems to disconnect and I get the following logs

2025-05-08T09:33:33.3775194Z INFO 169.254.129.3:53578 - "GET /api/v1/files/ HTTP/1.1" 500
    2025-05-08T09:33:33.4749055Z ERROR Exception in ASGI application
    ...
    2025-05-08T09:33:33.4749533Z raise exc.InvalidatePoolError()
    2025-05-08T09:33:33.4749547Z sqlalchemy.exc.InvalidatePoolError: ()
2025-05-08T09:33:33.4750228Z pyodbc.Error: ('HY000', "[HY000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Database 'xyz' on server 'xyz.net' is not currently available. Please retry the connection later.

I found that there is an automatic pause option available as i run my database serverless. If i disable this option in azure then the problem does not occur anymore.

(https://www.sqlshack.com/automatic-pause-and-resume-an-azure-sql-database/)

However, this introduces permanent costs of a couple of $s per day which i would like to avoid.

Is the code above using the sessionmaker correct and why does pool_recycle and pre_ping not work as expected? Reading in the documentation i thought it will reestablish a connection before actually executing the query


Solution

  • -Is the code above using the sessionmaker correct

    Ensure you are using , Import sessionmaker from sqlalchemy.orm, sqlalchemy.orm.Session for compatibility with sessionmaker.

    from sqlmodel import SQLModel, create_engine, Session
    from sqlalchemy.orm import sessionmaker
    from sqlalchemy.engine import URL
    
    def init_db(settings: SqlDBSettings = SqlDBSettings()):
        connection_url = URL.create(
            settings.DRIVER,
            username=settings.UID,
            password=settings.PASSWORD,
            host=settings.HOST,
            port=1433,
            database=settings.DBNAME,
            query={"driver": "ODBC Driver 17 for SQL Server"}
        )
        return create_engine(
            connection_url,
            echo=True,
            connect_args={"check_same_thread": False},
            pool_recycle=3600,
            pool_pre_ping=True,
        )
    
    engine = init_db()
    
    Session_pool = sessionmaker(
        autocommit=False,
        autoflush=False,
        bind=engine,
        class_=Session  # sqlmodel.Session is fine here
    )
    
    def get_db_session():
        with Session_pool() as session:
            yield session
    

    Why does pool_recycle and pre_ping not work as expected?

    -pool_recycle=3600: It reconnects if a connection is over an hour old, but this does not help if the database was paused.
    -pool_pre_ping=True: It checks if the connection is still alive before using it.

    With Azure SQL serverless, pool_pre_ping does not always work well when the database is paused. Resuming can take up to 60 seconds, but the connection check may fail right away. The driver gives an error (HY000) that is not always handled properly, causing SQLAlchemy to drop the whole connection pool.

    Follow this document for more details : Dealing with Disconnects

    You can try the below recommended steps to fix the issue:

    -You can reconnect on faliure with retry logic by wrapping up DB access . This approach retries the operation a few times while Azure SQL starts:

    from tenacity import retry, wait_fixed, stop_after_attempt
    from sqlalchemy.exc import OperationalError
    
    @retry(wait=wait_fixed(5), stop=stop_after_attempt(3), retry=retry_if_exception_type(OperationalError))
    def safe_get_file_by_id(session, id):
        return session.get(FileTable, id)
    

    -If your app is not high traffic, then you can use poolclass as NullPool while creating engine as

    from sqlalchemy.pool import NullPool
    engine = create_engine(connection_url, poolclass=NullPool)
    

    This disables connecting pooling.

    Follow this document for more : NullPool

    Microsoft Resources : Serverless compute tier for Azure SQL Database