Commit 177bb36
authored
## Summary
Adds `HTTP_POST_TIMEOUT` to the `methods_timeout` dict in
`backend/main.py`, enabling per-method timeout control for POST
requests.
Fixes #5941 — `sync-local-files` 504 timeouts on large payloads. POST
was the only HTTP method missing from the timeout config (omitted since
the original `ac328e5b` commit in Jan 2025).
## Changes
- `backend/main.py`: Add `"POST": os.environ.get('HTTP_POST_TIMEOUT')`
to `methods_timeout`
- `backend/tests/unit/test_timeout_middleware.py`: 3 new tests covering
POST timeout override, None fallback with `{"POST": None}`, and AST
verification of both key and env var name
## Deployment
After merge, set on **backend-sync** Cloud Run:
```
HTTP_POST_TIMEOUT=600
```
This matches `backend` (main) which already has
`HTTP_DEFAULT_TIMEOUT=600` — POST there already gets 600s via fallback.
The fix is specifically for `backend-sync` (and any other service) where
the default is 120s.
**Current env var state** (from mon's audit):
| Service | HTTP_DEFAULT_TIMEOUT | HTTP_POST_TIMEOUT (after this PR) |
|---------|---------------------|----------------------------------|
| backend (main) | 600s | set to 600s |
| backend-sync | 120s (hardcoded) | **set to 600s** ← fixes #5941 |
| All others | 120s (hardcoded) | unset (120s fallback) |
## WebSocket impact: None
HTTP_POST_TIMEOUT does NOT affect WebSocket connections (`/v4/listen`):
1. WebSocket upgrade requests are GET, not POST
2. Starlette `BaseHTTPMiddleware` only handles HTTP requests — WebSocket
connections go through a different ASGI path
3. `NO_SOCKET_TIMEOUT` in helm values is for a separate layer
## Risks
- **Scope**: `HTTP_POST_TIMEOUT` applies to ALL POST routes, not just
`/v1/sync-local-files`. Consistent with GET/PUT/PATCH/DELETE.
- **No behavior change without env var**: `None` is filtered by
`_parse_methods_timeout` → POST falls back to `HTTP_DEFAULT_TIMEOUT`.
## Tests
16/16 pass (`pytest backend/tests/unit/test_timeout_middleware.py`):
- 13 existing tests unchanged
- `test_post_timeout_override`: POST with explicit timeout → 504 on slow
handler
- `test_post_timeout_none_falls_back_to_default`: `{"POST": None}` →
skipped → default timeout
- `test_main_methods_timeout_includes_post`: AST-parses main.py, asserts
POST key + HTTP_POST_TIMEOUT env var
## Live Test Evidence
### L1 (standalone backend)
```
Parsed methods_timeout: {'POST': 2.0}
Default timeout: 120.0
POST resolved: 2.0
Without env var, parsed: {}
POST falls back to default: 120.0
Fast POST: 200 (expect 200) ✓
Slow POST: 504 (expect 504) ✓
```
### L2 (backend + client integrated)
```
GET /health: 200 {'status': 'ok'} ✓
POST /upload-fast: 200 {'received': 1500} ✓
POST /upload-slow: 504 ✓
```
### Changed-path coverage
| Path ID | Changed path | Happy-path | Non-happy-path | L1 | L2 |
|---|---|---|---|---|---|
| P1 | `main.py:121` POST in methods_timeout | POST completes within
timeout → 200 | POST exceeds timeout → 504 | PASS | PASS |
| P2 | `test_timeout_middleware.py` tests | 16/16 pass | N/A (test code)
| PASS | N/A |
_by AI for @beastoin_
2 files changed
Lines changed: 76 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
118 | 118 | | |
119 | 119 | | |
120 | 120 | | |
| 121 | + | |
121 | 122 | | |
122 | 123 | | |
123 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
182 | 182 | | |
183 | 183 | | |
184 | 184 | | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
0 commit comments