feat: enforce single-worker at startup

Fail with RuntimeError when WEB_CONCURRENCY or BANGUI_WORKERS > 1.

In-memory session cache, rate-limit windows, and runtime state are
process-local. Multi-worker silently causes stale limits, ghost sessions,
inconsistent status.

Skipped when TESTING=1.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-03 20:33:23 +02:00
parent e1a6491ac2
commit ae9313568e
5 changed files with 919 additions and 10 deletions

View File

@@ -628,6 +628,35 @@ backend:
**Single worker enforced**: The session cache is process-local. Multiple workers would cause random logouts. This is intentional — scale horizontally via orchestration, not vertically via workers.
**Single-Worker Requirement**
=============================
BanGUI enforces single-worker mode at startup. It fails immediately with a clear error if more than one worker is configured.
**Why this matters:**
- **In-memory session cache** — each worker has its own cache copy. A session cached in worker A is invisible to worker B. A user validated by A may be rejected by B.
- **Rate-limit windows** — per-IP counters are process-local. With 4 workers, a client hitting different workers gets 4× the intended rate limit.
- **Runtime state** — fail2ban status, pending recovery records, and jail service capability flags are all per-process. Dashboard queries to different workers return inconsistent data.
- **Background scheduler** — the database lock ensures only one instance runs scheduled jobs, but each worker's scheduler still fires. With multi-worker, the same job runs N times.
**Detection:**
The check runs at application startup in ``create_app()``:
- ``WEB_CONCURRENCY`` env var — set by gunicorn, and by uvicorn in recent versions when ``--workers N`` is passed
- ``BANGUI_WORKERS`` env var — explicit override (discouraged)
If either is set to a value > 1, ``RuntimeError`` is raised with instructions and a reference to this document.
**Test mode:**
The check is automatically skipped when ``TESTING=1`` is set. This allows the test suite to run with an arbitrary number of workers.
**What to do instead of multi-worker:**
Scale horizontally via container orchestration — run multiple containers behind a load balancer. Each container runs a single worker. The database lock ensures only one container runs background jobs at a time.
### Frontend Performance
**Static asset caching** (already configured):