expose explicit PauseRead/ResumeRead on Connection for application-driven read backpressure
- Dominant language
- Go
- Stars
- 4.6k
- Forks
- 505
- PR merge metrics
- No merged PRs in 30d
Description
**Is your feature request related to a problem? Please describe.**
Proxies, gateways and high fan-in servers can receive faster than they consume, but netpoll keeps reading into the inbound `LinkBuffer` as long as `EPOLLIN` fires. When consumers fall behind there is no way to stop reading, so memory grows unbounded and the process eventually OOMs. The "overloaded" signal lives in the application (e.g. downstream/outbound queue depth), so the application needs a way to pause/resume reading and let TCP flow control slow the peer.
**Describe the solution you'd like**
Three minimal primitives on the `Connection` interface:
```go
PauseRead() error // stop monitoring EPOLLIN for this connection (idempotent)
ResumeRead() error // resume monitoring EPOLLIN (idempotent)
IsReadPaused() bool // whether read is currently paused
```
Mechanism, not policy — the application decides when to pause/resume based on its own state.
Key implementation points:
Read and write interest become independent dimensions; the poll mask is recomputed from (readPaused, writeWatching) on each transition (adds PollR2N/PollN2R/PollN2W/PollW2N) so toggling read never disturbs a pending write.
Paused with no pending write drops to "no interest" instead of write-only, avoiding a level-triggered EPOLLOUT busy-spin.
Transitions are serialized with a per-connection mutex (the two dimensions are mutated by app vs poller, and epoll_ctl(MOD) sets an absolute mask).
Guarded by IsActive(); epoll + kqueue paths updated. EPOLLIN is level-triggered, so resume is lossless.
Pure addition: no behavior change unless the new APIs are called.
Describe alternatives you've considered
Fixed inbound-buffer threshold (feat/read-throttle / WithReadBufferThreshold). Auto-pauses on inputBuffer.Len() >= threshold. It throttles on the wrong signal — a proxy that drains netpoll's buffer into its own queue never hits the threshold yet still OOMs; it hard-codes one global policy (no hysteresis / per-route control); its implicit resume can stay paused forever; and its PollRW2W pause keeps EPOLLOUT, busy-spinning on receive-mostly conns. The explicit API is the underlying mechanism, on top of which such a threshold could still be built in user space.
App-level sleep/return in OnRequest. Doesn't stop the kernel delivering data, gives no TCP backpressure, and holds the processing lock while sleeping.
Additional context
Prior art: PR #298 / the feat/read-throttle branch, generalized here from a fixed-threshold policy into an explicit mechanism. I have a working implementation (epoll + kqueue) with a TestConnectionPauseResumeRead test and am happy to open a PR #423 .
Contributor guide
Assessment
This issue has not been assessed yet.