Querier: remove redundant chunk data copy in ingester streaming select - #7746
Draft
sandy2008 wants to merge 4 commits into
Draft
Querier: remove redundant chunk data copy in ingester streaming select#7746sandy2008 wants to merge 4 commits into
sandy2008 wants to merge 4 commits into
Conversation
detachChunksFromBuffer copied every chunk's data on the querier's ingester-read path to detach it from the gRPC receive buffer, but the chunk data never aliases that buffer in the first place: Recv() allocates a fresh QueryStreamResponse per message, TimeSeriesChunk.Unmarshal appends a zero Chunk value, and Chunk.Unmarshal's append therefore starts from a nil slice and allocates a private backing array. The copy cost an extra allocation and memcpy per chunk and kept both the original and the copy reachable for the duration of streamingSelect, raising peak heap. Labels are different: LabelAdapter.Unmarshal uses yoloString, so label names and values do alias the receive buffer, and the FromLabelAdaptersToLabelsWithCopy call is kept. The call-site comment now documents both facts, and the assumption the removal relies on: response messages are never reused across Recv calls. If QueryStreamResponse ever becomes pooled, a detach copy must be reinstated. Two tests pin the non-aliasing invariant, through a direct gogo round trip (with a wire-buffer mutation cross-check) and through the registered cortexCodec with a payload large enough to exceed the gRPC buffer-pooling threshold, so a future pooled or zero-copy decoder fails loudly. The original cortexproject#7670 benchmark, which constructed chunk data as sub-slices of one shared buffer (a shape a real gRPC unmarshal never produces), is rebuilt with independently allocated chunk data: dropping the copy saves ~107 KB and 200 allocations per 100-series response batch. Fixes cortexproject#7732 Signed-off-by: Sandy Chen <Yuxuan.Chen@morganstanley.com>
Signed-off-by: Sandy Chen <Yuxuan.Chen@morganstanley.com>
…romQLFuncsWithPrometheus / compactor unit test) Signed-off-by: Sandy Chen <Yuxuan.Chen@morganstanley.com>
…ce, FailedWithHaltError) Signed-off-by: Sandy Chen <Yuxuan.Chen@morganstanley.com>
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.
What this PR does:
Removes the
detachChunksFromBuffercopy that #7670 (unreleased) added to the querier's ingester streaming-select path. The chunk copy is redundant: chunk data never aliases the gRPC receive buffer, becauseRecv()allocates a freshQueryStreamResponseper message,TimeSeriesChunk.Unmarshalappends a zeroChunkvalue, andChunk.Unmarshal'sm.Data = append(m.Data[:0], ...)therefore starts from a nil slice and allocates a private backing array. The copy cost one allocation + memcpy per chunk and kept the original and the copy simultaneously reachable across thestreamingSelectloop, raising peak heap — the opposite of #7670's goal.The label copy (
FromLabelAdaptersToLabelsWithCopy) is unchanged and still required:LabelAdapter.UnmarshalusesyoloString, so label names/values genuinely alias the receive buffer.The issue asked maintainers to confirm no code path constructs
client.Chunkvalues that alias a shared or pooled buffer before removing the copy. That verification is part of this PR:pkg/cortexpb/codec.go,encoding.RegisterCodecV2) built on grpc-go's pooledmem.BufferSlicemachinery — but pooling only affects where the wire bytes live. Decoding still dispatches (via the protobuf-v2 legacy shim) to the gogo-generatedUnmarshal, which copies[]bytefields unconditionally.QueryStreamResponsedoes not implementReleasableMessage, so the codec never returns its buffer to a pool while decoded messages are live.sync.Pools,vtprotobuf,unsafe, everyclient.Chunk{...}construction site) found no producer whoseDataaliases shared memory on the read path.Recvcalls) and that a detach copy must be reinstated ifQueryStreamResponseever becomes pooled.The #7670 benchmark constructed its input by hand-slicing one shared buffer into all chunks — a shape a real gRPC unmarshal never produces, which is why the copy had appeared profitable. It is rebuilt with independently allocated chunk data; on that input, dropping the copy saves ~107 KB and 200 allocations per 100-series response batch (1002 vs 1202 allocs/op; 168 KB vs 275 KB/op — allocation numbers are the meaningful signal, time-per-op is noise-dominated at this size).
Also noticed while verifying, deliberately not touched here:
detachSeriesFromBufferinpkg/querier/blocks_store_queryable.go(added by #7519, store-gateway path, vendored Thanosstorepbtypes) has a similar shape and may warrant the same analysis separately.Which issue(s) this PR fixes:
Fixes #7732
Checklist
CHANGELOG.mdupdated - the order of entries should be[CHANGE],[FEATURE],[ENHANCEMENT],[BUGFIX](the unreleased Detach ingester series from gRPC buffers to reduce heap #7670 entry is amended to describe the final behavior)docs/configuration/v1-guarantees.mdupdated if this PR introduces experimental flags (n/a)Per the Generative AI Contribution Policy: this change was developed with substantial AI assistance (multiple independent AI implementations reconciled and then adversarially cross-reviewed, under my direction). I have reviewed and validated all of it and take responsibility for its correctness.