Pack responses into the buffer pool the cache already has - #661
Merged
Merged
Conversation
Four places pack a message into wire format themselves rather than letting the dns library do it, and each allocated a fresh buffer for every query: the DoH, DoQ and ODoH listeners packing an answer, and the net.Resolver adapter used for bootstrap lookups. Plain DNS and DoT get pooling from the dns library and were already unaffected. The buffer pool the cache backends use moves to packbuf.go, since it is no longer only theirs, and gains packToPool to pack into one. Over a million DoH queries answering five A records, that is 1282 MB allocated down to 1008 MB and 409 GC cycles down to 328. DoQ also built its length-prefixed frame with a second allocation. The prefix now goes into another pooled buffer, which takes the response path there from two allocations per query to none. A buffer is only recycled when the write that consumed it succeeded. A failed write can leave the transport holding the bytes: an HTTP/2 handler whose connection goes away mid-write returns while the frame is still queued, and a reset QUIC stream leaves the data with the sender. Reusing the buffer then could put the next answer packed into it on the wire in place of the one still to be sent.
folbricht
force-pushed
the
pool-response-packing
branch
from
September 27, 2026 16:54
c12abe7 to
eefe7c0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four places pack a message into wire format themselves rather than letting the dns library do it, and each allocated a fresh buffer for every query: the DoH, DoQ and ODoH listeners packing an answer, and the net.Resolver adapter used for bootstrap lookups. Plain DNS and DoT get pooling from the dns library and were already unaffected.
The buffer pool the cache backends use moves to
packbuf.go, since it is no longer only theirs, and gainspackToPoolto pack into one. Over a million DoH queries answering five A records, that is 1282 MB allocated down to 1008 MB and 409 GC cycles down to 328. Wall time per query is unchanged within the noise of the machine this was measured on; the saving is in allocation volume and collector work, which is what matters on a router.DoQ also built its length-prefixed frame with a second allocation. The prefix now goes into another pooled buffer, which takes the response path there from two allocations and 576 bytes per query to none.
A buffer is only recycled when the write that consumed it succeeded. A failed write can leave the transport holding the bytes: an HTTP/2 handler whose connection goes away mid-write returns while the frame is still queued, and a reset QUIC stream leaves the data with the sender. Reusing the buffer then could put the next answer packed into it on the wire in place of the one still to be sent.
ODoH and the net.Resolver adapter are done with the bytes before they return, so they hand the buffer back unconditionally. ODoH encrypts the packed message into a buffer of its own, and the adapter copies into the caller's.