Document limits, load control and performance

- New docs/09-limits-and-performance.md: principles, all protection
  layers, what players see, configuration reference, measurements,
  tuning guide, monitoring and how to test
- Server chapter summarises the limits and links to chapter 9; circuit
  breaker description and AI_MAX_INFLIGHT default corrected
- Architecture: new files in the layout, design decisions for load
  handling; docs index lists chapter 9
- Protocol, AI and operations chapters link to chapter 9; test counts
- Root and component READMEs describe the limits and settings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 13:04:21 +02:00
co-authored by Claude Opus 5.5
parent 763c5678f1
commit 9ffee8b952
11 changed files with 462 additions and 111 deletions
+12 -6
View File
@@ -5,7 +5,7 @@
| Component | Folder | Language / stack | Runs as | Responsibility |
|-----------|--------|------------------|---------|----------------|
| Website | `wizard_website/` | React 19, TypeScript, Vite, CSS modules | static files served by nginx (container port 8082) | UI: start page, lobby, game table |
| Game server | `wizard_server/app/` | Python 3.13, FastAPI, uvicorn | one process (container port 8083) | sessions, lobbies, running games, asking the AI for moves |
| Game server | `wizard_server/app/` | Python 3.13, FastAPI, uvicorn | one process (container port 8083) | sessions, lobbies, running games, asking the AI for moves, limits and load control |
| Game engine | `wizard_server/game_logic/` (package `wizard_game`) | pure Python, no dependencies | library, installed into server and AI images | the rules of Wizard |
| AI service | `wizard_ai/` | Python, `websockets` | one process, no listening port | chooses moves for AI seats; training tools |
@@ -89,8 +89,8 @@ Important properties of this flow:
```
.
├── compose.yaml # all three containers + networks
├── .env.example # template for .env (AI_SHARED_SECRET, …)
├── compose.yaml # all three containers, networks, CPU/memory caps
├── .env.example # template for .env (secret, resource limits, capacity)
├── README.md # short project readme
├── docs/ # this documentation
├── instructions.txt # original task description
@@ -107,8 +107,11 @@ Important properties of this flow:
│ │ ├── service.py # WizardService: dispatches messages, sessions, cleanup
│ │ ├── sessions.py # Session, Connection (ordered send queue), SessionRegistry
│ │ ├── lobby.py # Lobby, Seat, LobbyManager, AI turn loop
│ │ └── ai_bridge.py # request/response with the AI service + fallback
│ ├── tests/test_server.py # websocket-level tests of the server
│ │ ├── ai_bridge.py # request/response with the AI service, fallback, circuit breaker
│ │ ├── limits.py # TokenBucket, rate and connection limiters
│ │ └── load.py # LoadMonitor: detects overload (lag, CPU, cgroup throttling)
│ ├── tests/ # conftest.py/helpers.py (fixtures), test_server.py,
│ │ # test_protection.py (limits/overload), test_limits.py (units)
│ └── game_logic/ # installable package "wizard-game"
│ ├── pyproject.toml
│ ├── wizard_game/
@@ -130,7 +133,7 @@ Important properties of this flow:
│ │ ├── agents/ # Agent interface, random, heuristic, model agent
│ │ ├── model/ # encoding.py (features/actions), policy.py (model interface)
│ │ ├── training/ # env.py, self_play.py, evaluate.py
│ │ └── service/client.py # production websocket client
│ │ └── service/client.py # production websocket client (workers, queue, degradation)
│ └── tests/test_ai.py
│
└── wizard_website/
@@ -158,3 +161,6 @@ Important properties of this flow:
| Session token in `sessionStorage` | Survives page reloads (reconnect to the same seat) but is per tab, so you can test several players in several tabs. |
| Per-connection send queue | Websocket messages are always sent in order and never concurrently (see `sessions.Connection`). |
| Relative asset paths + relative API URL in the frontend | The app works under any path prefix (`/wizard/website/` in production, `/` in development) without rebuilding. |
| Refuse new games under load, never interrupt running ones | Players in a game are never punished for load; creating/starting games is what adds load, so that is what gets refused ("server busy"). Refusing sheds load by itself. See [chapter 9](09-limits-and-performance.md). |
| Hard Docker caps plus software limits | The caps guarantee the host is protected even if a software limit fails; the software limits make overload visible and graceful instead of just slow. |
| Health check stays "ok" under load | Docker must never restart an overloaded server — that would end every running game. |