Skip to content

Commit 5c7f26c

Browse files
olivermeyerclaude
andcommitted
fix(database): rename max_overflow to pool_max_overflow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b7976a6 commit 5c7f26c

3 files changed

Lines changed: 38 additions & 34 deletions

File tree

src/aignostics_foundry_core/AGENTS.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This file provides an overview of all modules in `aignostics_foundry_core`, thei
1717
| **log** | Configurable loguru logging initialisation | `logging_initialize(filter_func=None, *, context=None)`, `LogSettings` (env-prefix configurable), `InterceptHandler` for stdlib-to-loguru bridging |
1818
| **sentry** | Configurable Sentry integration | `sentry_initialize(integrations, *, context=None)`, `SentrySettings` (env-prefix configurable), `set_sentry_user(user, role_claim)` for Auth0 user context |
1919
| **service** | FastAPI-injectable base service | `BaseService` ABC with `get_service()` (cached per-class FastAPI `Depends` factory), `key()`, and abstract `health()` / `info()` methods; concrete subclasses implement health checks and module info |
20-
| **database** | Async SQLAlchemy session management + DB settings | `DatabaseSettings` (`OpaqueSettings` subclass; env prefix defaults to `{ctx.env_prefix}DB_`; `get_url()` with optional `db_name` substitution); `init_engine(db_url=None, pool_size=None, max_overflow=None, pool_timeout=None)` — all params optional, fall back to active context when `None`; `dispose_engine()`, `get_db_session()` (FastAPI dependency), `execute_with_session(func, …)`, `cli_run_with_db(func, …, db_url=None)`, `cli_run_with_engine(func, …, db_url=None)`, `with_engine` dual-mode decorator (supports `@with_engine`, `@with_engine()`, `@with_engine(db_url=…)`); auto-resets engine after `fork()` |
20+
| **database** | Async SQLAlchemy session management + DB settings | `DatabaseSettings` (`OpaqueSettings` subclass; env prefix defaults to `{ctx.env_prefix}DB_`; `get_url()` with optional `db_name` substitution); `init_engine(db_url=None, pool_size=None, pool_max_overflow=None, pool_timeout=None)` — all params optional, fall back to active context when `None`; `dispose_engine()`, `get_db_session()` (FastAPI dependency), `execute_with_session(func, …)`, `cli_run_with_db(func, …, db_url=None)`, `cli_run_with_engine(func, …, db_url=None)`, `with_engine` dual-mode decorator (supports `@with_engine`, `@with_engine()`, `@with_engine(db_url=…)`); auto-resets engine after `fork()` |
2121
| **cli** | Typer CLI preparation utilities | `prepare_cli(cli, epilog, *, context=None)` — discovers and registers subcommands via `locate_implementations`, sets epilog recursively, installs `no_args_is_help` workaround; `no_args_is_help_workaround(ctx)` — raises `typer.Exit` when no subcommand is invoked |
2222
| **boot** | Application / library boot sequence | `boot(context, sentry_integrations, log_filter, show_cmdline)` — runs once per process: parses `--env` CLI args, initialises logging and Sentry, amends the SSL trust chain via *truststore* and *certifi*, and logs boot/shutdown messages |
2323
| **user_agent** | Parameterised HTTP user-agent string builder | `user_agent(project_name, version, repository_url)` — builds `{project_name}-python-sdk/{version} (…)` string including platform info, current test, and GitHub Actions run URL |
@@ -220,7 +220,7 @@ This file provides an overview of all modules in `aignostics_foundry_core`, thei
220220

221221
- **Purpose**: Provides a self-contained `OpaqueSettings` subclass that reads database connection parameters from env vars. The env prefix defaults to `{FoundryContext.env_prefix}DB_` when not supplied, enabling zero-boilerplate DB configuration once a `FoundryContext` is installed.
222222
- **Key Features**:
223-
- `DatabaseSettings(OpaqueSettings)` — fields: `url: SecretStr` (required), `pool_size: int = 10`, `max_overflow: int = 10`, `pool_timeout: float = 30.0`, `db_name: str | None = None`
223+
- `DatabaseSettings(OpaqueSettings)` — fields: `url: SecretStr` (required), `pool_size: int = 10`, `pool_max_overflow: int = 10`, `pool_timeout: float = 30.0`, `db_name: str | None = None`
224224
- `__init__(_env_prefix=None, **kwargs)` — when `_env_prefix` is `None`, lazy-imports `get_context` and uses `f"{ctx.env_prefix}DB_"` as the prefix (avoids a circular import at module load time)
225225
- `get_url() -> str` — returns the raw URL from the secret; if `db_name` is set, replaces the path component in the URL (e.g. `…/postgres``…/mydb`) while preserving scheme, host, port, query, and fragment
226226
- `model_config = SettingsConfigDict(extra="ignore")` — extra env vars are silently ignored
@@ -323,12 +323,12 @@ This file provides an overview of all modules in `aignostics_foundry_core`, thei
323323

