rate_limit.rs registers four limiters (strict/upload/image/comment) plus
an "unknown" fallback bucket for when the real client IP can't be
determined, but only the strict/upload/image tiers were listed in
.env.example and AGENTS.md. Add the missing comment and unknown tier
tunables so the documented surface matches the code.
DB migration failure at startup panicked via .expect() (main.rs:223),
dumping a raw backtrace for what is usually just "PostgreSQL isn't running
yet". Worse, the 1.6s runtime retry budget was reused at startup, so it
almost always failed against a cold/slow DB (docker-compose without
healthchecks, local Postgres not started, etc.).
Changes:
- pool: extract build_pg_config() (returns Result, no panic); add
validate_database_url() so URL-format / DB_POOL_SIZE errors surface as
friendly exit(1) instead of hitting the LazyLock's unreachable .expect()
- pool: add get_conn_for_startup() — a startup-only retry loop with a
configurable total-duration window (MIGRATE_STARTUP_TIMEOUT_SECS,
default 30s, 500ms interval), separate from the runtime get_conn()
anti-avalanche fast retry (untouched)
- migrate: split run() into run_on_conn(&mut conn) so callers control the
connection-acquisition strategy; main.rs pairs this with the startup
retry. Advisory-lock release comment updated: exit(1) terminates the
process just like panic, so the session-level lock is still freed
- main: startup fatal errors (bad URL, DB unreachable, migration failed)
now each emit tracing::error! + eprintln! ERROR + targeted HINT, then
exit(1) — no panic, no backtrace nudge
- .env.example / AGENTS.md: document MIGRATE_STARTUP_TIMEOUT_SECS
No runtime behavior change: get_conn() and its ~40 call sites are
unchanged. Advisory-lock safety preserved (documented in migrate.rs).
- COMPRESSION_ALGORITHMS defaults to 'all' when unset
- Support 'none'/'off' to explicitly disable compression
- Add parse_compression_algorithms helper and unit tests
- Update .env.example comment to reflect new default
- Cache ammonia::Builder with LazyLock (was rebuilt per request)
- Enable tracing release_max_level_info to strip tracing overhead at compile time
- Remove TraceLayer and tower-http trace feature from production
- Increase DB pool size 10→20 (configurable via DB_POOL_SIZE)
- Increase SSR cache TTL 300s→3600s (configurable via SSR_CACHE_SECS)
Benchmark: 7,444 → 9,840 req/s, P99 latency 27.6ms → 11.1ms
- Replace image crate's WebP with zenwebp for better quality/speed
- Add webp.rs module with configurable quality and method
- Update .env.example with WEBP_QUALITY and WEBP_METHOD
- Add WebP decode support in image serving pipeline
- Add detailed timing logs for WebP conversion
- Separate /uploads and /api/upload routes from Dioxus app to avoid
IncrementalRenderer intercepting non-page requests
- Remove broken SmartIpKeyExtractor-based general_limit() that failed
under Dioxus dev server proxy (Unable To Extract Key)
- Move rate limiting into handlers using governor::RateLimiter directly
- Add IMAGE_LIMITER for /uploads/* serving
- Make all rate limits configurable via environment variables
- Add rate limit config to .env.example with sensible defaults