diff --git a/Docs/Architekture.md b/Docs/Architekture.md index 0c030f8..47ff61b 100644 --- a/Docs/Architekture.md +++ b/Docs/Architekture.md @@ -661,8 +661,8 @@ BanGUI maintains its **own SQLite database** (separate from the fail2ban databas - Session expiry is configurable (set during setup, stored in `settings`). - The frontend `AuthProvider` checks session validity on mount and redirects to `/login` if invalid. - The backend `dependencies.py` provides an `authenticated` dependency that validates the session cookie on every protected endpoint. -- **Session validation cache** — validated session tokens are cached in memory for 10 seconds (`_session_cache` dict in `dependencies.py`) to avoid a SQLite round-trip on every request from the same browser. The cache is invalidated immediately on logout. - This cache is process-local and not safe for multi-worker or distributed deployments. A clustered deployment should replace `_session_cache` with a shared cache or remove it entirely. +- **Session validation cache** — validated session tokens are cached in memory for 10 seconds (`_session_cache` dict in `dependencies.py`) to avoid a SQLite round-trip on every request from the same browser. The cache is invalidated immediately on logout. This cache is process-local and not safe for multi-worker or distributed deployments. A clustered deployment should replace `_session_cache` with a shared cache or remove it entirely. +- **Runtime state** — `RuntimeState` is process-local and only safe when BanGUI runs as a single asyncio worker. Mutating runtime state must not span `await` points because the current design relies on cooperative scheduling. Multi-worker or multi-process deployments must replace this runtime state with a shared coordination backend such as Redis, shared memory, or a database-backed store. - **Setup-completion flag** — once `is_setup_complete()` returns `True`, the result is stored in `app.state._setup_complete_cached`. The `SetupRedirectMiddleware` skips the DB query on all subsequent requests, removing 1 SQL query per request for the common post-setup case. The completion flag is only written after the runtime database is successfully initialized and all initial setup settings are persisted, preventing a failed setup from permanently bypassing the setup wizard. --- diff --git a/Docs/Tasks.md b/Docs/Tasks.md index 36808a8..a2fcebd 100644 --- a/Docs/Tasks.md +++ b/Docs/Tasks.md @@ -98,6 +98,8 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue. **Why this is needed:** The safety of `RuntimeState` depends on an implicit assumption (single-worker, no `await` between read and write) that is not written down anywhere. If the deployment model changes or a developer modifies state-mutating functions, silent data races could appear with no warning. Explicit documentation prevents this. +**Status:** Completed. + --- ### 6. `geo_service` Calls `db.commit()` Directly Instead of Deferring to Repository diff --git a/backend/app/utils/runtime_state.py b/backend/app/utils/runtime_state.py index 4006e55..2f10865 100644 --- a/backend/app/utils/runtime_state.py +++ b/backend/app/utils/runtime_state.py @@ -5,6 +5,12 @@ exposed through a controlled state manager object. This keeps the FastAPI framework state bag limited to shared infrastructure handles and immutable configuration while still allowing existing code to access runtime values via attribute proxying. + +RuntimeState is designed for a single-process, single-worker asyncio +deployment. Mutations must complete without awaiting across read-modify-write +sequences. If BanGUI is ever deployed with multiple workers or across processes, +this module must be replaced with a shared backend such as Redis or shared +memory. """ from __future__ import annotations @@ -13,9 +19,8 @@ import datetime from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any -from starlette.datastructures import State - import structlog +from starlette.datastructures import State from app.models.config import PendingRecovery from app.models.server import ServerStatus