324324
- **Purpose**: Manages a process-level async database engine singleton, providing session injection for FastAPI routes, background jobs, and CLI commands. All public functions accept optional DB-config params and fall back to the active `FoundryContext.database` when they are `None`.
325325
- **Key Features**:
326-
- `init_engine(db_url=None, pool_size=None, max_overflow=None, pool_timeout=None)` — initialises the global `AsyncEngine` and `async_sessionmaker`; subsequent calls are silent no-ops. When `db_url` is `None`, the URL and pool settings are resolved from `get_context().database`; raises `RuntimeError` if no context is installed or `ctx.database` is `None`. Pool parameters are omitted automatically for SQLite (which does not use `QueuePool`).
326+
- `init_engine(db_url=None, pool_size=None, pool_max_overflow=None, pool_timeout=None)` — initialises the global `AsyncEngine` and `async_sessionmaker`; subsequent calls are silent no-ops. When `db_url` is `None`, the URL and pool settings are resolved from `get_context().database`; raises `RuntimeError` if no context is installed or `ctx.database` is `None`. Pool parameters are omitted automatically for SQLite (which does not use `QueuePool`).
327327
- `dispose_engine()` — async; disposes the engine; called during application shutdown.
328328
- `get_db_session()` — async generator; yields an `AsyncSession`; raises `RuntimeError` if engine not initialised. Use as a FastAPI `Depends` target.
329329
- `execute_with_session(async_func, *args, **kwargs)` — async; runs `async_func` with a session injected as the `session` keyword argument. For background jobs and CLI helpers.
330-
- `cli_run_with_db(async_func, *args, db_url=None, pool_size=None, max_overflow=None, pool_timeout=None, **kwargs)` — synchronous wrapper: initialises engine, runs the coroutine, then disposes. All DB-config params optional; fall back to context when `None`. For CLI commands.
331-
- `cli_run_with_engine(async_func, *args, db_url=None, pool_size=None, max_overflow=None, pool_timeout=None, **kwargs)` — like `cli_run_with_db` but does not inject a session; for jobs that manage sessions themselves.
330+
- `cli_run_with_db(async_func, *args, db_url=None, pool_size=None, pool_max_overflow=None, pool_timeout=None, **kwargs)` — synchronous wrapper: initialises engine, runs the coroutine, then disposes. All DB-config params optional; fall back to context when `None`. For CLI commands.
331+
- `cli_run_with_engine(async_func, *args, db_url=None, pool_size=None, pool_max_overflow=None, pool_timeout=None, **kwargs)` — like `cli_run_with_db` but does not inject a session; for jobs that manage sessions themselves.
332332
- `with_engine` — dual-mode decorator; supports `@with_engine` (no-parens), `@with_engine()` (empty parens), and `@with_engine(db_url=…, …)` (explicit params). All params optional; fall back to context when absent. For long-lived workers; does **not** dispose after running.
333333
- Fork safety: `multiprocessing.util.register_after_fork` resets the engine in child processes automatically.
334334
- **Location**: `aignostics_foundry_core/database.py`

