- Shipped
- September 23, 2026 at 10:18 AM UTC
- Author
- Kamo
- Commit
- eb9cc3b
Both RestTemplate beans were built from a bare JdkClientHttpRequestFactory with no timeout at all. One upstream that accepts the connection and then never answers -- a wedged pod, a deadlock -- parked a gateway thread on that socket forever; enough of those exhaust the finite Tomcat thread pool behind the platform's single /api/** chokepoint and take down every tenant, not just the caller of the slow route. Connect timeout is 5s: the upstream Service always exists in-cluster, so a connect not established by then means the pod behind it is gone. Read timeout is a global 120s rather than a per-prefix value -- this class has no per-route RestTemplate today, the threat (an upstream that never answers) is the same regardless of prefix, and 120s comfortably covers the slowest legitimate calls this gateway forwards (large uploads to /api/docs and /api/media, AI chat, billing exports). Traefik's own inbound respondingTimeouts.readTimeout is 3600s, so nothing ahead of this call was ever going to cut a request shorter than 120s anyway. RestTemplateTimeoutTest drives a real socket that accepts the TCP connection and then never writes a response, using a short timeout in place of the real 120s so the test itself cannot hang the suite, and asserts the client gives up on its own. Reverting to the bare factory makes the underlying call hang past JUnit's own preemptive 3s guard, which is exactly the failure mode this fixes.
