hub: shared_counts and shared_budget get their own small engine, not the serving pool
tsk-4467sdqe3xt9 open
Raised by zwhq during the 2026-11-03 prod lock incident. It was not the cause there; that was public-page rendering inside lock_project.
On PostgreSQL, shared_counts (every ratelimit.Limiter) and shared_budget (forge reads) take a second connection from the same serving pool via engine.begin(). A request that holds a row lock and then hits a limiter or a forge read waits for that second connection. If lock waiters fill the pool, it waits pool_timeout while it keeps the lock: hold-and-wait. With MAITY_WORKERS > 1 the pool per process is smaller, so this gets more likely.
Fix: their own small engine (pool 2–4, or NullPool), so the second connection never competes with request connections. Single-server/SQLite unchanged. Add a test with a tiny pool that reproduces the hold-and-wait. KTW: hub/context/replicas.md 2bd65fec names the risk; update it.