src/aignostics_foundry_core/database.py

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class DatabaseSettings(OpaqueSettings):
3737
3838
* ``{PREFIX}URL`` — required; the full database connection URL
3939
* ``{PREFIX}POOL_SIZE`` — optional; connection pool size (default ``10``)
40-
* ``{PREFIX}MAX_OVERFLOW`` — optional; maximum pool overflow (default ``10``)
40+
* ``{PREFIX}POOL_MAX_OVERFLOW`` — optional; maximum pool overflow (default ``10``)
4141
* ``{PREFIX}POOL_TIMEOUT`` — optional; pool checkout timeout in seconds (default ``30.0``)
4242
* ``{PREFIX}NAME`` — optional; override the database name in the URL path component
4343
"""
@@ -46,7 +46,7 @@ class DatabaseSettings(OpaqueSettings):
4646

4747
url: SecretStr
4848
pool_size: int = 10
49-
max_overflow: int = 10
49+
pool_max_overflow: int = 10
5050
pool_timeout: float = 30.0
5151
db_name: str | None = None
5252

@@ -121,14 +121,14 @@ class _DatabaseModuleSentinel:
121121

122122

123123
_DEFAULT_POOL_SIZE = 10
124-
_DEFAULT_MAX_OVERFLOW = 10
124+
_DEFAULT_POOL_MAX_OVERFLOW = 10
125125
_DEFAULT_POOL_TIMEOUT = 30.0
126126

127127

128128
def _resolve_db_params(
129129
db_url: str | None,
130130
pool_size: int | None,
131-
max_overflow: int | None,
131+
pool_max_overflow: int | None,
132132
pool_timeout: float | None,
133133
) -> tuple[str, int, int, float]:
134134
"""Resolve database connection parameters, falling back to the active context.
@@ -138,7 +138,7 @@ def _resolve_db_params(
138138
params are replaced by their module-level defaults.
139139
140140
Returns:
141-
A tuple of ``(db_url, pool_size, max_overflow, pool_timeout)``.
141+
A tuple of ``(db_url, pool_size, pool_max_overflow, pool_timeout)``.
142142
143143
Raises:
144144
RuntimeError: If ``db_url`` is ``None`` and no context is installed, or
@@ -154,21 +154,21 @@ def _resolve_db_params(
154154
return (
155155
ctx.database.get_url(),
156156
pool_size if pool_size is not None else ctx.database.pool_size,
157-
max_overflow if max_overflow is not None else ctx.database.max_overflow,
157+
pool_max_overflow if pool_max_overflow is not None else ctx.database.pool_max_overflow,
158158
pool_timeout if pool_timeout is not None else ctx.database.pool_timeout,
159159
)
160160
return (
161161
db_url,
162162
pool_size if pool_size is not None else _DEFAULT_POOL_SIZE,
163-
max_overflow if max_overflow is not None else _DEFAULT_MAX_OVERFLOW,
163+
pool_max_overflow if pool_max_overflow is not None else _DEFAULT_POOL_MAX_OVERFLOW,
164164
pool_timeout if pool_timeout is not None else _DEFAULT_POOL_TIMEOUT,
165165
)
166166

167167

168168
def init_engine(
169169
db_url: str | None = None,
170170
pool_size: int | None = None,
171-
max_overflow: int | None = None,
171+
pool_max_overflow: int | None = None,
172172
pool_timeout: float | None = None,
173173
) -> None:
174174
"""Initialize the database engine singleton.
@@ -190,7 +190,7 @@ def init_engine(
190190
When ``None``, resolved from the active context's ``database`` settings.
191191
pool_size: Number of connections to keep in the pool. Ignored for dialects that
192192
do not support QueuePool (e.g. SQLite). Defaults to the context value or 10.
193-
max_overflow: Number of additional connections above pool_size. Ignored for
193+
pool_max_overflow: Number of additional connections above pool_size. Ignored for
194194
dialects that do not support QueuePool. Defaults to the context value or 10.
195195
pool_timeout: Seconds to wait for a connection from the pool. Ignored for
196196
dialects that do not support QueuePool. Defaults to the context value or 30.
@@ -205,12 +205,14 @@ def init_engine(
205205
logger.trace("Database engine already initialized, reusing existing engine and connection pool.")
206206
return # Already initialized
207207

208-
db_url, pool_size, max_overflow, pool_timeout = _resolve_db_params(db_url, pool_size, max_overflow, pool_timeout)
208+
db_url, pool_size, pool_max_overflow, pool_timeout = _resolve_db_params(
209+
db_url, pool_size, pool_max_overflow, pool_timeout
210+
)
209211

210212
logger.trace(
211-
"Initializing global database engine with pool_size={}, max_overflow={}, pool_timeout={}",
213+
"Initializing global database engine with pool_size={}, pool_max_overflow={}, pool_timeout={}",
212214
pool_size,
213-
max_overflow,
215+
pool_max_overflow,
214216
pool_timeout,
215217
)
216218

@@ -222,7 +224,7 @@ def init_engine(
222224
}
223225
if not db_url.startswith("sqlite"):
224226
engine_kwargs["pool_size"] = pool_size
225-
engine_kwargs["max_overflow"] = max_overflow
227+
engine_kwargs["max_overflow"] = pool_max_overflow
226228
engine_kwargs["pool_timeout"] = pool_timeout
227229

228230
_engine = create_async_engine(**engine_kwargs)
@@ -307,7 +309,7 @@ def cli_run_with_db(
307309
*args: Any, # noqa: ANN401
308310
db_url: str | None = None,
309311
pool_size: int | None = None,
310-
max_overflow: int | None = None,
312+
pool_max_overflow: int | None = None,
311313
pool_timeout: float | None = None,
312314
**kwargs: Any, # noqa: ANN401
313315
) -> Any: # noqa: ANN401
@@ -327,7 +329,7 @@ def cli_run_with_db(
327329
*args: Positional arguments forwarded to ``async_func``.
328330
db_url: Database connection URL. When ``None``, resolved from the active context.
329331
pool_size: Connection pool size (ignored for SQLite).
330-
max_overflow: Max overflow connections (ignored for SQLite).
332+
pool_max_overflow: Max overflow connections (ignored for SQLite).
331333
pool_timeout: Pool wait timeout in seconds (ignored for SQLite).
332334
**kwargs: Keyword arguments forwarded to ``async_func``.
333335
@@ -337,7 +339,7 @@ def cli_run_with_db(
337339
import asyncio # noqa: PLC0415
338340

339341
logger.trace("Initializing database engine for cli_run_with_db")
340-
init_engine(db_url=db_url, pool_size=pool_size, max_overflow=max_overflow, pool_timeout=pool_timeout)
342+
init_engine(db_url=db_url, pool_size=pool_size, pool_max_overflow=pool_max_overflow, pool_timeout=pool_timeout)
341343
logger.debug("Database engine initialized for cli_run_with_db")
342344

343345
try:
@@ -354,7 +356,7 @@ def cli_run_with_engine(
354356
*args: Any, # noqa: ANN401
355357
db_url: str | None = None,
356358
pool_size: int | None = None,
357-
max_overflow: int | None = None,
359+
pool_max_overflow: int | None = None,
358360
pool_timeout: float | None = None,
359361
**kwargs: Any, # noqa: ANN401
360362
) -> Any: # noqa: ANN401
@@ -374,7 +376,7 @@ def cli_run_with_engine(
374376
*args: Positional arguments forwarded to ``async_func``.
375377
db_url: Database connection URL. When ``None``, resolved from the active context.
376378
pool_size: Connection pool size (ignored for SQLite).
377-
max_overflow: Max overflow connections (ignored for SQLite).
379+
pool_max_overflow: Max overflow connections (ignored for SQLite).
378380
pool_timeout: Pool wait timeout in seconds (ignored for SQLite).
379381
**kwargs: Keyword arguments forwarded to ``async_func``.
380382
@@ -384,7 +386,7 @@ def cli_run_with_engine(
384386
import asyncio # noqa: PLC0415
385387

386388
logger.trace("Initializing database engine for cli_run_with_engine")
387-
init_engine(db_url=db_url, pool_size=pool_size, max_overflow=max_overflow, pool_timeout=pool_timeout)
389+
init_engine(db_url=db_url, pool_size=pool_size, pool_max_overflow=pool_max_overflow, pool_timeout=pool_timeout)
388390
logger.debug("Database engine initialized for cli_run_with_engine")
389391

390392
try:
@@ -400,7 +402,7 @@ def with_engine(
400402
*,
401403
db_url: str | None = None,
402404
pool_size: int | None = None,
403-
max_overflow: int | None = None,
405+
pool_max_overflow: int | None = None,
404406
pool_timeout: float | None = None,
405407
) -> Any: # noqa: ANN401
406408
"""Decorator (or decorator factory) to ensure database engine is initialized for async functions.
@@ -423,7 +425,7 @@ def with_engine(
423425
without parentheses). Do not pass explicitly.
424426
db_url: Database connection URL. When ``None``, resolved from the active context.
425427
pool_size: Connection pool size (ignored for SQLite).
426-
max_overflow: Max overflow connections (ignored for SQLite).
428+
pool_max_overflow: Max overflow connections (ignored for SQLite).
427429
pool_timeout: Pool wait timeout in seconds (ignored for SQLite).
428430
429431
Returns:
@@ -450,7 +452,9 @@ def decorator(f: Any) -> Any: # noqa: ANN401
450452
@functools.wraps(f)
451453
async def wrapper(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401
452454
logger.trace("Initializing database engine in with_engine wrapper for function {}", func_name)
453-
init_engine(db_url=db_url, pool_size=pool_size, max_overflow=max_overflow, pool_timeout=pool_timeout)
455+
init_engine(
456+
db_url=db_url, pool_size=pool_size, pool_max_overflow=pool_max_overflow, pool_timeout=pool_timeout
457+
)
454458
logger.debug("Database engine initialized in with_engine wrapper for function {}", func_name)
455459

456460
try:

tests/aignostics_foundry_core/database_settings_test.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
CUSTOM_PREFIX = "CUSTOM_DB_"
1515
CUSTOM_PREFIX_URL_ENV = "CUSTOM_DB_URL"
1616
DEFAULT_POOL_SIZE = 10
17-
DEFAULT_MAX_OVERFLOW = 10
17+
DEFAULT_POOL_MAX_OVERFLOW = 10
1818
DEFAULT_POOL_TIMEOUT = 30
1919
OVERRIDE_POOL_SIZE = 5
20-
OVERRIDE_MAX_OVERFLOW = 20
20+
OVERRIDE_POOL_MAX_OVERFLOW = 20
2121
OVERRIDE_POOL_TIMEOUT = 60
2222

2323

@@ -94,24 +94,24 @@ def test_explicit_env_prefix_overrides_context(monkeypatch: pytest.MonkeyPatch)
9494

9595
@pytest.mark.unit
9696
def test_pool_defaults_are_applied() -> None:
97-
"""pool_size, max_overflow, pool_timeout take their defaults when not set in env."""
97+
"""pool_size, pool_max_overflow, pool_timeout take their defaults when not set in env."""
9898
settings = DatabaseSettings(_env_prefix="TEST_DB_", url=SQLITE_URL)
9999
assert settings.pool_size == DEFAULT_POOL_SIZE
100-
assert settings.max_overflow == DEFAULT_MAX_OVERFLOW
100+
assert settings.pool_max_overflow == DEFAULT_POOL_MAX_OVERFLOW
101101
assert int(settings.pool_timeout) == DEFAULT_POOL_TIMEOUT
102102

103103

104104
@pytest.mark.unit
105105
def test_pool_overrides_from_env(monkeypatch: pytest.MonkeyPatch) -> None:
106-
"""Pool params read from {PREFIX}POOL_SIZE, {PREFIX}MAX_OVERFLOW, {PREFIX}POOL_TIMEOUT."""
106+
"""Pool params read from {PREFIX}POOL_SIZE, {PREFIX}POOL_MAX_OVERFLOW, {PREFIX}POOL_TIMEOUT."""
107107
monkeypatch.setenv("TEST_DB_URL", SQLITE_URL)
108108
monkeypatch.setenv("TEST_DB_POOL_SIZE", str(OVERRIDE_POOL_SIZE))
109-
monkeypatch.setenv("TEST_DB_MAX_OVERFLOW", str(OVERRIDE_MAX_OVERFLOW))
109+
monkeypatch.setenv("TEST_DB_POOL_MAX_OVERFLOW", str(OVERRIDE_POOL_MAX_OVERFLOW))
110110
monkeypatch.setenv("TEST_DB_POOL_TIMEOUT", str(OVERRIDE_POOL_TIMEOUT))
111111

112112
settings = DatabaseSettings(_env_prefix="TEST_DB_")
113113
assert settings.pool_size == OVERRIDE_POOL_SIZE
114-
assert settings.max_overflow == OVERRIDE_MAX_OVERFLOW
114+
assert settings.pool_max_overflow == OVERRIDE_POOL_MAX_OVERFLOW
115115
assert int(settings.pool_timeout) == OVERRIDE_POOL_TIMEOUT
116116

117117

0 commit comments

Comments
 (0)