Fix connection lifecycle, pool bounds, and log rotation (review #007)

Critical:
- C1: Set server-side idle/keep-alive timeouts on both hyper builders
  (http2 keep_alive_interval=15s + keep_alive_timeout, http1
  header_read_timeout). Both builders now set TokioTimer (required to
  avoid runtime panic). Prevents FD exhaustion from abandoned TLS
  connections — the root cause of the 2026-07-24 outage.
- C2: Add Semaphore(max_connections) gating the accept loop. Provides
  backpressure via OS TCP backlog when all permits are taken.

Warnings:
- W1: Add SIGUSR1 log-reopen handler. New ReopenableFileWriter
  (Arc<ArcSwap<File>> via custom MakeWriter) atomically swaps the log
  file. Enables postrotate logrotate without copytruncate, which caused
  the 1.15GB sparse file that wedged fail2ban.
- W2: Set pool_max_idle_per_host(10) on both upstream clients, bounding
  idle upstream connections per host.
- W3: Add connection_idle_timeout_secs to StaticConfig (default 60).
- W4: Add max_connections to StaticConfig (default 1024).

Both new fields are validated (> 0) and included in static config drift
detection on reload. Docs (config.md, README, ADR-009) updated.
This commit is contained in:
glm-5.2 committed 2026-07-28 10:16:26 +00:00
1 parent e803817350
commit 0885486028
14 files changed
+407 -41

No files matched your search

+2
View File
@@ -115,6 +115,8 @@ Configuration uses TOML and is split into **static** (requires restart) and
| `health_check_port` | `9900` | Local health check port (`0` to disable) |
| `admin_key_path` | `/etc/reverse-proxy/admin-key` | Path to admin Bearer token file (empty string to disable) |
| `shutdown_timeout_secs` | `30` | Graceful shutdown timeout |
| `connection_idle_timeout_secs` | `60` | Server-side idle timeout for client TLS connections (prevents FD exhaustion from abandoned connections) |
| `max_connections` | `1024` | Max concurrent client TLS connections (backpressure via semaphore) |
| `logging.level` | `"info"` | Log level |
| `logging.format` | `"text"` | Log format (`"text"` or `"json"`) |
| `logging.log_file_path` | (not set) | Path to log file for fail2ban |