diff --git a/app/db.py b/app/db.py index 0911158..cd75e69 100644 --- a/app/db.py +++ b/app/db.py @@ -5,6 +5,7 @@ from datetime import UTC, datetime from sqlalchemy import Engine, create_engine, event from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker +from sqlalchemy.pool import NullPool from app.config import get_settings @@ -20,7 +21,10 @@ def create_sqlite_engine(database_url: str): db_path.parent.mkdir(parents=True, exist_ok=True) connect_args = {"check_same_thread": False, "timeout": 10} - engine = create_engine(database_url, connect_args=connect_args) + engine_kwargs = {"connect_args": connect_args} + if database_url.startswith("sqlite:///") and database_url != "sqlite:///:memory:": + engine_kwargs["poolclass"] = NullPool + engine = create_engine(database_url, **engine_kwargs) engine.dialect.connect_args = connect_args @event.listens_for(engine, "connect") diff --git a/tests/unit/test_db_stability.py b/tests/unit/test_db_stability.py index 6f5bd0e..9c898b5 100644 --- a/tests/unit/test_db_stability.py +++ b/tests/unit/test_db_stability.py @@ -1,4 +1,5 @@ import pytest +from sqlalchemy.pool import NullPool from app.db import check_database_integrity, checkpoint_sqlite_wal, create_sqlite_engine, ensure_sqlite_schema_compat @@ -11,6 +12,14 @@ def test_check_database_integrity_returns_ok_for_valid_sqlite_database(): engine.dispose() +def test_file_sqlite_engine_does_not_reuse_connections_after_operational_errors(tmp_path): + engine = create_sqlite_engine(f"sqlite:///{tmp_path / 'app.db'}") + try: + assert isinstance(engine.pool, NullPool) + finally: + engine.dispose() + + def test_check_database_integrity_raises_when_sqlite_reports_problem(monkeypatch): class FakeCursor: def execute(self, _sql):