[router] Add weight-aware Helix group selection to balance multi-key RCU - #3003
Open
pthirun wants to merge 2 commits into
Open
[router] Add weight-aware Helix group selection to balance multi-key RCU#3003pthirun wants to merge 2 commits into
pthirun wants to merge 2 commits into
Conversation
The Helix-assisted-routing least-loaded group selector counts every request as +1 when picking a group, regardless of key count. A 1-key request and a 500-key multi-get therefore weigh identically, so a group can absorb disproportionate read-capacity (RCU) and get throttled with HTTP 429 while other groups retain capacity and the store-wide read quota still has headroom. Make the group's load counter weightable by the request's key count (an estimate of its RCU cost) so variable-size multi-key requests are balanced by keys/RCU rather than by raw request count. - HelixGroupSelectionStrategy: add selectGroup(requestId, groupCount, weight); the existing 2-arg overload becomes a default delegating with weight=1. - HelixGroupLeastLoadedStrategy: counters[] becomes a weighted sum (+= weight on select, -= stored weight on finish/timeout). The per-request weight is stored in a RequestGroupAssignment holder so the decrement is symmetric; weight is clamped to >= 1. - HelixGroupRoundRobinStrategy / HelixGroupSelector: implement the 3-arg overload (round-robin ignores weight; selector passes it through). - VeniceDelegateMode: pass venicePath.getPartitionKeys().size() as the weight when the new config is enabled, else 1. - New config router.helix.assisted.routing.group.selection.weight.aware.enabled (default false) gates the behavior, so it is a no-op unless explicitly enabled. finishRequest keeps its signature (the weight is stored in the strategy), so no change is needed in VeniceResponseAggregator. Added tests for heavy-request steering, weighted decrement symmetry, the non-positive-weight clamp (boundary), and default-overload back-compat. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.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.
Problem Statement
Under Helix-assisted routing, the router's least-loaded group selector (
HelixGroupLeastLoadedStrategy) increments a group's in-flight counter by exactly1per request inselectGroup, regardless of how many keys the request carries. A 1-key single-get and a 500-key multi-get therefore contribute identical load to the selected group.Server-side read-quota enforcement, in contrast, charges per key (RCU). The result is a two-sided accounting mismatch: a group that happens to receive a run of large multi-key requests accumulates far more read-capacity load than its request count suggests, so its per-node token buckets drain and requests get throttled with HTTP 429 — even when the store-wide read quota still has substantial headroom and other groups are idle. This is more likely on stores with highly variable multi-get batch sizes.
Solution
Make the group's load counter weightable by the request's key count (an estimate of its RCU cost), so variable-size multi-key requests are balanced across Helix groups by keys/RCU rather than by raw request count.
HelixGroupSelectionStrategy: addselectGroup(requestId, groupCount, weight). The existing 2-argselectGroup(requestId, groupCount)becomes adefaultmethod that delegates withweight = 1, preserving all existing callers and behavior.HelixGroupLeastLoadedStrategy:counters[]becomes a weighted sum (+= weighton select,-= stored weighton finish/timeout). The per-request weight is stored alongside the group id and leak-guard timeout future in a smallRequestGroupAssignmentholder (replacing the previousPair), so the decrement on completion/timeout is exactly symmetric and the counter cannot leak. Weight is clamped to>= 1so a burst of zero/negative-weight requests cannot all pile onto one group.HelixGroupRoundRobinStrategy/HelixGroupSelector: implement the new 3-arg overload (round-robin ignores weight; the selector passes it through to the underlying strategy).VeniceDelegateMode: passvenicePath.getPartitionKeys().size()as the weight when the new config is enabled, else1.finishRequestkeeps its signature (the weight is looked up from the strategy's own state), soVeniceResponseAggregatorneeds no change.The existing
group_pending_requestmetric now reflects weighted load when the feature is enabled, sincecounters[]is a weighted sum — giving per-group keys/RCU-in-flight visibility for free.Trade-off: when enabled,
counters[]tracks weighted (key-count) load rather than request count. Selection cost is unchanged (still a single scan under the existingsynchronizedblock).Code changes
router.helix.assisted.routing.group.selection.weight.aware.enabled— defaultfalse(no behavior change unless explicitly enabled).Concurrency-Specific Checks
synchronized (this)block; the new per-request weight is stored and read under the same lock.requestTimeoutFutureMapaccess remains guarded by the same lock as before.How was this PR tested?
TestHelixGroupLeastLoadedStrategy:0still counts as1),weight = 1(back-compat).TestVeniceDelegateModesuites pass unchanged).isHelixGroupSelectionWeightAwareEnabled()returnsfalse, so weight is always1(identical to today).Compiled and tested with JDK 17 (
:services:venice-routerand:internal:venice-common).Does this PR introduce any user-facing or breaking changes?
false; when disabled, group selection is byte-for-byte identical to the current implementation.🤖 Generated with GitHub Copilot CLI