From c2d8e6e21c2a602cf0cd1ce2a81d3dcba4f7e05b Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Wed, 21 Oct 2026 22:24:15 +0300 Subject: [PATCH] fix: leave Granian backpressure at its default --- AGENTS.md | 2 +- app/__main__.py | 1 - tests/test_main.py | 4 +--- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 91f3bc2..e4ac333 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,7 +60,7 @@ Endpoints inject a use case with `FromDI(UseCase)` from `modern_di_fastapi`, cal ### Pool and server sizing -`DB_POOL_SIZE` (positive) and `DB_MAX_OVERFLOW` (non-negative) size the pool, and `app/__main__.py` sets Granian's `backpressure` to their sum, so each worker accepts only as many requests at once as it has connections. Requests beyond that wait in Granian's socket backlog instead of queueing on the pool. `APP_WORKERS` sets the Granian worker count, and each worker has its own pool. `DB_POOL_TIMEOUT` (default 30 s) bounds the wait for a connection, and a timeout returns 503 through `exceptions.database_unavailable_handler`. advanced-alchemy wraps the timeout in a `RepositoryError`, so the handler is also registered for `RepositoryError` and re-raises anything not caused by a pool timeout. `DB_POOL_RECYCLE` defaults to SQLAlchemy's `-1` (never). Set it below the idle timeout of any proxy or load balancer in front of Postgres, so a connection it dropped is not handed out and retried after a 1-2 s backoff. +`DB_POOL_SIZE` (positive) and `DB_MAX_OVERFLOW` (non-negative) size the pool, and a request that finds it empty waits for a connection. Granian's `backpressure` is left at its default: it limits open connections per worker, idle keep-alive ones included, so setting it to the pool size makes new connections wait while the app is idle. `APP_WORKERS` sets the Granian worker count, and each worker has its own pool. `DB_POOL_TIMEOUT` (default 30 s) bounds the wait for a connection, and a timeout returns 503 through `exceptions.database_unavailable_handler`. advanced-alchemy wraps the timeout in a `RepositoryError`, so the handler is also registered for `RepositoryError` and re-raises anything not caused by a pool timeout. `DB_POOL_RECYCLE` defaults to SQLAlchemy's `-1` (never). Set it below the idle timeout of any proxy or load balancer in front of Postgres, so a connection it dropped is not handed out and retried after a 1-2 s backoff. ### Use cases and lost connections diff --git a/app/__main__.py b/app/__main__.py index 386d0e9..758a902 100644 --- a/app/__main__.py +++ b/app/__main__.py @@ -11,7 +11,6 @@ address=settings.app_host, port=settings.app_port, workers=settings.app_workers, - backpressure=settings.db_pool_size + settings.db_max_overflow, interface=Interfaces.ASGI, log_level=settings.log_level, loop=Loops.uvloop, diff --git a/tests/test_main.py b/tests/test_main.py index 6a47d5a..2ad56b4 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -17,13 +17,11 @@ def test_main(monkeypatch: pytest.MonkeyPatch) -> None: granian = mock.Mock() monkeypatch.setattr("granian.Granian", granian) monkeypatch.setattr(settings, "app_workers", 4) - monkeypatch.setattr(settings, "db_pool_size", 5) - monkeypatch.setattr(settings, "db_max_overflow", 2) runpy.run_module("app.__main__", run_name="__main__") assert granian.call_args.kwargs["workers"] == 4 # noqa: PLR2004 - assert granian.call_args.kwargs["backpressure"] == 7 # noqa: PLR2004 + assert "backpressure" not in granian.call_args.kwargs async def test_session() -> None: