• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20365

27 Jul 2026 06:04AM UTC coverage: 89.199% (+0.07%) from 89.125%
#20365

push

github

web-flow
core: Implement LB Delay Observability (Proposal A121) (#12807)

This PR implements **Attempt-Level RPC Delay Observability** across the core channel transport, built-in load balancers, xDS policies, and the OpenTelemetry telemetry plugin, aligned with [gRPC Proposal A121](https://github.com/grpc/proposal/pull/556).

38276 of 42911 relevant lines covered (89.2%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.36
/../xds/src/main/java/io/grpc/xds/WeightedRandomPicker.java
1
/*
2
 * Copyright 2019 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.xds;
18

19
import static com.google.common.base.Preconditions.checkArgument;
20
import static com.google.common.base.Preconditions.checkNotNull;
21

22
import com.google.common.annotations.VisibleForTesting;
23
import com.google.common.base.MoreObjects;
24
import com.google.common.primitives.UnsignedInteger;
25
import io.grpc.LoadBalancer.PickResult;
26
import io.grpc.LoadBalancer.PickSubchannelArgs;
27
import io.grpc.LoadBalancer.SubchannelPicker;
28
import java.util.Collections;
29
import java.util.List;
30
import java.util.Objects;
31

32
final class WeightedRandomPicker extends SubchannelPicker {
33

34
  @VisibleForTesting
35
  final List<WeightedChildPicker> weightedChildPickers;
36

37
  private final ThreadSafeRandom random;
38
  private final long totalWeight;
39

40
  static final class WeightedChildPicker {
41
    private final long weight;
42
    private final SubchannelPicker childPicker;
43

44
    WeightedChildPicker(long weight, SubchannelPicker childPicker) {
1✔
45
      checkArgument(weight >= 0, "weight is negative");
1✔
46
      checkArgument(weight <= UnsignedInteger.MAX_VALUE.longValue(), "weight is too large");
1✔
47
      checkNotNull(childPicker, "childPicker is null");
1✔
48

49
      this.weight = weight;
1✔
50
      this.childPicker = childPicker;
1✔
51
    }
1✔
52

53
    long getWeight() {
54
      return weight;
1✔
55
    }
56

57
    SubchannelPicker getPicker() {
58
      return childPicker;
1✔
59
    }
60

61
    @Override
62
    public boolean equals(Object o) {
63
      if (this == o) {
1✔
64
        return true;
×
65
      }
66
      if (o == null || getClass() != o.getClass()) {
1✔
67
        return false;
×
68
      }
69
      WeightedChildPicker that = (WeightedChildPicker) o;
1✔
70
      return weight == that.weight && Objects.equals(childPicker, that.childPicker);
1✔
71
    }
72

73
    @Override
74
    public int hashCode() {
75
      return Objects.hash(weight, childPicker);
×
76
    }
77

78
    @Override
79
    public String toString() {
80
      return MoreObjects.toStringHelper(this)
×
81
          .add("weight", weight)
×
82
          .add("childPicker", childPicker)
×
83
          .toString();
×
84
    }
85
  }
86

87
  WeightedRandomPicker(List<WeightedChildPicker> weightedChildPickers) {
88
    this(weightedChildPickers, ThreadSafeRandom.ThreadSafeRandomImpl.instance);
1✔
89
  }
1✔
90

91
  @VisibleForTesting
92
  WeightedRandomPicker(List<WeightedChildPicker> weightedChildPickers, ThreadSafeRandom random) {
1✔
93
    checkNotNull(weightedChildPickers, "weightedChildPickers in null");
1✔
94
    checkArgument(!weightedChildPickers.isEmpty(), "weightedChildPickers is empty");
1✔
95

96
    this.weightedChildPickers = Collections.unmodifiableList(weightedChildPickers);
1✔
97

98
    long totalWeight = 0;
1✔
99
    for (WeightedChildPicker weightedChildPicker : weightedChildPickers) {
1✔
100
      long weight = weightedChildPicker.getWeight();
1✔
101
      checkArgument(weight >= 0, "weight is negative");
1✔
102
      checkNotNull(weightedChildPicker.getPicker(), "childPicker is null");
1✔
103
      totalWeight += weight;
1✔
104
    }
1✔
105
    this.totalWeight = totalWeight;
1✔
106
    checkArgument(totalWeight <= UnsignedInteger.MAX_VALUE.longValue(),
1✔
107
        "total weight greater than unsigned int can hold");
108

109
    this.random = random;
1✔
110
  }
1✔
111

112
  @Override
113
  public final PickResult pickSubchannel(PickSubchannelArgs args) {
114
    SubchannelPicker childPicker = null;
1✔
115

116
    if (totalWeight == 0) {
1✔
117
      childPicker =
1✔
118
          weightedChildPickers.get(random.nextInt(weightedChildPickers.size())).getPicker();
1✔
119
    } else {
120
      long rand = random.nextLong(totalWeight);
1✔
121

122
      // Find the first idx such that rand < accumulatedWeights[idx]
123
      // Not using Arrays.binarySearch for better readability.
124
      long accumulatedWeight = 0;
1✔
125
      for (WeightedChildPicker weightedChildPicker : weightedChildPickers) {
1✔
126
        accumulatedWeight += weightedChildPicker.getWeight();
1✔
127
        if (rand < accumulatedWeight) {
1✔
128
          childPicker = weightedChildPicker.getPicker();
1✔
129
          break;
1✔
130
        }
131
      }
1✔
132
      checkNotNull(childPicker, "childPicker not found");
1✔
133
    }
134

135
    PickResult res = childPicker.pickSubchannel(args);
1✔
136
    if (!res.hasResult() && res.getDelayType() != null) {
1✔
137
      return PickResult.withNoResult(res.getDelayType(),
1✔
138
          "weighted_target: " + (res.getDelayReason() != null ? res.getDelayReason() : ""));
1✔
139
    }
140
    return res;
1✔
141
  }
142

143
  @Override
144
  public String toString() {
145
    return MoreObjects.toStringHelper(this)
×
146
        .add("weightedChildPickers", weightedChildPickers)
×
147
        .add("totalWeight", totalWeight)
×
148
        .toString();
×
149
  }
150
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc