Bound frozen RPC router batch allocations - #4038
Conversation
Limit JSON-RPC batches to 1,000 calls by default and reject oversized batches during streaming decode with one bounded error response. Add a configurable 30-second HTTP write timeout to release slow-client handlers.
PR SummaryLow Risk Overview Batch handling no longer unmarshals the whole array up front. README and config/router tests cover defaults, flag validation, and the oversized-batch rejection path. Reviewed by Cursor Bugbot for commit ea3a257. Bugbot is set up for automated code reviews on this repo. Configure here. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4038 +/- ##
==========================================
- Coverage 61.24% 60.25% -1.00%
==========================================
Files 2153 2054 -99
Lines 188393 176741 -11652
==========================================
- Hits 115387 106495 -8892
+ Misses 62274 60479 -1795
+ Partials 10732 9767 -965
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).
|
There was a problem hiding this comment.
The batch-limit change is a clean, behavior-preserving rewrite of serveBatch's decode path with good unit coverage, and config validation/plumbing is consistent. The only concern is the new mandatory 30s http.Server.WriteTimeout, which caps total handler time (not just write stalls) for a router whose workload is historical/archive queries.
Findings: 0 blocking | 1 non-blocking | 1 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- None at the file/PR level.
- 1 suggestion(s)/nit(s) flagged inline on specific lines.
| Addr: cfg.listenAddress, | ||
| Handler: router, | ||
| ReadHeaderTimeout: 10 * time.Second, | ||
| WriteTimeout: cfg.writeTimeout, |
There was a problem hiding this comment.
[suggestion] http.Server.WriteTimeout is not a write-stall timeout: the deadline is armed when the request headers are read, so it bounds handler execution plus response write. For this router that means any single request whose upstream takes longer than 30s — debug_traceBlockByNumber, debug_getRawReceipts, a wide eth_getLogs, or a large mixed batch fanned out across frozen nodes — has its connection torn down mid-response, even though the client is fast and the upstream would have answered. That is exactly the archive-style workload this router fronts, and there is currently no upstream http.Client timeout to keep such calls under the budget.
Two things worth reconsidering:
parseConfigrejects--write-timeout 0, so an operator cannot restore the previous (unbounded) behavior. Allowing0to mean "no write timeout" — matchinghttp.Server's own convention — would keep the safe default while leaving an escape hatch. If a mandatory timeout is intended, that is worth stating explicitly, since it is a behavior change for existing deployments.- The README's "HTTP response writes time out after 30 seconds" reads as a write-only deadline and will mislead operators tuning it; wording it as a total per-request deadline would set the right expectation.
For what it's worth, this does not affect the documented WebSocket passthrough: net/http's (*conn).hijackLocked calls rwc.SetDeadline(time.Time{}) before returning the connection, so ReverseProxy's upgrade path is left with no deadline. Only non-hijacked HTTP requests are affected.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ea3a257. Configure here.
| Addr: cfg.listenAddress, | ||
| Handler: router, | ||
| ReadHeaderTimeout: 10 * time.Second, | ||
| WriteTimeout: cfg.writeTimeout, |
There was a problem hiding this comment.
Write timeout drops WebSocket connections
High Severity
http.Server.WriteTimeout is an absolute write deadline on the TCP connection, not an idle-write timeout. Non-POST traffic, including WebSocket upgrades, is reverse-proxied via liveProxy, and httputil.ReverseProxy hijacks without clearing that deadline. Default 30s therefore tears down live-node subscriptions. --write-timeout also rejects 0, so the deadline cannot be disabled.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit ea3a257. Configure here.


Limit JSON-RPC batches to 1,000 calls by default and reject oversized
batches during streaming decode with one bounded error response. Add a
configurable 30-second HTTP write timeout to release slow-client handlers.
Fixes PLT-1085