1 /*
2  * Copyright 2023 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 io.grpc.Deadline;
21 import io.grpc.ExperimentalApi;
22 import io.grpc.Internal;
23 import io.grpc.LoadBalancer;
24 import io.grpc.LoadBalancer.Helper;
25 import io.grpc.LoadBalancerProvider;
26 import io.grpc.NameResolver.ConfigOrError;
27 import io.grpc.Status;
28 import io.grpc.internal.JsonUtil;
29 import io.grpc.xds.WeightedRoundRobinLoadBalancer.WeightedRoundRobinLoadBalancerConfig;
30 import java.util.Map;
31 
32 /**
33  * Provides a {@link WeightedRoundRobinLoadBalancer}.
34  * */
35 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9885")
36 @Internal
37 public final class WeightedRoundRobinLoadBalancerProvider extends LoadBalancerProvider {
38 
39   @VisibleForTesting
40   static final long MIN_WEIGHT_UPDATE_PERIOD_NANOS = 100_000_000L; // 100ms
41 
42   static final String SCHEME = "weighted_round_robin";
43 
44   @Override
newLoadBalancer(Helper helper)45   public LoadBalancer newLoadBalancer(Helper helper) {
46     return new WeightedRoundRobinLoadBalancer(helper, Deadline.getSystemTicker());
47   }
48 
49   @Override
isAvailable()50   public boolean isAvailable() {
51     return true;
52   }
53 
54   @Override
getPriority()55   public int getPriority() {
56     return 5;
57   }
58 
59   @Override
getPolicyName()60   public String getPolicyName() {
61     return SCHEME;
62   }
63 
64   @Override
parseLoadBalancingPolicyConfig(Map<String, ?> rawConfig)65   public ConfigOrError parseLoadBalancingPolicyConfig(Map<String, ?> rawConfig) {
66     try {
67       return parseLoadBalancingPolicyConfigInternal(rawConfig);
68     } catch (RuntimeException e) {
69       return ConfigOrError.fromError(
70           Status.UNAVAILABLE.withCause(e).withDescription(
71               "Failed parsing configuration for " + getPolicyName()));
72     }
73   }
74 
parseLoadBalancingPolicyConfigInternal(Map<String, ?> rawConfig)75   private ConfigOrError parseLoadBalancingPolicyConfigInternal(Map<String, ?> rawConfig) {
76     Long blackoutPeriodNanos = JsonUtil.getStringAsDuration(rawConfig, "blackoutPeriod");
77     Long weightExpirationPeriodNanos =
78             JsonUtil.getStringAsDuration(rawConfig, "weightExpirationPeriod");
79     Long oobReportingPeriodNanos = JsonUtil.getStringAsDuration(rawConfig, "oobReportingPeriod");
80     Boolean enableOobLoadReport = JsonUtil.getBoolean(rawConfig, "enableOobLoadReport");
81     Long weightUpdatePeriodNanos = JsonUtil.getStringAsDuration(rawConfig, "weightUpdatePeriod");
82     Float errorUtilizationPenalty = JsonUtil.getNumberAsFloat(rawConfig, "errorUtilizationPenalty");
83 
84     WeightedRoundRobinLoadBalancerConfig.Builder configBuilder =
85             WeightedRoundRobinLoadBalancerConfig.newBuilder();
86     if (blackoutPeriodNanos != null) {
87       configBuilder.setBlackoutPeriodNanos(blackoutPeriodNanos);
88     }
89     if (weightExpirationPeriodNanos != null) {
90       configBuilder.setWeightExpirationPeriodNanos(weightExpirationPeriodNanos);
91     }
92     if (enableOobLoadReport != null) {
93       configBuilder.setEnableOobLoadReport(enableOobLoadReport);
94     }
95     if (oobReportingPeriodNanos != null) {
96       configBuilder.setOobReportingPeriodNanos(oobReportingPeriodNanos);
97     }
98     if (weightUpdatePeriodNanos != null) {
99       configBuilder.setWeightUpdatePeriodNanos(weightUpdatePeriodNanos);
100       if (weightUpdatePeriodNanos < MIN_WEIGHT_UPDATE_PERIOD_NANOS) {
101         configBuilder.setWeightUpdatePeriodNanos(MIN_WEIGHT_UPDATE_PERIOD_NANOS);
102       }
103     }
104     if (errorUtilizationPenalty != null) {
105       configBuilder.setErrorUtilizationPenalty(errorUtilizationPenalty);
106     }
107     return ConfigOrError.fromConfig(configBuilder.build());
108   }
109 }
110