Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ AWS_REGION=us-west-2
# SQLite (local dev):
DATABASE_URL=sqlite+aiosqlite:///./data/dev.db

# Connection pool tuning — applied to MySQL / PostgreSQL only (SQLite ignored).
# DB_POOL_PRE_PING is required for asyncpg: connections broken by task
# cancellation otherwise remain pooled and stall the next checkout.
# DB_POOL_SIZE=10
# DB_MAX_OVERFLOW=20
# DB_POOL_RECYCLE=1800
# DB_POOL_PRE_PING=true
# DB_POOL_TIMEOUT=10

# Engine config
ENGINES_CONFIG=./config.yaml
SQL_ROW_LIMIT=500
Expand Down
9 changes: 9 additions & 0 deletions backend/src/analytics_agent/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,15 @@ def get_api_key(self) -> str:
# Database — defaults to the user config dir; override via DATABASE_URL env var
database_url: str = f"sqlite+aiosqlite:///{_CONFIG_DIR}/data/agent.db"

# Connection pool — applied only to non-SQLite engines (MySQL / PostgreSQL).
# pool_pre_ping is critical for asyncpg: without it, a connection broken by
# task cancellation stays in the pool and the next checkout hangs forever.
db_pool_size: int = 10
db_max_overflow: int = 20
db_pool_recycle: int = 1800
db_pool_pre_ping: bool = True
db_pool_timeout: int = 10

# Engine config — defaults to the user config dir; override via ENGINES_CONFIG env var
engines_config: str = str(_CONFIG_DIR / "config.yaml")
sql_row_limit: int = 500
Expand Down
20 changes: 15 additions & 5 deletions backend/src/analytics_agent/db/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,21 @@
def _get_engine():
global _engine, _AsyncSessionFactory
if _engine is None:
_engine = create_async_engine(
settings.database_url,
echo=settings.log_level == "DEBUG",
connect_args={"check_same_thread": False} if "sqlite" in settings.database_url else {},
)
url = settings.database_url
is_sqlite = "sqlite" in url
kwargs: dict = {
"echo": settings.log_level == "DEBUG",
"connect_args": {"check_same_thread": False} if is_sqlite else {},
}
if not is_sqlite:
kwargs.update(
pool_size=settings.db_pool_size,
max_overflow=settings.db_max_overflow,
pool_recycle=settings.db_pool_recycle,
pool_pre_ping=settings.db_pool_pre_ping,
pool_timeout=settings.db_pool_timeout,
)
_engine = create_async_engine(url, **kwargs)
_AsyncSessionFactory = async_sessionmaker(_engine, expire_on_commit=False)
return _engine

Expand Down