Repository navigation
perf: coalesce expanded reads in HTTP backend queues - #567
Merged
Merged
Conversation
deniszh
previously approved these changes
Oct 15, 2026
timtofan
force-pushed
the
atimofieiev/backend-render-batching
branch
from
October 15, 2026 15:32
50590a3 to
f79f2ae
Compare
timtofan
marked this pull request as ready for review
October 15, 2026 15:32
deniszh
approved these changes
Oct 15, 2026
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.
Why
Large resolved globs currently create one HTTP render request per metric and replica. This repeats HTTP, compression and protobuf framing work thousands of times while fetching the same underlying series. Reduce that fixed request overhead without changing metric routing, replica-majority selection or the number of metrics admitted concurrently.
Architecture
Coalescing happens inside each backend's existing render queue, after TLD/path-cache routing has independently selected the backend for each metric:
flowchart LR Expansion[Glob expansion] --> Routing[Existing per-target routing] Routing --> Queue[Per-backend render queue] Queue --> Wire[Bounded multi-target HTTP request] Wire --> Replies[Separate replies for original targets] Replies --> Merge[Existing per-metric replica merge]One glob expansion carries a unique context marker. A backend coalesces only immediately queued, unique literal targets carrying that exact context and time window. Batches default to at most 32 targets and stay below an 8KiB encoded URL budget. There is no batching timer or sharing across independent client requests.
The backend's shared semaphore reserves one slot per original metric in a batch. Find/info requests still share the same capacity. Admission honours cancellation, and a carried slow request cannot starve the fast queue.
Replies are delivered to the original per-target channels. Missing names become the HTTP client's existing not-found error; a failed or slow replica reports its error once to each original replica fan-in. Unexpected renamed/duplicate replies use concurrent individual reads within the already reserved slots. Routing-cache miss fallback and the existing majority merger remain unchanged.
HTTP backends explicitly declare the required named-response/missing-series contract. gRPC retains individual calls because empty stream EOF and per-request NotFound have different semantics. Supporting gRPC coalescing requires defining and testing its per-target error contract first. This is a transport capability boundary, with no new fork or wire format.
Before / after
Usage
The existing glob-expansion call site marks eligible requests, and both the default API configuration and example enable:
Set
renderBatchSize: 1and restart carbonapi to restore individual HTTP requests. ExistingresolveGlobs, queue classification and routing configuration remain in force.Validation
govulncheckpass on the rebased branch (Go 1.27.2; golangci-lint 2.14.0).masteratd651603c, including perf: reduce carbonapi CPU, allocations and retained cache memory #566 and the current dependency/toolchain updates. Binary values and JSON match exactly. In one replay, physical backend requests fell from 42,399 to 13,370. Coalescing is opportunistic, so the request count varies with scheduling; this is not a live production CPU result.golang.org/x/sync v0.23.0, matching the current base branch's Go 1.26 minimum. No additional Go support change is introduced by this PR.Rollout and rollback
A live canary is still required before rollout: compare CPU per returned point, p99/max read latency, peak RSS, allocations and backend errors under matched production traffic. Observe physical HTTP counts separately from logical metric subrequests. Restore
renderBatchSize: 1and restart, or revert the release, if the canary does not improve the production result.