transport: optimize heap allocations in ready reader and update syscall connection handling#9035
Conversation
a160ebc to
ce2cb88
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9035 +/- ##
==========================================
- Coverage 83.06% 82.78% -0.28%
==========================================
Files 413 413
Lines 33135 33404 +269
==========================================
+ Hits 27523 27655 +132
- Misses 4199 4258 +59
- Partials 1413 1491 +78
🚀 New features to boost your workflow:
|
|
@arjan-bal : Could the failing test in CI be related to changes here? I haven't seen that test flake anytime recently. |
The test was reported in #8993 and a fix was made. I suspect the fix was incomplete so I've re-opened the issue and kept is assigned to the test author. I don't think the failure is related to the changes in this PR, as the ReadyReader code is currently only used with ALTS. The changes to integrate it into the HTTP/2 framer will happen in multiple follow-up PRs. |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request optimizes the nonBlockingReader by moving per-call state into the struct to avoid heap allocations and refactors the New function to use a specialized newNonBlockingReader constructor. The review feedback highlights that these optimizations introduce thread-safety concerns that need to be documented or addressed, and suggests resetting the internal state after each read to ensure that references can be properly garbage collected.
| type nonBlockingReader struct { | ||
| raw syscall.RawConn | ||
| // The following fields are stored as field to avoid heap allocations. | ||
| state readState | ||
| doRead func(fd uintptr) bool | ||
| } |
There was a problem hiding this comment.
The nonBlockingReader struct is no longer thread-safe because it uses shared fields (state) to store per-call request and response parameters to avoid heap allocations. While this is an effective optimization for gRPC's typical single-threaded reader loop, it is a regression in safety compared to the previous implementation. If ReadOnReady is called concurrently from multiple goroutines, it will result in data races and corrupted state. Please document this limitation or ensure that concurrent access is not possible.
There was a problem hiding this comment.
By default, most readers and writers are not thread-safe. They maintain internal state without mutex protection, requiring callers to handle synchronization themselves. Because lack of thread safety is the standard expectation, only implementations that are thread-safe need to document their behavior. For example, none of the exported types in the bufio package mention thread safety: https://pkg.go.dev/bufio#Reader
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Test reported in #8993 is fixed and not flaking any more. Test |
| if !ok { | ||
| return nil | ||
| } | ||
| if raw, err := sysConn.SyscallConn(); err == nil { |
There was a problem hiding this comment.
Nit: Handle the error case to reduce the indent in the non-error case.
This PR eliminates per-call heap allocations in
nonBlockingReader.ReadOnReady().Previously, calling
RawConn.Readwith an inline closure caused captured variables (and the closure itself) to escape to the heap. To resolve this, we moved the closure's required state and return values into fields on thenonBlockingReaderstruct. The state is set before execution, and the results are read afterward. Because the closure now only relies on the receiver's fields, we instantiate it exactly once duringnonBlockingReaderconstruction and reuse it, completely avoiding allocations on the hot path.Additionally, this change updates the types for which non-blocking reads are performed to the following:
net.Dial. This avoid reading encrypted data from credentials.syscallConns.ReadyReaderinterface themselves to support encrypted connections.These changes are a prerequisite for #9032.
Benchmarks
#9032 uses
nonBlockingReaderinstead of standardnet.Conn.Readand confirms zero increase in heap allocations for streaming RPCs.RELEASE NOTES: N/A