Skip to content

[BUG] RoundRobinLoadBalancer shares weight state across selectors whose first upstream is the same #8441

Open

Description

@Aias00

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

RoundRobinLoadBalancer keeps its smooth weighted round-robin state in methodWeightMap, keyed only by the URL of the first upstream in the list:

https://github.com/apache/shenyu/blob/9dd9a12fb/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/spi/RoundRobinLoadBalancer.java#L47-L48

String key = upstreamList.get(0).getUrl();
ConcurrentMap<UpstreamIdentity, WeightedRoundRobin> map = MapUtils.computeIfAbsent(methodWeightMap, key, k -> new ConcurrentHashMap<>(16));

The key carries neither the selector nor the whole upstream set. Consequences:

  1. Two selectors whose upstream lists start with the same upstream share one WeightedRoundRobin per UpstreamIdentity. Selections in one selector change the current counters, and therefore the next pick, of the other.
  2. If the shared upstream has a different weight in the two selectors, every alternation hits weight != weightedRoundRobin.getWeight() (L61-L64) and setWeight resets current to 0 (L117-L120), so that node's counter is reset continuously.
  3. The shared map holds the union of both lists, so upstreamList.size() != map.size() (L74) is true on every call. Each call that wins updateLock copies the whole map and replaces it in methodWeightMap.
  4. Reordering a selector's upstream list moves its state to a different key. The previous state is never removed from methodWeightMap, because recycling only runs inside the map of the current key.

Expected Behavior

Round-robin state is isolated per selector, or per complete upstream set. The configured weights determine the traffic ratio of each selector independently of other selectors.

Steps To Reproduce

  1. Use the same RoundRobinLoadBalancer instance (it is an SPI singleton).
  2. Define list S1 = [A (weight 1), B (weight 9)] and list S2 = [A (weight 9), C (weight 1)], where A has the same URL in both.
  3. Call select(S1, data) and select(S2, data) alternately, for example 10,000 times each.
  4. Count the picks per upstream for each list. The ratios differ from 1:9 and 9:1, whereas running each list alone in a fresh balancer gives the configured ratio.

Environment

ShenYu version(s): master @ 9dd9a12fb

Debug logs

No response

Anything else?

Suggested fix: key the state by the full ordered identity list, the way HashLoadBalancer keys ringCache with List.copyOf(ringKey), or pass a selector id through LoadBalanceData and key by it. This issue is different from #8327, which fixed weight-accumulation overflow.

Found by static code review; I have not yet run the reproduction above.

Activity

  1. konlue commented on Nov 12, 2026

    @konlue
    Contributor

    /assign

  2. added a commit that references this issue on Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

type: bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions