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

grpc / grpc-java / #20034

29 Oct 2025 05:05PM UTC coverage: 88.514% (-0.02%) from 88.533%
#20034

push

github

ejona86
Include causal status details in higher-level statuses

When an operation fails and we want to produce a new status at a higher
level, we commonly are turning the first status into an exception to
attach to the new exception. We should instead prefer to keep as much
information in the status description itself, as cause is not as
reliable to be logged/propagated.

I do expect long-term we'll want to expose an API in grpc-api for this,
but for the moment let's keep it internal. In particular, we'd have to
figure out its name. I could also believe we might want different
formatting, which becomes a clearer discussion when we can see the
usages.

I'm pretty certain there are some other places that could benefit from
this utility, as I remember really wishing I had these functions a month
or two ago. But these are the places I could immediately find.

OutlierDetectionLoadBalancerConfig had its status code changed from
INTERNAL to UNAVAILABLE because the value comes externally, and so isn't
a gRPC bug or such. I didn't change the xds policies in the same way
because it's murkier as the configuration for those is largely generated
within xds itself.

34957 of 39493 relevant lines covered (88.51%)

0.89 hits per line

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

65.0
/../xds/src/main/java/io/grpc/xds/WeightedTargetLoadBalancerProvider.java
1
/*
2
 * Copyright 2020 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 com.google.common.annotations.VisibleForTesting;
20
import com.google.common.base.MoreObjects;
21
import io.grpc.Internal;
22
import io.grpc.LoadBalancer;
23
import io.grpc.LoadBalancer.Helper;
24
import io.grpc.LoadBalancerProvider;
25
import io.grpc.LoadBalancerRegistry;
26
import io.grpc.NameResolver.ConfigOrError;
27
import io.grpc.Status;
28
import io.grpc.internal.GrpcUtil;
29
import io.grpc.internal.JsonUtil;
30
import io.grpc.util.GracefulSwitchLoadBalancer;
31
import java.util.LinkedHashMap;
32
import java.util.Map;
33
import java.util.Objects;
34
import javax.annotation.Nullable;
35

36
/**
37
 * The provider for the weighted_target balancing policy.  This class should not be
38
 * directly referenced in code.  The policy should be accessed through {@link
39
 * LoadBalancerRegistry#getProvider} with the name "weighted_target_experimental".
40
 */
41
@Internal
42
public final class WeightedTargetLoadBalancerProvider extends LoadBalancerProvider {
43

44
  @Nullable
45
  private final LoadBalancerRegistry lbRegistry;
46

47
  // We can not call this(LoadBalancerRegistry.getDefaultRegistry()), because it will get stuck
48
  // recursively loading LoadBalancerRegistry and WeightedTargetLoadBalancerProvider.
49
  public WeightedTargetLoadBalancerProvider() {
50
    this(null);
1✔
51
  }
1✔
52

53
  @VisibleForTesting
54
  WeightedTargetLoadBalancerProvider(@Nullable LoadBalancerRegistry lbRegistry) {
1✔
55
    this.lbRegistry = lbRegistry;
1✔
56
  }
1✔
57

58
  @Override
59
  public boolean isAvailable() {
60
    return true;
1✔
61
  }
62

63
  @Override
64
  public int getPriority() {
65
    return 5;
1✔
66
  }
67

68
  @Override
69
  public String getPolicyName() {
70
    return XdsLbPolicies.WEIGHTED_TARGET_POLICY_NAME;
1✔
71
  }
72

73
  @Override
74
  public LoadBalancer newLoadBalancer(Helper helper) {
75
    return new WeightedTargetLoadBalancer(helper);
1✔
76
  }
77

78
  @Override
79
  public ConfigOrError parseLoadBalancingPolicyConfig(Map<String, ?> rawConfig) {
80
    try {
81
      Map<String, ?> targets = JsonUtil.getObject(rawConfig, "targets");
1✔
82
      if (targets == null || targets.isEmpty()) {
1✔
83
        return ConfigOrError.fromError(Status.INTERNAL.withDescription(
×
84
            "No targets provided for weighted_target LB policy:\n " + rawConfig));
85
      }
86
      Map<String, WeightedPolicySelection> parsedChildConfigs = new LinkedHashMap<>();
1✔
87
      for (String name : targets.keySet()) {
1✔
88
        Map<String, ?> rawWeightedTarget = JsonUtil.getObject(targets, name);
1✔
89
        if (rawWeightedTarget == null || rawWeightedTarget.isEmpty()) {
1✔
90
          return ConfigOrError.fromError(Status.INTERNAL.withDescription(
×
91
              "No config for target " + name + " in weighted_target LB policy:\n " + rawConfig));
92
        }
93
        Integer weight = JsonUtil.getNumberAsInteger(rawWeightedTarget, "weight");
1✔
94
        if (weight == null || weight < 1) {
1✔
95
          return ConfigOrError.fromError(Status.INTERNAL.withDescription(
×
96
              "Wrong weight for target " + name + " in weighted_target LB policy:\n " + rawConfig));
97
        }
98
        LoadBalancerRegistry lbRegistry =
99
            this.lbRegistry == null ? LoadBalancerRegistry.getDefaultRegistry() : this.lbRegistry;
1✔
100
        ConfigOrError childConfig = GracefulSwitchLoadBalancer.parseLoadBalancingPolicyConfig(
1✔
101
            JsonUtil.getListOfObjects(rawWeightedTarget, "childPolicy"), lbRegistry);
1✔
102
        if (childConfig.getError() != null) {
1✔
103
          return ConfigOrError.fromError(GrpcUtil.statusWithDetails(
×
104
              Status.Code.INTERNAL,
105
              "Could not parse weighted_target's child policy: " + name,
106
              childConfig.getError()));
×
107
        }
108
        parsedChildConfigs.put(name, new WeightedPolicySelection(weight, childConfig.getConfig()));
1✔
109
      }
1✔
110
      return ConfigOrError.fromConfig(new WeightedTargetConfig(parsedChildConfigs));
1✔
111
    } catch (RuntimeException e) {
×
112
      return ConfigOrError.fromError(
×
113
          Status.INTERNAL.withCause(e).withDescription(
×
114
              "Failed to parse weighted_target LB config: " + rawConfig));
115
    }
116
  }
117

118
  static final class WeightedPolicySelection {
119

120
    final int weight;
121
    final Object childConfig;
122

123
    WeightedPolicySelection(int weight, Object childConfig) {
1✔
124
      this.weight = weight;
1✔
125
      this.childConfig = childConfig;
1✔
126
    }
1✔
127

128
    @Override
129
    public boolean equals(Object o) {
130
      if (this == o) {
1✔
131
        return true;
×
132
      }
133
      if (o == null || getClass() != o.getClass()) {
1✔
134
        return false;
×
135
      }
136
      WeightedPolicySelection that = (WeightedPolicySelection) o;
1✔
137
      return weight == that.weight && Objects.equals(childConfig, that.childConfig);
1✔
138
    }
139

140
    @Override
141
    public int hashCode() {
142
      return Objects.hash(weight, childConfig);
×
143
    }
144

145
    @Override
146
    public String toString() {
147
      return MoreObjects.toStringHelper(this)
×
148
          .add("weight", weight)
×
149
          .add("childConfig", childConfig)
×
150
          .toString();
×
151
    }
152
  }
153

154
  /** The lb config for WeightedTargetLoadBalancer. */
155
  static final class WeightedTargetConfig {
156

157
    final Map<String, WeightedPolicySelection> targets;
158

159
    WeightedTargetConfig(Map<String, WeightedPolicySelection> targets) {
1✔
160
      this.targets = targets;
1✔
161
    }
1✔
162

163
    @Override
164
    public boolean equals(Object o) {
165
      if (this == o) {
1✔
166
        return true;
×
167
      }
168
      if (o == null || getClass() != o.getClass()) {
1✔
169
        return false;
×
170
      }
171
      WeightedTargetConfig that = (WeightedTargetConfig) o;
1✔
172
      return Objects.equals(targets, that.targets);
1✔
173
    }
174

175
    @Override
176
    public int hashCode() {
177
      return Objects.hashCode(targets);
×
178
    }
179

180
    @Override
181
    public String toString() {
182
      return MoreObjects.toStringHelper(this)
×
183
          .add("targets", targets)
×
184
          .toString();
×
185
    }
186
  }
187
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc