mirror of https://github.com/grpc/grpc-java.git
core: promote ServiceConfigErrorHandling (#6633)
This commit is contained in:
parent
5b7f5b8c3b
commit
751faa6faa
|
|
@ -99,13 +99,6 @@ public abstract class AbstractManagedChannelImplBuilder
|
|||
private static final long DEFAULT_RETRY_BUFFER_SIZE_IN_BYTES = 1L << 24; // 16M
|
||||
private static final long DEFAULT_PER_RPC_BUFFER_LIMIT_IN_BYTES = 1L << 20; // 1M
|
||||
|
||||
@VisibleForTesting
|
||||
static final String ENABLE_SERVICE_CONFIG_ERROR_HANDLING_PROPERTY =
|
||||
"io.grpc.internal.ManagedChannelImpl.enableServiceConfigErrorHandling";
|
||||
private static final boolean DEFAULT_ENABLE_SERVICE_CONFIG_ERROR_HANDLING =
|
||||
Boolean.parseBoolean(
|
||||
System.getProperty(ENABLE_SERVICE_CONFIG_ERROR_HANDLING_PROPERTY, "false"));
|
||||
|
||||
ObjectPool<? extends Executor> executorPool = DEFAULT_EXECUTOR_POOL;
|
||||
|
||||
ObjectPool<? extends Executor> offloadExecutorPool = DEFAULT_EXECUTOR_POOL;
|
||||
|
|
@ -165,8 +158,6 @@ public abstract class AbstractManagedChannelImplBuilder
|
|||
@Nullable
|
||||
ProxyDetector proxyDetector;
|
||||
|
||||
boolean enableServiceConfigErrorHandling = DEFAULT_ENABLE_SERVICE_CONFIG_ERROR_HANDLING;
|
||||
|
||||
/**
|
||||
* Sets the maximum message size allowed for a single gRPC frame. If an inbound messages
|
||||
* larger than this limit is received it will not be processed and the RPC will fail with
|
||||
|
|
@ -457,16 +448,6 @@ public abstract class AbstractManagedChannelImplBuilder
|
|||
return thisT();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables service config error handling implemented in {@link ManagedChannelImpl2}. By default,
|
||||
* it is disabled unless system property {@link #ENABLE_SERVICE_CONFIG_ERROR_HANDLING_PROPERTY} is
|
||||
* set to {@code "true"}.
|
||||
*/
|
||||
protected T enableServiceConfigErrorHandling() {
|
||||
this.enableServiceConfigErrorHandling = true;
|
||||
return thisT();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable or enable stats features. Enabled by default.
|
||||
*
|
||||
|
|
@ -527,17 +508,6 @@ public abstract class AbstractManagedChannelImplBuilder
|
|||
|
||||
@Override
|
||||
public ManagedChannel build() {
|
||||
if (this.enableServiceConfigErrorHandling) {
|
||||
return new ManagedChannelOrphanWrapper(new ManagedChannelImpl2(
|
||||
this,
|
||||
buildTransportFactory(),
|
||||
// TODO(carl-mastrangelo): Allow clients to pass this in
|
||||
new ExponentialBackoffPolicy.Provider(),
|
||||
SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
|
||||
GrpcUtil.STOPWATCH_SUPPLIER,
|
||||
getEffectiveInterceptors(),
|
||||
TimeProvider.SYSTEM_TIME_PROVIDER));
|
||||
}
|
||||
return new ManagedChannelOrphanWrapper(new ManagedChannelImpl(
|
||||
this,
|
||||
buildTransportFactory(),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import static io.grpc.LoadBalancer.ATTR_LOAD_BALANCING_CONFIG;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import io.grpc.Attributes;
|
||||
import io.grpc.ChannelLogger;
|
||||
import io.grpc.ChannelLogger.ChannelLogLevel;
|
||||
import io.grpc.ConnectivityState;
|
||||
import io.grpc.ConnectivityStateInfo;
|
||||
|
|
@ -39,13 +42,13 @@ import io.grpc.Status;
|
|||
import io.grpc.internal.ServiceConfigUtil.LbConfig;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressWarnings("deprecation") // migrate to AutoConfiguredLoadBalancerFactory2 is required
|
||||
// TODO(creamsoup) fully deprecate LoadBalancer.ATTR_LOAD_BALANCING_CONFIG
|
||||
@SuppressWarnings("deprecation")
|
||||
public final class AutoConfiguredLoadBalancerFactory {
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(AutoConfiguredLoadBalancerFactory.class.getName());
|
||||
|
|
@ -107,8 +110,10 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns non-OK status if resolvedAddresses is rejected and should be considered as a
|
||||
* name-resolution error.
|
||||
* Returns non-OK status if resolvedAddresses is empty and delegate lb requires address ({@link
|
||||
* LoadBalancer#canHandleEmptyAddressListFromNameResolution()} returns {@code false}). {@code
|
||||
* AutoConfiguredLoadBalancer} doesn't expose {@code
|
||||
* canHandleEmptyAddressListFromNameResolution} because it depends on the delegated LB.
|
||||
*/
|
||||
Status tryHandleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
|
||||
List<EquivalentAddressGroup> servers = resolvedAddresses.getAddresses();
|
||||
|
|
@ -116,12 +121,14 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
if (attributes.get(ATTR_LOAD_BALANCING_CONFIG) != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected ATTR_LOAD_BALANCING_CONFIG from upstream: "
|
||||
+ attributes.get(ATTR_LOAD_BALANCING_CONFIG));
|
||||
+ attributes.get(ATTR_LOAD_BALANCING_CONFIG));
|
||||
}
|
||||
Map<String, ?> configMap = attributes.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG);
|
||||
PolicySelection selection;
|
||||
PolicySelection policySelection =
|
||||
(PolicySelection) resolvedAddresses.getLoadBalancingPolicyConfig();
|
||||
ResolvedPolicySelection resolvedSelection;
|
||||
|
||||
try {
|
||||
selection = decideLoadBalancerProvider(servers, configMap);
|
||||
resolvedSelection = resolveLoadBalancerProvider(servers, policySelection);
|
||||
} catch (PolicyException e) {
|
||||
Status s = Status.INTERNAL.withDescription(e.getMessage());
|
||||
helper.updateBalancingState(ConnectivityState.TRANSIENT_FAILURE, new FailingPicker(s));
|
||||
|
|
@ -130,6 +137,7 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
delegate = new NoopLoadBalancer();
|
||||
return Status.OK;
|
||||
}
|
||||
PolicySelection selection = resolvedSelection.policySelection;
|
||||
|
||||
if (delegateProvider == null
|
||||
|| !selection.provider.getPolicyName().equals(delegateProvider.getPolicyName())) {
|
||||
|
|
@ -142,24 +150,25 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
ChannelLogLevel.INFO, "Load balancer changed from {0} to {1}",
|
||||
old.getClass().getSimpleName(), delegate.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
if (selection.config != null) {
|
||||
Object lbConfig = selection.config;
|
||||
if (lbConfig != null) {
|
||||
helper.getChannelLogger().log(
|
||||
ChannelLogLevel.DEBUG, "Load-balancing config: {0}", selection.config);
|
||||
attributes =
|
||||
attributes.toBuilder().set(ATTR_LOAD_BALANCING_CONFIG, selection.config).build();
|
||||
attributes.toBuilder().set(ATTR_LOAD_BALANCING_CONFIG, selection.rawConfig).build();
|
||||
}
|
||||
|
||||
LoadBalancer delegate = getDelegate();
|
||||
if (selection.serverList.isEmpty()
|
||||
if (resolvedSelection.serverList.isEmpty()
|
||||
&& !delegate.canHandleEmptyAddressListFromNameResolution()) {
|
||||
return Status.UNAVAILABLE.withDescription(
|
||||
"NameResolver returned no usable address. addrs=" + servers + ", attrs=" + attributes);
|
||||
} else {
|
||||
delegate.handleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(selection.serverList)
|
||||
.setAddresses(resolvedSelection.serverList)
|
||||
.setAttributes(attributes)
|
||||
.setLoadBalancingPolicyConfig(lbConfig)
|
||||
.build());
|
||||
return Status.OK;
|
||||
}
|
||||
|
|
@ -199,24 +208,17 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Picks a load balancer based on given criteria. In order of preference:
|
||||
*
|
||||
* <ol>
|
||||
* <li>User provided lb on the channel. This is a degenerate case and not handled here.
|
||||
* This options is deprecated.</li>
|
||||
* <li>Policy from "loadBalancingConfig" if present. This is not covered here.</li>
|
||||
* <li>"grpclb" if any gRPC LB balancer addresses are present</li>
|
||||
* <li>The policy from "loadBalancingPolicy" if present</li>
|
||||
* <li>"pick_first" if the service config choice does not specify</li>
|
||||
* </ol>
|
||||
* Resolves a load balancer based on given criteria. If policySelection is {@code null} and
|
||||
* given servers contains any gRPC LB addresses, it will fall back to "grpclb". If no gRPC LB
|
||||
* addresses are not present, it will fall back to {@link #defaultPolicy}.
|
||||
*
|
||||
* @param servers The list of servers reported
|
||||
* @param config the service config object
|
||||
* @return the new load balancer factory, never null
|
||||
* @param policySelection the selected policy from raw service config
|
||||
* @return the resolved policy selection
|
||||
*/
|
||||
@VisibleForTesting
|
||||
PolicySelection decideLoadBalancerProvider(
|
||||
List<EquivalentAddressGroup> servers, @Nullable Map<String, ?> config)
|
||||
ResolvedPolicySelection resolveLoadBalancerProvider(
|
||||
List<EquivalentAddressGroup> servers, @Nullable PolicySelection policySelection)
|
||||
throws PolicyException {
|
||||
// Check for balancer addresses
|
||||
boolean haveBalancerAddress = false;
|
||||
|
|
@ -229,36 +231,10 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
}
|
||||
}
|
||||
|
||||
List<LbConfig> lbConfigs = null;
|
||||
if (config != null) {
|
||||
List<Map<String, ?>> rawLbConfigs =
|
||||
ServiceConfigUtil.getLoadBalancingConfigsFromServiceConfig(config);
|
||||
lbConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList(rawLbConfigs);
|
||||
}
|
||||
if (lbConfigs != null && !lbConfigs.isEmpty()) {
|
||||
LinkedHashSet<String> policiesTried = new LinkedHashSet<>();
|
||||
for (LbConfig lbConfig : lbConfigs) {
|
||||
String policy = lbConfig.getPolicyName();
|
||||
LoadBalancerProvider provider = registry.getProvider(policy);
|
||||
if (provider == null) {
|
||||
policiesTried.add(policy);
|
||||
} else {
|
||||
if (!policiesTried.isEmpty()) {
|
||||
// Before returning, log all previously tried policies
|
||||
helper.getChannelLogger().log(
|
||||
ChannelLogLevel.DEBUG,
|
||||
"{0} specified by Service Config are not available", policiesTried);
|
||||
}
|
||||
return new PolicySelection(
|
||||
provider,
|
||||
policy.equals(GRPCLB_POLICY_NAME) ? servers : backendAddrs,
|
||||
lbConfig.getRawConfigValue());
|
||||
}
|
||||
}
|
||||
if (!haveBalancerAddress) {
|
||||
throw new PolicyException(
|
||||
"None of " + policiesTried + " specified by Service Config are available.");
|
||||
}
|
||||
if (policySelection != null) {
|
||||
String policyName = policySelection.provider.getPolicyName();
|
||||
return new ResolvedPolicySelection(
|
||||
policySelection, policyName.equals(GRPCLB_POLICY_NAME) ? servers : backendAddrs);
|
||||
}
|
||||
|
||||
if (haveBalancerAddress) {
|
||||
|
|
@ -278,20 +254,29 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
helper.getChannelLogger().log(ChannelLogLevel.ERROR, errorMsg);
|
||||
logger.warning(errorMsg);
|
||||
}
|
||||
return new PolicySelection(
|
||||
getProviderOrThrow(
|
||||
"round_robin", "received balancer addresses but grpclb runtime is missing"),
|
||||
backendAddrs, null);
|
||||
return new ResolvedPolicySelection(
|
||||
new PolicySelection(
|
||||
getProviderOrThrow(
|
||||
"round_robin", "received balancer addresses but grpclb runtime is missing"),
|
||||
/* rawConfig = */ null,
|
||||
/* config= */ null),
|
||||
backendAddrs);
|
||||
}
|
||||
return new PolicySelection(grpclbProvider, servers, null);
|
||||
return new ResolvedPolicySelection(
|
||||
new PolicySelection(
|
||||
grpclbProvider, /* rawConfig= */ null, /* config= */ null), servers);
|
||||
}
|
||||
// No balancer address this time. If balancer address shows up later, we want to make sure
|
||||
// the warning is logged one more time.
|
||||
roundRobinDueToGrpclbDepMissing = false;
|
||||
|
||||
// No config nor balancer address. Use default.
|
||||
return new PolicySelection(
|
||||
getProviderOrThrow(defaultPolicy, "using default policy"), servers, null);
|
||||
return new ResolvedPolicySelection(
|
||||
new PolicySelection(
|
||||
getProviderOrThrow(defaultPolicy, "using default policy"),
|
||||
/* rawConfig= */ null,
|
||||
/* config= */ null),
|
||||
servers);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -306,13 +291,27 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Unlike a normal {@link LoadBalancer.Factory}, this accepts a full service config rather than
|
||||
* Parses first available LoadBalancer policy from service config. Available LoadBalancer should
|
||||
* be registered to {@link LoadBalancerRegistry}. If the first available LoadBalancer policy is
|
||||
* invalid, it doesn't fall-back to next available policy, instead it returns error. This also
|
||||
* means, it ignores LoadBalancer policies after the first available one even if any of them are
|
||||
* invalid.
|
||||
*
|
||||
* <p>Order of policy preference:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Policy from "loadBalancingConfig" if present</li>
|
||||
* <li>The policy from deprecated "loadBalancingPolicy" if present</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* <p>Unlike a normal {@link LoadBalancer.Factory}, this accepts a full service config rather than
|
||||
* the LoadBalancingConfig.
|
||||
*
|
||||
* @return null if no selection could be made.
|
||||
* @return the parsed {@link PolicySelection}, or {@code null} if no selection could be made.
|
||||
*/
|
||||
@Nullable
|
||||
ConfigOrError selectLoadBalancerPolicy(Map<String, ?> serviceConfig) {
|
||||
ConfigOrError parseLoadBalancerPolicy(Map<String, ?> serviceConfig, ChannelLogger channelLogger) {
|
||||
try {
|
||||
List<LbConfig> loadBalancerConfigs = null;
|
||||
if (serviceConfig != null) {
|
||||
|
|
@ -328,10 +327,18 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
if (provider == null) {
|
||||
policiesTried.add(policy);
|
||||
} else {
|
||||
return ConfigOrError.fromConfig(new PolicySelection(
|
||||
provider,
|
||||
/* serverList= */ null,
|
||||
lbConfig.getRawConfigValue()));
|
||||
if (!policiesTried.isEmpty()) {
|
||||
channelLogger.log(
|
||||
ChannelLogLevel.DEBUG,
|
||||
"{0} specified by Service Config are not available", policiesTried);
|
||||
}
|
||||
ConfigOrError parsedLbPolicyConfig =
|
||||
provider.parseLoadBalancingPolicyConfig(lbConfig.getRawConfigValue());
|
||||
if (parsedLbPolicyConfig.getError() != null) {
|
||||
return parsedLbPolicyConfig;
|
||||
}
|
||||
return ConfigOrError.fromConfig(
|
||||
new PolicySelection(provider, serviceConfig, parsedLbPolicyConfig.getConfig()));
|
||||
}
|
||||
}
|
||||
return ConfigOrError.fromError(
|
||||
|
|
@ -357,25 +364,65 @@ public final class AutoConfiguredLoadBalancerFactory {
|
|||
@VisibleForTesting
|
||||
static final class PolicySelection {
|
||||
final LoadBalancerProvider provider;
|
||||
@Nullable final List<EquivalentAddressGroup> serverList;
|
||||
// TODO(carl-mastrangelo): make this the non-raw service config object.
|
||||
@Nullable final Map<String, ?> config;
|
||||
|
||||
PolicySelection(
|
||||
LoadBalancerProvider provider, List<EquivalentAddressGroup> serverList,
|
||||
@Nullable Map<String, ?> config) {
|
||||
this.provider = checkNotNull(provider, "provider");
|
||||
this.serverList = Collections.unmodifiableList(checkNotNull(serverList, "serverList"));
|
||||
this.config = config;
|
||||
}
|
||||
@Nullable final Map<String, ?> rawConfig;
|
||||
@Nullable final Object config;
|
||||
|
||||
PolicySelection(
|
||||
LoadBalancerProvider provider,
|
||||
@Nullable Map<String, ?> config) {
|
||||
@Nullable Map<String, ?> rawConfig,
|
||||
@Nullable Object config) {
|
||||
this.provider = checkNotNull(provider, "provider");
|
||||
this.serverList = null;
|
||||
this.rawConfig = rawConfig;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PolicySelection that = (PolicySelection) o;
|
||||
return Objects.equal(provider, that.provider)
|
||||
&& Objects.equal(rawConfig, that.rawConfig)
|
||||
&& Objects.equal(config, that.config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(provider, rawConfig, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("provider", provider)
|
||||
.add("rawConfig", rawConfig)
|
||||
.add("config", config)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static final class ResolvedPolicySelection {
|
||||
final PolicySelection policySelection;
|
||||
final List<EquivalentAddressGroup> serverList;
|
||||
|
||||
ResolvedPolicySelection(
|
||||
PolicySelection policySelection, List<EquivalentAddressGroup> serverList) {
|
||||
this.policySelection = checkNotNull(policySelection, "policySelection");
|
||||
this.serverList = Collections.unmodifiableList(checkNotNull(serverList, "serverList"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("policySelection", policySelection)
|
||||
.add("serverList", serverList)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class EmptyPicker extends SubchannelPicker {
|
||||
|
|
|
|||
|
|
@ -1,447 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static io.grpc.LoadBalancer.ATTR_LOAD_BALANCING_CONFIG;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import io.grpc.Attributes;
|
||||
import io.grpc.ChannelLogger;
|
||||
import io.grpc.ChannelLogger.ChannelLogLevel;
|
||||
import io.grpc.ConnectivityState;
|
||||
import io.grpc.ConnectivityStateInfo;
|
||||
import io.grpc.EquivalentAddressGroup;
|
||||
import io.grpc.LoadBalancer;
|
||||
import io.grpc.LoadBalancer.Helper;
|
||||
import io.grpc.LoadBalancer.PickResult;
|
||||
import io.grpc.LoadBalancer.PickSubchannelArgs;
|
||||
import io.grpc.LoadBalancer.ResolvedAddresses;
|
||||
import io.grpc.LoadBalancer.Subchannel;
|
||||
import io.grpc.LoadBalancer.SubchannelPicker;
|
||||
import io.grpc.LoadBalancerProvider;
|
||||
import io.grpc.LoadBalancerRegistry;
|
||||
import io.grpc.NameResolver.ConfigOrError;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.internal.ServiceConfigUtil.LbConfig;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressWarnings("deprecation") // after migrated to 2, we can deprecate it
|
||||
public final class AutoConfiguredLoadBalancerFactory2 {
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(AutoConfiguredLoadBalancerFactory2.class.getName());
|
||||
private static final String GRPCLB_POLICY_NAME = "grpclb";
|
||||
|
||||
private final LoadBalancerRegistry registry;
|
||||
private final String defaultPolicy;
|
||||
|
||||
public AutoConfiguredLoadBalancerFactory2(String defaultPolicy) {
|
||||
this(LoadBalancerRegistry.getDefaultRegistry(), defaultPolicy);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AutoConfiguredLoadBalancerFactory2(LoadBalancerRegistry registry, String defaultPolicy) {
|
||||
this.registry = checkNotNull(registry, "registry");
|
||||
this.defaultPolicy = checkNotNull(defaultPolicy, "defaultPolicy");
|
||||
}
|
||||
|
||||
public AutoConfiguredLoadBalancer newLoadBalancer(Helper helper) {
|
||||
return new AutoConfiguredLoadBalancer(helper);
|
||||
}
|
||||
|
||||
private static final class NoopLoadBalancer extends LoadBalancer {
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void handleResolvedAddressGroups(List<EquivalentAddressGroup> s, Attributes a) {}
|
||||
|
||||
@Override
|
||||
public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {}
|
||||
|
||||
@Override
|
||||
public void handleNameResolutionError(Status error) {}
|
||||
|
||||
@Override
|
||||
public void shutdown() {}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public final class AutoConfiguredLoadBalancer {
|
||||
private final Helper helper;
|
||||
private LoadBalancer delegate;
|
||||
private LoadBalancerProvider delegateProvider;
|
||||
private boolean roundRobinDueToGrpclbDepMissing;
|
||||
|
||||
AutoConfiguredLoadBalancer(Helper helper) {
|
||||
this.helper = helper;
|
||||
delegateProvider = registry.getProvider(defaultPolicy);
|
||||
if (delegateProvider == null) {
|
||||
throw new IllegalStateException("Could not find policy '" + defaultPolicy
|
||||
+ "'. Make sure its implementation is either registered to LoadBalancerRegistry or"
|
||||
+ " included in META-INF/services/io.grpc.LoadBalancerProvider from your jar files.");
|
||||
}
|
||||
delegate = delegateProvider.newLoadBalancer(helper);
|
||||
}
|
||||
|
||||
public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
|
||||
tryHandleResolvedAddresses(resolvedAddresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-OK status if resolvedAddresses is empty and delegate lb requires address ({@link
|
||||
* LoadBalancer#canHandleEmptyAddressListFromNameResolution()} returns {@code false}). {@code
|
||||
* AutoConfiguredLoadBalancer} doesn't expose {@code
|
||||
* canHandleEmptyAddressListFromNameResolution} because it depends on the delegated LB.
|
||||
*/
|
||||
Status tryHandleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
|
||||
List<EquivalentAddressGroup> servers = resolvedAddresses.getAddresses();
|
||||
Attributes attributes = resolvedAddresses.getAttributes();
|
||||
if (attributes.get(ATTR_LOAD_BALANCING_CONFIG) != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected ATTR_LOAD_BALANCING_CONFIG from upstream: "
|
||||
+ attributes.get(ATTR_LOAD_BALANCING_CONFIG));
|
||||
}
|
||||
PolicySelection policySelection =
|
||||
(PolicySelection) resolvedAddresses.getLoadBalancingPolicyConfig();
|
||||
ResolvedPolicySelection resolvedSelection;
|
||||
|
||||
try {
|
||||
resolvedSelection = resolveLoadBalancerProvider(servers, policySelection);
|
||||
} catch (PolicyException e) {
|
||||
Status s = Status.INTERNAL.withDescription(e.getMessage());
|
||||
helper.updateBalancingState(ConnectivityState.TRANSIENT_FAILURE, new FailingPicker(s));
|
||||
delegate.shutdown();
|
||||
delegateProvider = null;
|
||||
delegate = new NoopLoadBalancer();
|
||||
return Status.OK;
|
||||
}
|
||||
PolicySelection selection = resolvedSelection.policySelection;
|
||||
|
||||
if (delegateProvider == null
|
||||
|| !selection.provider.getPolicyName().equals(delegateProvider.getPolicyName())) {
|
||||
helper.updateBalancingState(ConnectivityState.CONNECTING, new EmptyPicker());
|
||||
delegate.shutdown();
|
||||
delegateProvider = selection.provider;
|
||||
LoadBalancer old = delegate;
|
||||
delegate = delegateProvider.newLoadBalancer(helper);
|
||||
helper.getChannelLogger().log(
|
||||
ChannelLogLevel.INFO, "Load balancer changed from {0} to {1}",
|
||||
old.getClass().getSimpleName(), delegate.getClass().getSimpleName());
|
||||
}
|
||||
Object lbConfig = selection.config;
|
||||
if (lbConfig != null) {
|
||||
helper.getChannelLogger().log(
|
||||
ChannelLogLevel.DEBUG, "Load-balancing config: {0}", selection.config);
|
||||
attributes =
|
||||
attributes.toBuilder().set(ATTR_LOAD_BALANCING_CONFIG, selection.rawConfig).build();
|
||||
}
|
||||
|
||||
LoadBalancer delegate = getDelegate();
|
||||
if (resolvedSelection.serverList.isEmpty()
|
||||
&& !delegate.canHandleEmptyAddressListFromNameResolution()) {
|
||||
return Status.UNAVAILABLE.withDescription(
|
||||
"NameResolver returned no usable address. addrs=" + servers + ", attrs=" + attributes);
|
||||
} else {
|
||||
delegate.handleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(resolvedSelection.serverList)
|
||||
.setAttributes(attributes)
|
||||
.setLoadBalancingPolicyConfig(lbConfig)
|
||||
.build());
|
||||
return Status.OK;
|
||||
}
|
||||
}
|
||||
|
||||
void handleNameResolutionError(Status error) {
|
||||
getDelegate().handleNameResolutionError(error);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
void handleSubchannelState(Subchannel subchannel, ConnectivityStateInfo stateInfo) {
|
||||
getDelegate().handleSubchannelState(subchannel, stateInfo);
|
||||
}
|
||||
|
||||
void requestConnection() {
|
||||
getDelegate().requestConnection();
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
delegate.shutdown();
|
||||
delegate = null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public LoadBalancer getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setDelegate(LoadBalancer lb) {
|
||||
delegate = lb;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
LoadBalancerProvider getDelegateProvider() {
|
||||
return delegateProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a load balancer based on given criteria. If policySelection is {@code null} and
|
||||
* given servers contains any gRPC LB addresses, it will fall back to "grpclb". If no gRPC LB
|
||||
* addresses are not present, it will fall back to {@link #defaultPolicy}.
|
||||
*
|
||||
* @param servers The list of servers reported
|
||||
* @param policySelection the selected policy from raw service config
|
||||
* @return the resolved policy selection
|
||||
*/
|
||||
@VisibleForTesting
|
||||
ResolvedPolicySelection resolveLoadBalancerProvider(
|
||||
List<EquivalentAddressGroup> servers, @Nullable PolicySelection policySelection)
|
||||
throws PolicyException {
|
||||
// Check for balancer addresses
|
||||
boolean haveBalancerAddress = false;
|
||||
List<EquivalentAddressGroup> backendAddrs = new ArrayList<>();
|
||||
for (EquivalentAddressGroup s : servers) {
|
||||
if (s.getAttributes().get(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY) != null) {
|
||||
haveBalancerAddress = true;
|
||||
} else {
|
||||
backendAddrs.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (policySelection != null) {
|
||||
String policyName = policySelection.provider.getPolicyName();
|
||||
return new ResolvedPolicySelection(
|
||||
policySelection, policyName.equals(GRPCLB_POLICY_NAME) ? servers : backendAddrs);
|
||||
}
|
||||
|
||||
if (haveBalancerAddress) {
|
||||
// This is a special case where the existence of balancer address in the resolved address
|
||||
// selects "grpclb" policy if the service config couldn't select a policy
|
||||
LoadBalancerProvider grpclbProvider = registry.getProvider(GRPCLB_POLICY_NAME);
|
||||
if (grpclbProvider == null) {
|
||||
if (backendAddrs.isEmpty()) {
|
||||
throw new PolicyException(
|
||||
"Received ONLY balancer addresses but grpclb runtime is missing");
|
||||
}
|
||||
if (!roundRobinDueToGrpclbDepMissing) {
|
||||
// We don't log the warning every time we have an update.
|
||||
roundRobinDueToGrpclbDepMissing = true;
|
||||
String errorMsg = "Found balancer addresses but grpclb runtime is missing."
|
||||
+ " Will use round_robin. Please include grpc-grpclb in your runtime dependencies.";
|
||||
helper.getChannelLogger().log(ChannelLogLevel.ERROR, errorMsg);
|
||||
logger.warning(errorMsg);
|
||||
}
|
||||
return new ResolvedPolicySelection(
|
||||
new PolicySelection(
|
||||
getProviderOrThrow(
|
||||
"round_robin", "received balancer addresses but grpclb runtime is missing"),
|
||||
/* rawConfig = */ null,
|
||||
/* config= */ null),
|
||||
backendAddrs);
|
||||
}
|
||||
return new ResolvedPolicySelection(
|
||||
new PolicySelection(
|
||||
grpclbProvider, /* rawConfig= */ null, /* config= */ null), servers);
|
||||
}
|
||||
// No balancer address this time. If balancer address shows up later, we want to make sure
|
||||
// the warning is logged one more time.
|
||||
roundRobinDueToGrpclbDepMissing = false;
|
||||
|
||||
// No config nor balancer address. Use default.
|
||||
return new ResolvedPolicySelection(
|
||||
new PolicySelection(
|
||||
getProviderOrThrow(defaultPolicy, "using default policy"),
|
||||
/* rawConfig= */ null,
|
||||
/* config= */ null),
|
||||
servers);
|
||||
}
|
||||
}
|
||||
|
||||
private LoadBalancerProvider getProviderOrThrow(String policy, String choiceReason)
|
||||
throws PolicyException {
|
||||
LoadBalancerProvider provider = registry.getProvider(policy);
|
||||
if (provider == null) {
|
||||
throw new PolicyException(
|
||||
"Trying to load '" + policy + "' because " + choiceReason + ", but it's unavailable");
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses first available LoadBalancer policy from service config. Available LoadBalancer should
|
||||
* be registered to {@link LoadBalancerRegistry}. If the first available LoadBalancer policy is
|
||||
* invalid, it doesn't fall-back to next available policy, instead it returns error. This also
|
||||
* means, it ignores LoadBalancer policies after the first available one even if any of them are
|
||||
* invalid.
|
||||
*
|
||||
* <p>Order of policy preference:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Policy from "loadBalancingConfig" if present</li>
|
||||
* <li>The policy from deprecated "loadBalancingPolicy" if present</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* <p>Unlike a normal {@link LoadBalancer.Factory}, this accepts a full service config rather than
|
||||
* the LoadBalancingConfig.
|
||||
*
|
||||
* @return the parsed {@link PolicySelection}, or {@code null} if no selection could be made.
|
||||
*/
|
||||
@Nullable
|
||||
ConfigOrError parseLoadBalancerPolicy(Map<String, ?> serviceConfig, ChannelLogger channelLogger) {
|
||||
try {
|
||||
List<LbConfig> loadBalancerConfigs = null;
|
||||
if (serviceConfig != null) {
|
||||
List<Map<String, ?>> rawLbConfigs =
|
||||
ServiceConfigUtil.getLoadBalancingConfigsFromServiceConfig(serviceConfig);
|
||||
loadBalancerConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList(rawLbConfigs);
|
||||
}
|
||||
if (loadBalancerConfigs != null && !loadBalancerConfigs.isEmpty()) {
|
||||
List<String> policiesTried = new ArrayList<>();
|
||||
for (LbConfig lbConfig : loadBalancerConfigs) {
|
||||
String policy = lbConfig.getPolicyName();
|
||||
LoadBalancerProvider provider = registry.getProvider(policy);
|
||||
if (provider == null) {
|
||||
policiesTried.add(policy);
|
||||
} else {
|
||||
if (!policiesTried.isEmpty()) {
|
||||
channelLogger.log(
|
||||
ChannelLogLevel.DEBUG,
|
||||
"{0} specified by Service Config are not available", policiesTried);
|
||||
}
|
||||
ConfigOrError parsedLbPolicyConfig =
|
||||
provider.parseLoadBalancingPolicyConfig(lbConfig.getRawConfigValue());
|
||||
if (parsedLbPolicyConfig.getError() != null) {
|
||||
return parsedLbPolicyConfig;
|
||||
}
|
||||
return ConfigOrError.fromConfig(
|
||||
new PolicySelection(provider, serviceConfig, parsedLbPolicyConfig.getConfig()));
|
||||
}
|
||||
}
|
||||
return ConfigOrError.fromError(
|
||||
Status.UNKNOWN.withDescription(
|
||||
"None of " + policiesTried + " specified by Service Config are available."));
|
||||
}
|
||||
return null;
|
||||
} catch (RuntimeException e) {
|
||||
return ConfigOrError.fromError(
|
||||
Status.UNKNOWN.withDescription("can't parse load balancer configuration").withCause(e));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static final class PolicyException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private PolicyException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static final class PolicySelection {
|
||||
final LoadBalancerProvider provider;
|
||||
@Nullable final Map<String, ?> rawConfig;
|
||||
@Nullable final Object config;
|
||||
|
||||
PolicySelection(
|
||||
LoadBalancerProvider provider,
|
||||
@Nullable Map<String, ?> rawConfig,
|
||||
@Nullable Object config) {
|
||||
this.provider = checkNotNull(provider, "provider");
|
||||
this.rawConfig = rawConfig;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PolicySelection that = (PolicySelection) o;
|
||||
return Objects.equal(provider, that.provider)
|
||||
&& Objects.equal(rawConfig, that.rawConfig)
|
||||
&& Objects.equal(config, that.config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(provider, rawConfig, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("provider", provider)
|
||||
.add("rawConfig", rawConfig)
|
||||
.add("config", config)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static final class ResolvedPolicySelection {
|
||||
final PolicySelection policySelection;
|
||||
final List<EquivalentAddressGroup> serverList;
|
||||
|
||||
ResolvedPolicySelection(
|
||||
PolicySelection policySelection, List<EquivalentAddressGroup> serverList) {
|
||||
this.policySelection = checkNotNull(policySelection, "policySelection");
|
||||
this.serverList = Collections.unmodifiableList(checkNotNull(serverList, "serverList"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("policySelection", policySelection)
|
||||
.add("serverList", serverList)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class EmptyPicker extends SubchannelPicker {
|
||||
|
||||
@Override
|
||||
public PickResult pickSubchannel(PickSubchannelArgs args) {
|
||||
return PickResult.withNoResult();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FailingPicker extends SubchannelPicker {
|
||||
private final Status failure;
|
||||
|
||||
FailingPicker(Status failure) {
|
||||
this.failure = failure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PickResult pickSubchannel(PickSubchannelArgs args) {
|
||||
return PickResult.withError(failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import static io.grpc.internal.ServiceConfigInterceptor.RETRY_POLICY_KEY;
|
|||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
|
@ -96,7 +97,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
|
@ -130,6 +130,11 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
static final Status SUBCHANNEL_SHUTDOWN_STATUS =
|
||||
Status.UNAVAILABLE.withDescription("Subchannel shutdown invoked");
|
||||
|
||||
private static final ServiceConfigHolder EMPTY_SERVICE_CONFIG =
|
||||
new ServiceConfigHolder(
|
||||
Collections.<String, Object>emptyMap(),
|
||||
ManagedChannelServiceConfig.empty());
|
||||
|
||||
private final InternalLogId logId;
|
||||
private final String target;
|
||||
private final NameResolverRegistry nameResolverRegistry;
|
||||
|
|
@ -243,28 +248,22 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
private final ChannelTracer channelTracer;
|
||||
private final ChannelLogger channelLogger;
|
||||
private final InternalChannelz channelz;
|
||||
|
||||
// Must be mutated and read from syncContext
|
||||
@CheckForNull
|
||||
private Boolean haveBackends; // a flag for doing channel tracing when flipped
|
||||
// a flag for doing channel tracing when flipped
|
||||
private ResolutionState lastResolutionState = ResolutionState.NO_RESOLUTION;
|
||||
// Must be mutated and read from constructor or syncContext
|
||||
// TODO(notcarl): check this value when error in service config resolution
|
||||
// used for channel tracing when value changed
|
||||
private ServiceConfigHolder lastServiceConfig = EMPTY_SERVICE_CONFIG;
|
||||
@Nullable
|
||||
private Map<String, ?> lastServiceConfig; // used for channel tracing when value changed
|
||||
@Nullable
|
||||
private final Map<String, ?> defaultServiceConfig;
|
||||
private final ServiceConfigHolder defaultServiceConfig;
|
||||
// Must be mutated and read from constructor or syncContext
|
||||
// See service config error handling spec for reference.
|
||||
// TODO(notcarl): check this value when error in service config resolution
|
||||
@SuppressWarnings("UnusedVariable")
|
||||
private boolean waitingForServiceConfig = true;
|
||||
private boolean serviceConfigUpdated = false;
|
||||
private final boolean lookUpServiceConfig;
|
||||
|
||||
// One instance per channel.
|
||||
private final ChannelBufferMeter channelBufferUsed = new ChannelBufferMeter();
|
||||
|
||||
@Nullable
|
||||
private Throttle throttle;
|
||||
|
||||
private final long perRpcBufferLimit;
|
||||
private final long channelBufferLimit;
|
||||
|
||||
|
|
@ -504,6 +503,7 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
final Metadata headers,
|
||||
final Context context) {
|
||||
checkState(retryEnabled, "retry should be enabled");
|
||||
final Throttle throttle = lastServiceConfig.managedChannelServiceConfig.getRetryThrottling();
|
||||
final class RetryStream extends RetriableStream<ReqT> {
|
||||
RetryStream() {
|
||||
super(
|
||||
|
|
@ -582,18 +582,20 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
new ExecutorHolder(
|
||||
checkNotNull(builder.offloadExecutorPool, "offloadExecutorPool"));
|
||||
this.nameResolverRegistry = builder.nameResolverRegistry;
|
||||
ScParser serviceConfigParser =
|
||||
new ScParser(
|
||||
retryEnabled,
|
||||
builder.maxRetryAttempts,
|
||||
builder.maxHedgedAttempts,
|
||||
loadBalancerFactory,
|
||||
channelLogger);
|
||||
this.nameResolverArgs =
|
||||
NameResolver.Args.newBuilder()
|
||||
.setDefaultPort(builder.getDefaultPort())
|
||||
.setProxyDetector(proxyDetector)
|
||||
.setSynchronizationContext(syncContext)
|
||||
.setScheduledExecutorService(scheduledExecutor)
|
||||
.setServiceConfigParser(
|
||||
new ScParser(
|
||||
retryEnabled,
|
||||
builder.maxRetryAttempts,
|
||||
builder.maxHedgedAttempts,
|
||||
loadBalancerFactory))
|
||||
.setServiceConfigParser(serviceConfigParser)
|
||||
.setChannelLogger(channelLogger)
|
||||
.setOffloadExecutor(
|
||||
// Avoid creating the offloadExecutor until it is first used
|
||||
|
|
@ -610,10 +612,23 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
this.delayedTransport = new DelayedClientTransport(this.executor, this.syncContext);
|
||||
this.delayedTransport.start(delayedTransportListener);
|
||||
this.backoffPolicyProvider = backoffPolicyProvider;
|
||||
serviceConfigInterceptor = new ServiceConfigInterceptor(
|
||||
retryEnabled, builder.maxRetryAttempts, builder.maxHedgedAttempts);
|
||||
this.defaultServiceConfig = builder.defaultServiceConfig;
|
||||
this.lastServiceConfig = defaultServiceConfig;
|
||||
|
||||
serviceConfigInterceptor = new ServiceConfigInterceptor(retryEnabled);
|
||||
if (builder.defaultServiceConfig != null) {
|
||||
ConfigOrError parsedDefaultServiceConfig =
|
||||
serviceConfigParser.parseServiceConfig(builder.defaultServiceConfig);
|
||||
checkState(
|
||||
parsedDefaultServiceConfig.getError() == null,
|
||||
"Default config is invalid: %s",
|
||||
parsedDefaultServiceConfig.getError());
|
||||
this.defaultServiceConfig =
|
||||
new ServiceConfigHolder(
|
||||
builder.defaultServiceConfig,
|
||||
(ManagedChannelServiceConfig) parsedDefaultServiceConfig.getConfig());
|
||||
this.lastServiceConfig = this.defaultServiceConfig;
|
||||
} else {
|
||||
this.defaultServiceConfig = null;
|
||||
}
|
||||
this.lookUpServiceConfig = builder.lookUpServiceConfig;
|
||||
Channel channel = new RealChannel(nameResolver.getServiceAuthority());
|
||||
channel = ClientInterceptors.intercept(channel, serviceConfigInterceptor);
|
||||
|
|
@ -667,11 +682,8 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
|
||||
// May only be called in constructor or syncContext
|
||||
private void handleServiceConfigUpdate() {
|
||||
waitingForServiceConfig = false;
|
||||
serviceConfigInterceptor.handleUpdate(lastServiceConfig);
|
||||
if (retryEnabled) {
|
||||
throttle = ServiceConfigUtil.getThrottlePolicy(lastServiceConfig);
|
||||
}
|
||||
serviceConfigUpdated = true;
|
||||
serviceConfigInterceptor.handleUpdate(lastServiceConfig.managedChannelServiceConfig);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
@ -1309,48 +1321,73 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
Attributes attrs = resolutionResult.getAttributes();
|
||||
channelLogger.log(
|
||||
ChannelLogLevel.DEBUG, "Resolved address: {0}, config={1}", servers, attrs);
|
||||
ResolutionState lastResolutionStateCopy = lastResolutionState;
|
||||
|
||||
if (haveBackends == null || !haveBackends) {
|
||||
if (lastResolutionState != ResolutionState.SUCCESS) {
|
||||
channelLogger.log(ChannelLogLevel.INFO, "Address resolved: {0}", servers);
|
||||
haveBackends = true;
|
||||
lastResolutionState = ResolutionState.SUCCESS;
|
||||
}
|
||||
|
||||
nameResolverBackoffPolicy = null;
|
||||
ConfigOrError configOrError = resolutionResult.getServiceConfig();
|
||||
ServiceConfigHolder validServiceConfig = null;
|
||||
Status serviceConfigError = null;
|
||||
if (configOrError != null) {
|
||||
Map<String, ?> rawServiceConfig =
|
||||
resolutionResult.getAttributes().get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG);
|
||||
validServiceConfig = configOrError.getConfig() == null
|
||||
? null
|
||||
: new ServiceConfigHolder(
|
||||
rawServiceConfig, (ManagedChannelServiceConfig) configOrError.getConfig());
|
||||
serviceConfigError = configOrError.getError();
|
||||
}
|
||||
|
||||
// Assuming no error in config resolution for now.
|
||||
final Map<String, ?> serviceConfig =
|
||||
attrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG);
|
||||
Map<String, ?> effectiveServiceConfig;
|
||||
ServiceConfigHolder effectiveServiceConfig;
|
||||
if (!lookUpServiceConfig) {
|
||||
if (serviceConfig != null) {
|
||||
if (validServiceConfig != null) {
|
||||
channelLogger.log(
|
||||
ChannelLogLevel.INFO,
|
||||
"Service config from name resolver discarded by channel settings");
|
||||
}
|
||||
effectiveServiceConfig = defaultServiceConfig;
|
||||
effectiveServiceConfig =
|
||||
defaultServiceConfig == null ? EMPTY_SERVICE_CONFIG : defaultServiceConfig;
|
||||
attrs = attrs.toBuilder().discard(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG).build();
|
||||
} else {
|
||||
// Try to use config if returned from name resolver
|
||||
// Otherwise, try to use the default config if available
|
||||
if (serviceConfig != null) {
|
||||
effectiveServiceConfig = serviceConfig;
|
||||
} else {
|
||||
if (validServiceConfig != null) {
|
||||
effectiveServiceConfig = validServiceConfig;
|
||||
} else if (defaultServiceConfig != null) {
|
||||
effectiveServiceConfig = defaultServiceConfig;
|
||||
if (defaultServiceConfig != null) {
|
||||
channelLogger.log(
|
||||
ChannelLogLevel.INFO,
|
||||
"Received no service config, using default service config");
|
||||
} else if (serviceConfigError != null) {
|
||||
if (!serviceConfigUpdated) {
|
||||
// First DNS lookup has invalid service config, and cannot fall back to default
|
||||
channelLogger.log(
|
||||
ChannelLogLevel.INFO,
|
||||
"Received no service config, using default service config");
|
||||
"Fallback to error due to invalid first service config without default config");
|
||||
onError(configOrError.getError());
|
||||
return;
|
||||
} else {
|
||||
effectiveServiceConfig = lastServiceConfig;
|
||||
}
|
||||
} else {
|
||||
effectiveServiceConfig = EMPTY_SERVICE_CONFIG;
|
||||
}
|
||||
|
||||
// FIXME(notcarl): reference equality is not right (although not harmful) right now.
|
||||
// Name resolver should return the same config if txt record is the same
|
||||
if (effectiveServiceConfig != lastServiceConfig) {
|
||||
channelLogger.log(ChannelLogLevel.INFO,
|
||||
"Service config changed{0}", effectiveServiceConfig == null ? " to null" : "");
|
||||
if (!effectiveServiceConfig.equals(lastServiceConfig)) {
|
||||
channelLogger.log(
|
||||
ChannelLogLevel.INFO,
|
||||
"Service config changed{0}",
|
||||
effectiveServiceConfig == EMPTY_SERVICE_CONFIG ? " to empty" : "");
|
||||
lastServiceConfig = effectiveServiceConfig;
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO(creamsoup): when `servers` is empty and lastResolutionStateCopy == SUCCESS
|
||||
// and lbNeedAddress, it shouldn't call the handleServiceConfigUpdate. But,
|
||||
// lbNeedAddress is not deterministic
|
||||
handleServiceConfigUpdate();
|
||||
} catch (RuntimeException re) {
|
||||
logger.log(
|
||||
|
|
@ -1363,18 +1400,31 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
// Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match.
|
||||
if (NameResolverListener.this.helper == ManagedChannelImpl.this.lbHelper) {
|
||||
Attributes effectiveAttrs = attrs;
|
||||
if (effectiveServiceConfig != serviceConfig) {
|
||||
if (effectiveServiceConfig != validServiceConfig) {
|
||||
effectiveAttrs = attrs.toBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, effectiveServiceConfig)
|
||||
.set(
|
||||
GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG,
|
||||
effectiveServiceConfig.rawServiceConfig)
|
||||
.build();
|
||||
}
|
||||
|
||||
Status handleResult = helper.lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(effectiveAttrs)
|
||||
.build());
|
||||
.setAddresses(servers)
|
||||
.setAttributes(effectiveAttrs)
|
||||
.setLoadBalancingPolicyConfig(
|
||||
effectiveServiceConfig.managedChannelServiceConfig.getLoadBalancingConfig())
|
||||
.build());
|
||||
|
||||
if (!handleResult.isOk()) {
|
||||
handleErrorInSyncContext(handleResult.augmentDescription(resolver + " was used"));
|
||||
if (servers.isEmpty() && lastResolutionStateCopy == ResolutionState.SUCCESS) {
|
||||
// lb doesn't expose that it needs address or not, because for some LB it is not
|
||||
// deterministic. Assuming lb needs address if LB returns error when the address is
|
||||
// empty and it is not the first resolution.
|
||||
scheduleExponentialBackOffInSyncContext();
|
||||
} else {
|
||||
handleErrorInSyncContext(handleResult.augmentDescription(resolver + " was used"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1399,15 +1449,21 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
private void handleErrorInSyncContext(Status error) {
|
||||
logger.log(Level.WARNING, "[{0}] Failed to resolve name. status={1}",
|
||||
new Object[] {getLogId(), error});
|
||||
if (haveBackends == null || haveBackends) {
|
||||
if (lastResolutionState != ResolutionState.ERROR) {
|
||||
channelLogger.log(ChannelLogLevel.WARNING, "Failed to resolve name: {0}", error);
|
||||
haveBackends = false;
|
||||
lastResolutionState = ResolutionState.ERROR;
|
||||
}
|
||||
// Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match.
|
||||
if (NameResolverListener.this.helper != ManagedChannelImpl.this.lbHelper) {
|
||||
return;
|
||||
}
|
||||
|
||||
helper.lb.handleNameResolutionError(error);
|
||||
|
||||
scheduleExponentialBackOffInSyncContext();
|
||||
}
|
||||
|
||||
private void scheduleExponentialBackOffInSyncContext() {
|
||||
if (scheduledNameResolverRefresh != null && scheduledNameResolverRefresh.isPending()) {
|
||||
// The name resolver may invoke onError multiple times, but we only want to
|
||||
// schedule one backoff attempt
|
||||
|
|
@ -1845,17 +1901,20 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
private final int maxRetryAttemptsLimit;
|
||||
private final int maxHedgedAttemptsLimit;
|
||||
private final AutoConfiguredLoadBalancerFactory autoLoadBalancerFactory;
|
||||
private final ChannelLogger channelLogger;
|
||||
|
||||
ScParser(
|
||||
boolean retryEnabled,
|
||||
int maxRetryAttemptsLimit,
|
||||
int maxHedgedAttemptsLimit,
|
||||
AutoConfiguredLoadBalancerFactory autoLoadBalancerFactory) {
|
||||
AutoConfiguredLoadBalancerFactory autoLoadBalancerFactory,
|
||||
ChannelLogger channelLogger) {
|
||||
this.retryEnabled = retryEnabled;
|
||||
this.maxRetryAttemptsLimit = maxRetryAttemptsLimit;
|
||||
this.maxHedgedAttemptsLimit = maxHedgedAttemptsLimit;
|
||||
this.autoLoadBalancerFactory =
|
||||
checkNotNull(autoLoadBalancerFactory, "autoLoadBalancerFactory");
|
||||
this.channelLogger = checkNotNull(channelLogger, "channelLogger");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1863,7 +1922,7 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
try {
|
||||
Object loadBalancingPolicySelection;
|
||||
ConfigOrError choiceFromLoadBalancer =
|
||||
autoLoadBalancerFactory.selectLoadBalancerPolicy(rawServiceConfig);
|
||||
autoLoadBalancerFactory.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
if (choiceFromLoadBalancer == null) {
|
||||
loadBalancingPolicySelection = null;
|
||||
} else if (choiceFromLoadBalancer.getError() != null) {
|
||||
|
|
@ -1895,4 +1954,54 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
+ "See https://github.com/grpc/grpc-java/issues/5015 for more details", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A ResolutionState indicates the status of last name resolution.
|
||||
*/
|
||||
enum ResolutionState {
|
||||
NO_RESOLUTION,
|
||||
SUCCESS,
|
||||
ERROR
|
||||
}
|
||||
|
||||
// TODO(creamsoup) remove this class when AutoConfiguredLoadBalancerFactory doesn't require raw
|
||||
// service config.
|
||||
private static final class ServiceConfigHolder {
|
||||
Map<String, ?> rawServiceConfig;
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig;
|
||||
|
||||
ServiceConfigHolder(
|
||||
Map<String, ?> rawServiceConfig, ManagedChannelServiceConfig managedChannelServiceConfig) {
|
||||
this.rawServiceConfig = checkNotNull(rawServiceConfig, "rawServiceConfig");
|
||||
this.managedChannelServiceConfig =
|
||||
checkNotNull(managedChannelServiceConfig, "managedChannelServiceConfig");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ServiceConfigHolder that = (ServiceConfigHolder) o;
|
||||
return Objects.equal(rawServiceConfig, that.rawServiceConfig)
|
||||
&& Objects
|
||||
.equal(managedChannelServiceConfig, that.managedChannelServiceConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(rawServiceConfig, managedChannelServiceConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("rawServiceConfig", rawServiceConfig)
|
||||
.add("managedChannelServiceConfig", managedChannelServiceConfig)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -39,9 +39,7 @@ final class ManagedChannelServiceConfig {
|
|||
|
||||
private final Map<String, MethodInfo> serviceMethodMap;
|
||||
private final Map<String, MethodInfo> serviceMap;
|
||||
// TODO(notcarl/zdapeng): use retryThrottling here
|
||||
@Nullable
|
||||
@SuppressWarnings("unused")
|
||||
private final Throttle retryThrottling;
|
||||
@Nullable
|
||||
private final Object loadBalancingConfig;
|
||||
|
|
@ -57,6 +55,16 @@ final class ManagedChannelServiceConfig {
|
|||
this.loadBalancingConfig = loadBalancingConfig;
|
||||
}
|
||||
|
||||
/** Returns an empty {@link ManagedChannelServiceConfig}. */
|
||||
static ManagedChannelServiceConfig empty() {
|
||||
return
|
||||
new ManagedChannelServiceConfig(
|
||||
new HashMap<String, MethodInfo>(),
|
||||
new HashMap<String, MethodInfo>(),
|
||||
/* retryThrottling= */ null,
|
||||
/* loadBalancingConfig= */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the Channel level config values (e.g. excludes load balancing)
|
||||
*/
|
||||
|
|
@ -138,6 +146,41 @@ final class ManagedChannelServiceConfig {
|
|||
return loadBalancingConfig;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Throttle getRetryThrottling() {
|
||||
return retryThrottling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ManagedChannelServiceConfig that = (ManagedChannelServiceConfig) o;
|
||||
return Objects.equal(serviceMethodMap, that.serviceMethodMap)
|
||||
&& Objects.equal(serviceMap, that.serviceMap)
|
||||
&& Objects.equal(retryThrottling, that.retryThrottling)
|
||||
&& Objects.equal(loadBalancingConfig, that.loadBalancingConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(serviceMethodMap, serviceMap, retryThrottling, loadBalancingConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("serviceMethodMap", serviceMethodMap)
|
||||
.add("serviceMap", serviceMap)
|
||||
.add("retryThrottling", retryThrottling)
|
||||
.add("loadBalancingConfig", loadBalancingConfig)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of MethodConfig from a ServiceConfig with restrictions from Channel setting.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,322 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Strings;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.internal.RetriableStream.Throttle;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* {@link ManagedChannelServiceConfig2} is a fully parsed and validated representation of service
|
||||
* configuration data.
|
||||
*/
|
||||
final class ManagedChannelServiceConfig2 {
|
||||
|
||||
private final Map<String, MethodInfo> serviceMethodMap;
|
||||
private final Map<String, MethodInfo> serviceMap;
|
||||
@Nullable
|
||||
private final Throttle retryThrottling;
|
||||
@Nullable
|
||||
private final Object loadBalancingConfig;
|
||||
|
||||
ManagedChannelServiceConfig2(
|
||||
Map<String, MethodInfo> serviceMethodMap,
|
||||
Map<String, MethodInfo> serviceMap,
|
||||
@Nullable Throttle retryThrottling,
|
||||
@Nullable Object loadBalancingConfig) {
|
||||
this.serviceMethodMap = Collections.unmodifiableMap(new HashMap<>(serviceMethodMap));
|
||||
this.serviceMap = Collections.unmodifiableMap(new HashMap<>(serviceMap));
|
||||
this.retryThrottling = retryThrottling;
|
||||
this.loadBalancingConfig = loadBalancingConfig;
|
||||
}
|
||||
|
||||
/** Returns an empty {@link ManagedChannelServiceConfig2}. */
|
||||
static ManagedChannelServiceConfig2 empty() {
|
||||
return
|
||||
new ManagedChannelServiceConfig2(
|
||||
new HashMap<String, MethodInfo>(),
|
||||
new HashMap<String, MethodInfo>(),
|
||||
/* retryThrottling= */ null,
|
||||
/* loadBalancingConfig= */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the Channel level config values (e.g. excludes load balancing)
|
||||
*/
|
||||
static ManagedChannelServiceConfig2 fromServiceConfig(
|
||||
Map<String, ?> serviceConfig,
|
||||
boolean retryEnabled,
|
||||
int maxRetryAttemptsLimit,
|
||||
int maxHedgedAttemptsLimit,
|
||||
@Nullable Object loadBalancingConfig) {
|
||||
Throttle retryThrottling = null;
|
||||
if (retryEnabled) {
|
||||
retryThrottling = ServiceConfigUtil.getThrottlePolicy(serviceConfig);
|
||||
}
|
||||
Map<String, MethodInfo> serviceMethodMap = new HashMap<>();
|
||||
Map<String, MethodInfo> serviceMap = new HashMap<>();
|
||||
|
||||
// Try and do as much validation here before we swap out the existing configuration. In case
|
||||
// the input is invalid, we don't want to lose the existing configuration.
|
||||
List<Map<String, ?>> methodConfigs =
|
||||
ServiceConfigUtil.getMethodConfigFromServiceConfig(serviceConfig);
|
||||
|
||||
if (methodConfigs == null) {
|
||||
// this is surprising, but possible.
|
||||
return new ManagedChannelServiceConfig2(
|
||||
serviceMethodMap, serviceMap, retryThrottling, loadBalancingConfig);
|
||||
}
|
||||
|
||||
for (Map<String, ?> methodConfig : methodConfigs) {
|
||||
MethodInfo info = new MethodInfo(
|
||||
methodConfig, retryEnabled, maxRetryAttemptsLimit, maxHedgedAttemptsLimit);
|
||||
|
||||
List<Map<String, ?>> nameList =
|
||||
ServiceConfigUtil.getNameListFromMethodConfig(methodConfig);
|
||||
|
||||
checkArgument(
|
||||
nameList != null && !nameList.isEmpty(), "no names in method config %s", methodConfig);
|
||||
for (Map<String, ?> name : nameList) {
|
||||
String serviceName = ServiceConfigUtil.getServiceFromName(name);
|
||||
checkArgument(!Strings.isNullOrEmpty(serviceName), "missing service name");
|
||||
String methodName = ServiceConfigUtil.getMethodFromName(name);
|
||||
if (Strings.isNullOrEmpty(methodName)) {
|
||||
// Service scoped config
|
||||
checkArgument(
|
||||
!serviceMap.containsKey(serviceName), "Duplicate service %s", serviceName);
|
||||
serviceMap.put(serviceName, info);
|
||||
} else {
|
||||
// Method scoped config
|
||||
String fullMethodName = MethodDescriptor.generateFullMethodName(serviceName, methodName);
|
||||
checkArgument(
|
||||
!serviceMethodMap.containsKey(fullMethodName),
|
||||
"Duplicate method name %s",
|
||||
fullMethodName);
|
||||
serviceMethodMap.put(fullMethodName, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ManagedChannelServiceConfig2(
|
||||
serviceMethodMap, serviceMap, retryThrottling, loadBalancingConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the per-service configuration for the channel.
|
||||
*/
|
||||
Map<String, MethodInfo> getServiceMap() {
|
||||
return serviceMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the per-method configuration for the channel.
|
||||
*/
|
||||
Map<String, MethodInfo> getServiceMethodMap() {
|
||||
return serviceMethodMap;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
Object getLoadBalancingConfig() {
|
||||
return loadBalancingConfig;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Throttle getRetryThrottling() {
|
||||
return retryThrottling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ManagedChannelServiceConfig2 that = (ManagedChannelServiceConfig2) o;
|
||||
return Objects.equal(serviceMethodMap, that.serviceMethodMap)
|
||||
&& Objects.equal(serviceMap, that.serviceMap)
|
||||
&& Objects.equal(retryThrottling, that.retryThrottling)
|
||||
&& Objects.equal(loadBalancingConfig, that.loadBalancingConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(serviceMethodMap, serviceMap, retryThrottling, loadBalancingConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("serviceMethodMap", serviceMethodMap)
|
||||
.add("serviceMap", serviceMap)
|
||||
.add("retryThrottling", retryThrottling)
|
||||
.add("loadBalancingConfig", loadBalancingConfig)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of MethodConfig from a ServiceConfig with restrictions from Channel setting.
|
||||
*/
|
||||
static final class MethodInfo {
|
||||
// TODO(carl-mastrangelo): add getters for these fields and make them private.
|
||||
final Long timeoutNanos;
|
||||
final Boolean waitForReady;
|
||||
final Integer maxInboundMessageSize;
|
||||
final Integer maxOutboundMessageSize;
|
||||
final RetryPolicy retryPolicy;
|
||||
final HedgingPolicy hedgingPolicy;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param retryEnabled when false, the argument maxRetryAttemptsLimit will have no effect.
|
||||
*/
|
||||
MethodInfo(
|
||||
Map<String, ?> methodConfig, boolean retryEnabled, int maxRetryAttemptsLimit,
|
||||
int maxHedgedAttemptsLimit) {
|
||||
timeoutNanos = ServiceConfigUtil.getTimeoutFromMethodConfig(methodConfig);
|
||||
waitForReady = ServiceConfigUtil.getWaitForReadyFromMethodConfig(methodConfig);
|
||||
maxInboundMessageSize =
|
||||
ServiceConfigUtil.getMaxResponseMessageBytesFromMethodConfig(methodConfig);
|
||||
if (maxInboundMessageSize != null) {
|
||||
checkArgument(
|
||||
maxInboundMessageSize >= 0,
|
||||
"maxInboundMessageSize %s exceeds bounds", maxInboundMessageSize);
|
||||
}
|
||||
maxOutboundMessageSize =
|
||||
ServiceConfigUtil.getMaxRequestMessageBytesFromMethodConfig(methodConfig);
|
||||
if (maxOutboundMessageSize != null) {
|
||||
checkArgument(
|
||||
maxOutboundMessageSize >= 0,
|
||||
"maxOutboundMessageSize %s exceeds bounds", maxOutboundMessageSize);
|
||||
}
|
||||
|
||||
Map<String, ?> retryPolicyMap =
|
||||
retryEnabled ? ServiceConfigUtil.getRetryPolicyFromMethodConfig(methodConfig) : null;
|
||||
retryPolicy = retryPolicyMap == null
|
||||
? RetryPolicy.DEFAULT : retryPolicy(retryPolicyMap, maxRetryAttemptsLimit);
|
||||
|
||||
Map<String, ?> hedgingPolicyMap =
|
||||
retryEnabled ? ServiceConfigUtil.getHedgingPolicyFromMethodConfig(methodConfig) : null;
|
||||
hedgingPolicy = hedgingPolicyMap == null
|
||||
? HedgingPolicy.DEFAULT : hedgingPolicy(hedgingPolicyMap, maxHedgedAttemptsLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(
|
||||
timeoutNanos,
|
||||
waitForReady,
|
||||
maxInboundMessageSize,
|
||||
maxOutboundMessageSize,
|
||||
retryPolicy,
|
||||
hedgingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof MethodInfo)) {
|
||||
return false;
|
||||
}
|
||||
MethodInfo that = (MethodInfo) other;
|
||||
return Objects.equal(this.timeoutNanos, that.timeoutNanos)
|
||||
&& Objects.equal(this.waitForReady, that.waitForReady)
|
||||
&& Objects.equal(this.maxInboundMessageSize, that.maxInboundMessageSize)
|
||||
&& Objects.equal(this.maxOutboundMessageSize, that.maxOutboundMessageSize)
|
||||
&& Objects.equal(this.retryPolicy, that.retryPolicy)
|
||||
&& Objects.equal(this.hedgingPolicy, that.hedgingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("timeoutNanos", timeoutNanos)
|
||||
.add("waitForReady", waitForReady)
|
||||
.add("maxInboundMessageSize", maxInboundMessageSize)
|
||||
.add("maxOutboundMessageSize", maxOutboundMessageSize)
|
||||
.add("retryPolicy", retryPolicy)
|
||||
.add("hedgingPolicy", hedgingPolicy)
|
||||
.toString();
|
||||
}
|
||||
|
||||
private static RetryPolicy retryPolicy(Map<String, ?> retryPolicy, int maxAttemptsLimit) {
|
||||
int maxAttempts = checkNotNull(
|
||||
ServiceConfigUtil.getMaxAttemptsFromRetryPolicy(retryPolicy),
|
||||
"maxAttempts cannot be empty");
|
||||
checkArgument(maxAttempts >= 2, "maxAttempts must be greater than 1: %s", maxAttempts);
|
||||
maxAttempts = Math.min(maxAttempts, maxAttemptsLimit);
|
||||
|
||||
long initialBackoffNanos = checkNotNull(
|
||||
ServiceConfigUtil.getInitialBackoffNanosFromRetryPolicy(retryPolicy),
|
||||
"initialBackoff cannot be empty");
|
||||
checkArgument(
|
||||
initialBackoffNanos > 0,
|
||||
"initialBackoffNanos must be greater than 0: %s",
|
||||
initialBackoffNanos);
|
||||
|
||||
long maxBackoffNanos = checkNotNull(
|
||||
ServiceConfigUtil.getMaxBackoffNanosFromRetryPolicy(retryPolicy),
|
||||
"maxBackoff cannot be empty");
|
||||
checkArgument(
|
||||
maxBackoffNanos > 0, "maxBackoff must be greater than 0: %s", maxBackoffNanos);
|
||||
|
||||
double backoffMultiplier = checkNotNull(
|
||||
ServiceConfigUtil.getBackoffMultiplierFromRetryPolicy(retryPolicy),
|
||||
"backoffMultiplier cannot be empty");
|
||||
checkArgument(
|
||||
backoffMultiplier > 0,
|
||||
"backoffMultiplier must be greater than 0: %s",
|
||||
backoffMultiplier);
|
||||
|
||||
return new RetryPolicy(
|
||||
maxAttempts, initialBackoffNanos, maxBackoffNanos, backoffMultiplier,
|
||||
ServiceConfigUtil.getRetryableStatusCodesFromRetryPolicy(retryPolicy));
|
||||
}
|
||||
|
||||
private static HedgingPolicy hedgingPolicy(
|
||||
Map<String, ?> hedgingPolicy, int maxAttemptsLimit) {
|
||||
int maxAttempts = checkNotNull(
|
||||
ServiceConfigUtil.getMaxAttemptsFromHedgingPolicy(hedgingPolicy),
|
||||
"maxAttempts cannot be empty");
|
||||
checkArgument(maxAttempts >= 2, "maxAttempts must be greater than 1: %s", maxAttempts);
|
||||
maxAttempts = Math.min(maxAttempts, maxAttemptsLimit);
|
||||
|
||||
long hedgingDelayNanos = checkNotNull(
|
||||
ServiceConfigUtil.getHedgingDelayNanosFromHedgingPolicy(hedgingPolicy),
|
||||
"hedgingDelay cannot be empty");
|
||||
checkArgument(
|
||||
hedgingDelayNanos >= 0, "hedgingDelay must not be negative: %s", hedgingDelayNanos);
|
||||
|
||||
return new HedgingPolicy(
|
||||
maxAttempts, hedgingDelayNanos,
|
||||
ServiceConfigUtil.getNonFatalStatusCodesFromHedgingPolicy(hedgingPolicy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,8 +26,6 @@ import io.grpc.ClientInterceptor;
|
|||
import io.grpc.Deadline;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.internal.ManagedChannelServiceConfig.MethodInfo;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.annotation.CheckForNull;
|
||||
|
|
@ -40,34 +38,20 @@ final class ServiceConfigInterceptor implements ClientInterceptor {
|
|||
|
||||
// Map from method name to MethodInfo
|
||||
@VisibleForTesting
|
||||
final AtomicReference<ManagedChannelServiceConfig> managedChannelServiceConfig
|
||||
= new AtomicReference<>();
|
||||
final AtomicReference<ManagedChannelServiceConfig> managedChannelServiceConfig =
|
||||
new AtomicReference<>();
|
||||
|
||||
private final boolean retryEnabled;
|
||||
private final int maxRetryAttemptsLimit;
|
||||
private final int maxHedgedAttemptsLimit;
|
||||
|
||||
// Setting this to true and observing this equal to true are run in different threads.
|
||||
private volatile boolean initComplete;
|
||||
|
||||
ServiceConfigInterceptor(
|
||||
boolean retryEnabled, int maxRetryAttemptsLimit, int maxHedgedAttemptsLimit) {
|
||||
ServiceConfigInterceptor(boolean retryEnabled) {
|
||||
this.retryEnabled = retryEnabled;
|
||||
this.maxRetryAttemptsLimit = maxRetryAttemptsLimit;
|
||||
this.maxHedgedAttemptsLimit = maxHedgedAttemptsLimit;
|
||||
}
|
||||
|
||||
void handleUpdate(@Nullable Map<String, ?> serviceConfig) {
|
||||
// TODO(carl-mastrangelo): delete this.
|
||||
ManagedChannelServiceConfig conf;
|
||||
if (serviceConfig == null) {
|
||||
conf = new ManagedChannelServiceConfig(
|
||||
new HashMap<String, MethodInfo>(), new HashMap<String, MethodInfo>(), null, null);
|
||||
} else {
|
||||
conf = ManagedChannelServiceConfig.fromServiceConfig(
|
||||
serviceConfig, retryEnabled, maxRetryAttemptsLimit, maxHedgedAttemptsLimit, null);
|
||||
}
|
||||
managedChannelServiceConfig.set(conf);
|
||||
void handleUpdate(@Nullable ManagedChannelServiceConfig serviceConfig) {
|
||||
managedChannelServiceConfig.set(serviceConfig);
|
||||
initComplete = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,199 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.ClientCall;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.Deadline;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.internal.ManagedChannelServiceConfig2.MethodInfo;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Modifies RPCs in conformance with a Service Config.
|
||||
*/
|
||||
final class ServiceConfigInterceptor2 implements ClientInterceptor {
|
||||
|
||||
// Map from method name to MethodInfo
|
||||
@VisibleForTesting
|
||||
final AtomicReference<ManagedChannelServiceConfig2> managedChannelServiceConfig =
|
||||
new AtomicReference<>();
|
||||
|
||||
private final boolean retryEnabled;
|
||||
|
||||
// Setting this to true and observing this equal to true are run in different threads.
|
||||
private volatile boolean initComplete;
|
||||
|
||||
ServiceConfigInterceptor2(boolean retryEnabled) {
|
||||
this.retryEnabled = retryEnabled;
|
||||
}
|
||||
|
||||
void handleUpdate(@Nullable ManagedChannelServiceConfig2 serviceConfig) {
|
||||
managedChannelServiceConfig.set(serviceConfig);
|
||||
initComplete = true;
|
||||
}
|
||||
|
||||
static final CallOptions.Key<RetryPolicy.Provider> RETRY_POLICY_KEY =
|
||||
CallOptions.Key.create("internal-retry-policy");
|
||||
static final CallOptions.Key<HedgingPolicy.Provider> HEDGING_POLICY_KEY =
|
||||
CallOptions.Key.create("internal-hedging-policy");
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
|
||||
final MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
|
||||
if (retryEnabled) {
|
||||
if (initComplete) {
|
||||
final RetryPolicy retryPolicy = getRetryPolicyFromConfig(method);
|
||||
final class ImmediateRetryPolicyProvider implements RetryPolicy.Provider {
|
||||
@Override
|
||||
public RetryPolicy get() {
|
||||
return retryPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
final HedgingPolicy hedgingPolicy = getHedgingPolicyFromConfig(method);
|
||||
final class ImmediateHedgingPolicyProvider implements HedgingPolicy.Provider {
|
||||
@Override
|
||||
public HedgingPolicy get() {
|
||||
return hedgingPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
verify(
|
||||
retryPolicy.equals(RetryPolicy.DEFAULT) || hedgingPolicy.equals(HedgingPolicy.DEFAULT),
|
||||
"Can not apply both retry and hedging policy for the method '%s'", method);
|
||||
|
||||
callOptions = callOptions
|
||||
.withOption(RETRY_POLICY_KEY, new ImmediateRetryPolicyProvider())
|
||||
.withOption(HEDGING_POLICY_KEY, new ImmediateHedgingPolicyProvider());
|
||||
} else {
|
||||
final class DelayedRetryPolicyProvider implements RetryPolicy.Provider {
|
||||
/**
|
||||
* Returns RetryPolicy.DEFAULT if name resolving is not complete at the moment the method
|
||||
* is invoked, otherwise returns the RetryPolicy computed from service config.
|
||||
*
|
||||
* <p>Note that this method is used no more than once for each call.
|
||||
*/
|
||||
@Override
|
||||
public RetryPolicy get() {
|
||||
if (!initComplete) {
|
||||
return RetryPolicy.DEFAULT;
|
||||
}
|
||||
return getRetryPolicyFromConfig(method);
|
||||
}
|
||||
}
|
||||
|
||||
final class DelayedHedgingPolicyProvider implements HedgingPolicy.Provider {
|
||||
/**
|
||||
* Returns HedgingPolicy.DEFAULT if name resolving is not complete at the moment the
|
||||
* method is invoked, otherwise returns the HedgingPolicy computed from service config.
|
||||
*
|
||||
* <p>Note that this method is used no more than once for each call.
|
||||
*/
|
||||
@Override
|
||||
public HedgingPolicy get() {
|
||||
if (!initComplete) {
|
||||
return HedgingPolicy.DEFAULT;
|
||||
}
|
||||
HedgingPolicy hedgingPolicy = getHedgingPolicyFromConfig(method);
|
||||
verify(
|
||||
hedgingPolicy.equals(HedgingPolicy.DEFAULT)
|
||||
|| getRetryPolicyFromConfig(method).equals(RetryPolicy.DEFAULT),
|
||||
"Can not apply both retry and hedging policy for the method '%s'", method);
|
||||
return hedgingPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
callOptions = callOptions
|
||||
.withOption(RETRY_POLICY_KEY, new DelayedRetryPolicyProvider())
|
||||
.withOption(HEDGING_POLICY_KEY, new DelayedHedgingPolicyProvider());
|
||||
}
|
||||
}
|
||||
|
||||
MethodInfo info = getMethodInfo(method);
|
||||
if (info == null) {
|
||||
return next.newCall(method, callOptions);
|
||||
}
|
||||
|
||||
if (info.timeoutNanos != null) {
|
||||
Deadline newDeadline = Deadline.after(info.timeoutNanos, TimeUnit.NANOSECONDS);
|
||||
Deadline existingDeadline = callOptions.getDeadline();
|
||||
// If the new deadline is sooner than the existing deadline, swap them.
|
||||
if (existingDeadline == null || newDeadline.compareTo(existingDeadline) < 0) {
|
||||
callOptions = callOptions.withDeadline(newDeadline);
|
||||
}
|
||||
}
|
||||
if (info.waitForReady != null) {
|
||||
callOptions =
|
||||
info.waitForReady ? callOptions.withWaitForReady() : callOptions.withoutWaitForReady();
|
||||
}
|
||||
if (info.maxInboundMessageSize != null) {
|
||||
Integer existingLimit = callOptions.getMaxInboundMessageSize();
|
||||
if (existingLimit != null) {
|
||||
callOptions = callOptions.withMaxInboundMessageSize(
|
||||
Math.min(existingLimit, info.maxInboundMessageSize));
|
||||
} else {
|
||||
callOptions = callOptions.withMaxInboundMessageSize(info.maxInboundMessageSize);
|
||||
}
|
||||
}
|
||||
if (info.maxOutboundMessageSize != null) {
|
||||
Integer existingLimit = callOptions.getMaxOutboundMessageSize();
|
||||
if (existingLimit != null) {
|
||||
callOptions = callOptions.withMaxOutboundMessageSize(
|
||||
Math.min(existingLimit, info.maxOutboundMessageSize));
|
||||
} else {
|
||||
callOptions = callOptions.withMaxOutboundMessageSize(info.maxOutboundMessageSize);
|
||||
}
|
||||
}
|
||||
|
||||
return next.newCall(method, callOptions);
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
private MethodInfo getMethodInfo(MethodDescriptor<?, ?> method) {
|
||||
ManagedChannelServiceConfig2 mcsc = managedChannelServiceConfig.get();
|
||||
MethodInfo info = null;
|
||||
if (mcsc != null) {
|
||||
info = mcsc.getServiceMethodMap().get(method.getFullMethodName());
|
||||
}
|
||||
if (info == null && mcsc != null) {
|
||||
String serviceName = method.getServiceName();
|
||||
info = mcsc.getServiceMap().get(serviceName);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
RetryPolicy getRetryPolicyFromConfig(MethodDescriptor<?, ?> method) {
|
||||
MethodInfo info = getMethodInfo(method);
|
||||
return info == null ? RetryPolicy.DEFAULT : info.retryPolicy;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
HedgingPolicy getHedgingPolicyFromConfig(MethodDescriptor<?, ?> method) {
|
||||
MethodInfo info = getMethodInfo(method);
|
||||
return info == null ? HedgingPolicy.DEFAULT : info.hedgingPolicy;
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ import static org.junit.Assert.assertNotNull;
|
|||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
|
@ -481,19 +480,6 @@ public class AbstractManagedChannelImplBuilderTest {
|
|||
assertThat(builder.lookUpServiceConfig).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableServiceConfigErrorHandling() {
|
||||
String propertyValue = System.getProperty(
|
||||
AbstractManagedChannelImplBuilder.ENABLE_SERVICE_CONFIG_ERROR_HANDLING_PROPERTY);
|
||||
assumeTrue(propertyValue == null);
|
||||
|
||||
Builder builder = new Builder("target");
|
||||
assertThat(builder.enableServiceConfigErrorHandling).isFalse();
|
||||
|
||||
builder.enableServiceConfigErrorHandling();
|
||||
assertThat(builder.enableServiceConfigErrorHandling).isTrue();
|
||||
}
|
||||
|
||||
static class Builder extends AbstractManagedChannelImplBuilder<Builder> {
|
||||
Builder(String target) {
|
||||
super(target);
|
||||
|
|
|
|||
|
|
@ -51,23 +51,25 @@ import io.grpc.LoadBalancer.SubchannelStateListener;
|
|||
import io.grpc.LoadBalancerProvider;
|
||||
import io.grpc.LoadBalancerRegistry;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.NameResolver.ConfigOrError;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.SynchronizationContext;
|
||||
import io.grpc.grpclb.GrpclbLoadBalancerProvider;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory.AutoConfiguredLoadBalancer;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory.PolicyException;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory.PolicySelection;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory.ResolvedPolicySelection;
|
||||
import io.grpc.util.ForwardingLoadBalancerHelper;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
@ -78,8 +80,8 @@ import org.mockito.ArgumentCaptor;
|
|||
/**
|
||||
* Unit tests for {@link AutoConfiguredLoadBalancerFactory}.
|
||||
*/
|
||||
@Deprecated // to be migrate to AutoConfiguredLoadBalancerFactoryTest2
|
||||
@RunWith(JUnit4.class)
|
||||
// TODO(creamsoup) remove backward compatible check when fully migrated
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AutoConfiguredLoadBalancerFactoryTest {
|
||||
private static final LoadBalancerRegistry defaultRegistry =
|
||||
|
|
@ -90,12 +92,18 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
private final ChannelLogger channelLogger = mock(ChannelLogger.class);
|
||||
private final LoadBalancer testLbBalancer = mock(LoadBalancer.class);
|
||||
private final LoadBalancer testLbBalancer2 = mock(LoadBalancer.class);
|
||||
private final LoadBalancerProvider testLbBalancerProvider =
|
||||
mock(LoadBalancerProvider.class,
|
||||
delegatesTo(new FakeLoadBalancerProvider("test_lb", testLbBalancer)));
|
||||
private final LoadBalancerProvider testLbBalancerProvider2 =
|
||||
mock(LoadBalancerProvider.class,
|
||||
delegatesTo(new FakeLoadBalancerProvider("test_lb2", testLbBalancer2)));
|
||||
private final AtomicReference<ConfigOrError> nextParsedConfigOrError =
|
||||
new AtomicReference<>(ConfigOrError.fromConfig("default"));
|
||||
private final AtomicReference<ConfigOrError> nextParsedConfigOrError2 =
|
||||
new AtomicReference<>(ConfigOrError.fromConfig("default2"));
|
||||
private final FakeLoadBalancerProvider testLbBalancerProvider =
|
||||
mock(FakeLoadBalancerProvider.class,
|
||||
delegatesTo(
|
||||
new FakeLoadBalancerProvider("test_lb", testLbBalancer, nextParsedConfigOrError)));
|
||||
private final FakeLoadBalancerProvider testLbBalancerProvider2 =
|
||||
mock(FakeLoadBalancerProvider.class,
|
||||
delegatesTo(
|
||||
new FakeLoadBalancerProvider("test_lb2", testLbBalancer2, nextParsedConfigOrError2)));
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
|
@ -190,6 +198,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.setLoadBalancingPolicyConfig(null)
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
|
|
@ -197,13 +206,11 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void handleResolvedAddressGroups_shutsDownOldBalancer() {
|
||||
Map<String, String> serviceConfig = new HashMap<>();
|
||||
serviceConfig.put("loadBalancingPolicy", "round_robin");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
public void handleResolvedAddressGroups_shutsDownOldBalancer() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": { } } ] }");
|
||||
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
Helper helper = new TestHelper() {
|
||||
|
|
@ -232,7 +239,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(serviceConfigAttrs)
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
|
|
@ -242,13 +249,13 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleResolvedAddressGroups_propagateLbConfigToDelegate() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfigs.getConfig()).isNotNull();
|
||||
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
Helper helper = new TestHelper();
|
||||
|
|
@ -257,7 +264,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(serviceConfigAttrs)
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
verify(testLbBalancerProvider).newLoadBalancer(same(helper));
|
||||
|
|
@ -267,22 +274,22 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
ArgumentCaptor.forClass(ResolvedAddresses.class);
|
||||
verify(testLbBalancer).handleResolvedAddresses(resultCaptor.capture());
|
||||
assertThat(resultCaptor.getValue().getAddresses()).containsExactlyElementsIn(servers).inOrder();
|
||||
Attributes actualAttributes = resultCaptor.getValue().getAttributes();
|
||||
assertThat(actualAttributes.get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(Collections.singletonMap("setting1", "high"));
|
||||
assertThat(resultCaptor.getValue().getAttributes().get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(rawServiceConfig);
|
||||
verify(testLbBalancer, atLeast(0)).canHandleEmptyAddressListFromNameResolution();
|
||||
ArgumentCaptor<Map<String, ?>> lbConfigCaptor = ArgumentCaptor.forClass(Map.class);
|
||||
verify(testLbBalancerProvider).parseLoadBalancingPolicyConfig(lbConfigCaptor.capture());
|
||||
assertThat(lbConfigCaptor.getValue()).containsExactly("setting1", "high");
|
||||
verifyNoMoreInteractions(testLbBalancer);
|
||||
|
||||
serviceConfig =
|
||||
rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"low\" } } ] }");
|
||||
serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
|
||||
handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(serviceConfigAttrs)
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
resultCaptor =
|
||||
|
|
@ -290,10 +297,11 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
verify(testLbBalancer, times(2)).handleResolvedAddresses(resultCaptor.capture());
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
assertThat(resultCaptor.getValue().getAddresses()).containsExactlyElementsIn(servers).inOrder();
|
||||
actualAttributes = resultCaptor.getValue().getAttributes();
|
||||
// But the balancer config is changed.
|
||||
assertThat(actualAttributes.get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(Collections.singletonMap("setting1", "low"));
|
||||
assertThat(resultCaptor.getValue().getAttributes().get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(rawServiceConfig);
|
||||
verify(testLbBalancerProvider, times(2))
|
||||
.parseLoadBalancingPolicyConfig(lbConfigCaptor.capture());
|
||||
assertThat(lbConfigCaptor.getValue()).containsExactly("setting1", "low");
|
||||
// Service config didn't change policy, thus the delegateLb is not swapped
|
||||
verifyNoMoreInteractions(testLbBalancer);
|
||||
verify(testLbBalancerProvider).newLoadBalancer(any(Helper.class));
|
||||
|
|
@ -304,7 +312,9 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
// This case only happens when grpclb is missing. We will use a local registry
|
||||
LoadBalancerRegistry registry = new LoadBalancerRegistry();
|
||||
registry.register(new PickFirstLoadBalancerProvider());
|
||||
registry.register(new FakeLoadBalancerProvider("round_robin", testLbBalancer));
|
||||
registry.register(
|
||||
new FakeLoadBalancerProvider(
|
||||
"round_robin", testLbBalancer, /* nextParsedLbPolicyConfig= */ null));
|
||||
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
|
|
@ -339,11 +349,11 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(serviceConfig, helper.getChannelLogger());
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(Collections.<EquivalentAddressGroup>emptyList())
|
||||
.setAttributes(Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig).build())
|
||||
.setLoadBalancingPolicyConfig(lbConfig.getConfig())
|
||||
.build());
|
||||
|
||||
assertThat(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).isFalse();
|
||||
|
|
@ -358,13 +368,14 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(helper);
|
||||
|
||||
Map<String, ?> serviceConfig =
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb2\": { \"setting1\": \"high\" } } ] }");
|
||||
ConfigOrError lbConfigs =
|
||||
lbf.parseLoadBalancerPolicy(rawServiceConfig, helper.getChannelLogger());
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(Collections.<EquivalentAddressGroup>emptyList())
|
||||
.setAttributes(Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig).build())
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
|
|
@ -374,26 +385,25 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
ArgumentCaptor.forClass(ResolvedAddresses.class);
|
||||
verify(testLbBalancer2).handleResolvedAddresses(resultCaptor.capture());
|
||||
assertThat(resultCaptor.getValue().getAddresses()).isEmpty();
|
||||
Attributes actualAttributes = resultCaptor.getValue().getAttributes();
|
||||
|
||||
Map<String, ?> lbConfig = actualAttributes.get(LoadBalancer.ATTR_LOAD_BALANCING_CONFIG);
|
||||
assertThat(lbConfig).isEqualTo(Collections.<String, Object>singletonMap("setting1", "high"));
|
||||
assertThat(actualAttributes.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG))
|
||||
.isSameInstanceAs(serviceConfig);
|
||||
assertThat(resultCaptor.getValue().getLoadBalancingPolicyConfig())
|
||||
.isEqualTo(nextParsedConfigOrError2.get().getConfig());
|
||||
assertThat(resultCaptor.getValue().getAttributes().get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(rawServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_noBalancerAddresses_noServiceConfig_pickFirst()
|
||||
throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig = null;
|
||||
PolicySelection policySelection = null;
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.provider).isInstanceOf(PickFirstLoadBalancerProvider.class);
|
||||
assertThat(selection.policySelection.provider)
|
||||
.isInstanceOf(PickFirstLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.config).isNull();
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
|
|
@ -402,39 +412,43 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = new AutoConfiguredLoadBalancerFactory("test_lb")
|
||||
.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig = null;
|
||||
PolicySelection policySelection = null;
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.provider).isSameInstanceAs(testLbBalancerProvider);
|
||||
assertThat(selection.policySelection.provider).isSameInstanceAs(testLbBalancerProvider);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.config).isNull();
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_oneBalancer_noServiceConfig_grpclb() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig = null;
|
||||
PolicySelection policySelection = null;
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.policySelection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.config).isNull();
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbPolicy() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, String> serviceConfig = new HashMap<>();
|
||||
serviceConfig.put("loadBalancingPolicy", "round_robin");
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingPolicy\": \"round_robin\"}");
|
||||
|
||||
ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfig.getConfig()).isNotNull();
|
||||
PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
new EquivalentAddressGroup(
|
||||
|
|
@ -443,21 +457,23 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){}));
|
||||
List<EquivalentAddressGroup> backends = Arrays.asList(servers.get(1));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.provider.getClass().getName()).isEqualTo(
|
||||
assertThat(selection.policySelection.provider.getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
assertThat(selection.serverList).isEqualTo(backends);
|
||||
assertThat(selection.config).isEqualTo(Collections.<String, Object>emptyMap());
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbConfig() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [{\"round_robin\": {}}]}");
|
||||
|
||||
ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfig.getConfig()).isNotNull();
|
||||
PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
new EquivalentAddressGroup(
|
||||
|
|
@ -466,55 +482,54 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){}));
|
||||
List<EquivalentAddressGroup> backends = Arrays.asList(servers.get(1));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.provider.getClass().getName()).isEqualTo(
|
||||
assertThat(selection.policySelection.provider.getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
assertThat(selection.serverList).isEqualTo(backends);
|
||||
assertThat(selection.config).isEqualTo(Collections.<String, Object>emptyMap());
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_grpclbConfigPropagated() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig =
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"grpclb\": {\"childPolicy\": [ {\"pick_first\": {} } ] } }"
|
||||
+ "] }");
|
||||
+ "{\"grpclb\": {\"childPolicy\": [ {\"pick_first\": {} } ] } }"
|
||||
+ "] }");
|
||||
ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfig.getConfig()).isNotNull();
|
||||
PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
|
||||
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.policySelection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.config).isEqualTo(
|
||||
parseConfig("{\"childPolicy\": [ {\"pick_first\": {} } ] }"));
|
||||
assertThat(selection.policySelection.config)
|
||||
.isEqualTo(((PolicySelection) lbConfig.getConfig()).config);
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_policyUnavailButGrpclbAddressPresent() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"unavail\": {} }"
|
||||
+ "] }");
|
||||
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, null);
|
||||
|
||||
assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.policySelection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.config).isNull();
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
|
|
@ -524,34 +539,32 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
LoadBalancerRegistry registry = new LoadBalancerRegistry();
|
||||
registry.register(new PickFirstLoadBalancerProvider());
|
||||
LoadBalancerProvider fakeRoundRobinProvider =
|
||||
new FakeLoadBalancerProvider("round_robin", testLbBalancer);
|
||||
new FakeLoadBalancerProvider("round_robin", testLbBalancer, null);
|
||||
registry.register(fakeRoundRobinProvider);
|
||||
AutoConfiguredLoadBalancer lb = new AutoConfiguredLoadBalancerFactory(
|
||||
registry, GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"grpclb\": {} } ] }");
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()),
|
||||
new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, null);
|
||||
|
||||
assertThat(selection.provider).isSameInstanceAs(fakeRoundRobinProvider);
|
||||
assertThat(selection.config).isNull();
|
||||
assertThat(selection.policySelection.provider).isSameInstanceAs(fakeRoundRobinProvider);
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.ERROR),
|
||||
startsWith("Found balancer addresses but grpclb runtime is missing"));
|
||||
|
||||
// Called for the second time, the warning is only logged once
|
||||
selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
selection = lb.resolveLoadBalancerProvider(servers, null);
|
||||
|
||||
assertThat(selection.provider).isSameInstanceAs(fakeRoundRobinProvider);
|
||||
assertThat(selection.policySelection.provider).isSameInstanceAs(fakeRoundRobinProvider);
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
// Balancer addresses are filtered out in the server list passed to round_robin
|
||||
assertThat(selection.serverList).containsExactly(servers.get(1));
|
||||
assertThat(selection.config).isNull();
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
verifyNoMoreInteractions(channelLogger);;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -559,18 +572,16 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
throws Exception {
|
||||
LoadBalancerRegistry registry = new LoadBalancerRegistry();
|
||||
registry.register(new PickFirstLoadBalancerProvider());
|
||||
registry.register(new FakeLoadBalancerProvider("round_robin", testLbBalancer));
|
||||
registry.register(new FakeLoadBalancerProvider("round_robin", testLbBalancer, null));
|
||||
AutoConfiguredLoadBalancer lb = new AutoConfiguredLoadBalancerFactory(
|
||||
registry, GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"grpclb\": {} } ] }");
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
try {
|
||||
lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
lb.resolveLoadBalancerProvider(servers, null);
|
||||
fail("Should throw");
|
||||
} catch (PolicyException e) {
|
||||
assertThat(e)
|
||||
|
|
@ -579,105 +590,26 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbPolicyOverridesDefault() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, String> serviceConfig = new HashMap<>();
|
||||
serviceConfig.put("loadBalancingPolicy", "round_robin");
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
|
||||
assertThat(selection.provider.getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
assertThat(selection.config).isEqualTo(Collections.emptyMap());
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbConfigOverridesDefault() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {\"setting1\": \"high\"} } ] }");
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
|
||||
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfigs.getConfig()).isNotNull();
|
||||
PolicySelection policySelection = (PolicySelection) lbConfigs.getConfig();
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
|
||||
assertThat(selection.provider.getClass().getName()).isEqualTo(
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.policySelection.provider.getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.config).isEqualTo(Collections.singletonMap("setting1", "high"));
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbPolicyFailsOnUnknown() {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, String> serviceConfig = new HashMap<>();
|
||||
serviceConfig.put("loadBalancingPolicy", "MAGIC_BALANCER");
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
try {
|
||||
lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
fail();
|
||||
} catch (PolicyException e) {
|
||||
assertThat(e).hasMessageThat().isEqualTo(
|
||||
"None of [magic_balancer] specified by Service Config are available.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbConfigFailsOnUnknown() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"magic_balancer\": {} } ] }");
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
try {
|
||||
lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
fail();
|
||||
} catch (PolicyException e) {
|
||||
assertThat(e).hasMessageThat().isEqualTo(
|
||||
"None of [magic_balancer] specified by Service Config are available.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbConfigSkipUnknown() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": [ {\"magic_balancer\": {} }, {\"round_robin\": {} } ] }");
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
|
||||
assertThat(selection.provider.getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.config).isEqualTo(Collections.emptyMap());
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.DEBUG),
|
||||
eq("{0} specified by Service Config are not available"),
|
||||
eq(new LinkedHashSet<>(Arrays.asList("magic_balancer"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigHasZeroLbConfig() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(
|
||||
servers, Collections.<String, Object>emptyMap());
|
||||
|
||||
assertThat(selection.provider).isInstanceOf(PickFirstLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void channelTracing_lbPolicyChanged() {
|
||||
public void channelTracing_lbPolicyChanged() throws Exception {
|
||||
final FakeClock clock = new FakeClock();
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
|
|
@ -734,38 +666,44 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
|
||||
Map<String, String> serviceConfig = new HashMap<>();
|
||||
serviceConfig.put("loadBalancingPolicy", "round_robin");
|
||||
ConfigOrError testLbParsedConfig = ConfigOrError.fromConfig("foo");
|
||||
nextParsedConfigOrError.set(testLbParsedConfig);
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { } } ] }");
|
||||
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig).build())
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.INFO),
|
||||
eq("Load balancer changed from {0} to {1}"),
|
||||
eq("PickFirstLoadBalancer"), eq("RoundRobinLoadBalancer"));
|
||||
eq("PickFirstLoadBalancer"),
|
||||
eq(testLbBalancer.getClass().getSimpleName()));
|
||||
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.DEBUG),
|
||||
eq("Load-balancing config: {0}"),
|
||||
eq(Collections.emptyMap()));
|
||||
eq(testLbParsedConfig.getConfig()));
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
|
||||
serviceConfig.put("loadBalancingPolicy", "round_robin");
|
||||
testLbParsedConfig = ConfigOrError.fromConfig("bar");
|
||||
nextParsedConfigOrError.set(testLbParsedConfig);
|
||||
serviceConfig = parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { } } ] }");
|
||||
lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig).build())
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
verify(channelLogger, times(2)).log(
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.DEBUG),
|
||||
eq("Load-balancing config: {0}"),
|
||||
eq(Collections.emptyMap()));
|
||||
eq(testLbParsedConfig.getConfig()));
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
|
||||
servers = Collections.singletonList(new EquivalentAddressGroup(
|
||||
|
|
@ -781,11 +719,122 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.INFO),
|
||||
eq("Load balancer changed from {0} to {1}"),
|
||||
eq("RoundRobinLoadBalancer"), eq("GrpclbLoadBalancer"));
|
||||
eq(testLbBalancer.getClass().getSimpleName()), eq("GrpclbLoadBalancer"));
|
||||
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_failedOnUnknown() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"magic_balancer\": {} } ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed.getError()).isNotNull();
|
||||
assertThat(parsed.getError().getDescription())
|
||||
.isEqualTo("None of [magic_balancer] specified by Service Config are available.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerPolicy_failedOnUnknown() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingPolicy\": \"magic_balancer\"}");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed.getError()).isNotNull();
|
||||
assertThat(parsed.getError().getDescription())
|
||||
.isEqualTo("None of [magic_balancer] specified by Service Config are available.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_multipleValidPolicies() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"round_robin\": {}},"
|
||||
+ "{\"test_lb\": {} } ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getError()).isNull();
|
||||
assertThat(parsed.getConfig()).isInstanceOf(PolicySelection.class);
|
||||
assertThat(((PolicySelection) parsed.getConfig()).provider.getClass().getName())
|
||||
.isEqualTo("io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_policyShouldBeIgnoredIfConfigExists() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": [{\"round_robin\": {} } ],"
|
||||
+ "\"loadBalancingPolicy\": \"pick_first\" }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getError()).isNull();
|
||||
assertThat(parsed.getConfig()).isInstanceOf(PolicySelection.class);
|
||||
assertThat(((PolicySelection) parsed.getConfig()).provider.getClass().getName())
|
||||
.isEqualTo("io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_policyShouldBeIgnoredEvenIfUnknownPolicyExists()
|
||||
throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": [{\"magic_balancer\": {} } ],"
|
||||
+ "\"loadBalancingPolicy\": \"round_robin\" }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed.getError()).isNotNull();
|
||||
assertThat(parsed.getError().getDescription())
|
||||
.isEqualTo("None of [magic_balancer] specified by Service Config are available.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parseLoadBalancerConfig_firstInvalidPolicy() throws Exception {
|
||||
when(testLbBalancerProvider.parseLoadBalancingPolicyConfig(any(Map.class)))
|
||||
.thenReturn(ConfigOrError.fromError(Status.UNKNOWN));
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"test_lb\": {}},"
|
||||
+ "{\"round_robin\": {} } ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getConfig()).isNull();
|
||||
assertThat(parsed.getError()).isEqualTo(Status.UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parseLoadBalancerConfig_firstValidSecondInvalidPolicy() throws Exception {
|
||||
when(testLbBalancerProvider.parseLoadBalancingPolicyConfig(any(Map.class)))
|
||||
.thenReturn(ConfigOrError.fromError(Status.UNKNOWN));
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"round_robin\": {}},"
|
||||
+ "{\"test_lb\": {} } ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getConfig()).isNotNull();
|
||||
assertThat(((PolicySelection) parsed.getConfig()).config).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_someProvidesAreNotAvailable() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ "
|
||||
+ "{\"magic_balancer\": {} },"
|
||||
+ "{\"round_robin\": {}} ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getConfig()).isNotNull();
|
||||
assertThat(((PolicySelection) parsed.getConfig()).config).isNotNull();
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.DEBUG),
|
||||
eq("{0} specified by Service Config are not available"),
|
||||
eq(new ArrayList<>(Collections.singletonList("magic_balancer"))));
|
||||
}
|
||||
|
||||
|
||||
public static class ForwardingLoadBalancer extends LoadBalancer {
|
||||
private final LoadBalancer delegate;
|
||||
|
||||
|
|
@ -886,13 +935,18 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class FakeLoadBalancerProvider extends LoadBalancerProvider {
|
||||
private static class FakeLoadBalancerProvider extends LoadBalancerProvider {
|
||||
private final String policyName;
|
||||
private final LoadBalancer balancer;
|
||||
private final AtomicReference<ConfigOrError> nextParsedLbPolicyConfig;
|
||||
|
||||
FakeLoadBalancerProvider(String policyName, LoadBalancer balancer) {
|
||||
FakeLoadBalancerProvider(
|
||||
String policyName,
|
||||
LoadBalancer balancer,
|
||||
AtomicReference<ConfigOrError> nextParsedLbPolicyConfig) {
|
||||
this.policyName = policyName;
|
||||
this.balancer = balancer;
|
||||
this.nextParsedLbPolicyConfig = nextParsedLbPolicyConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -914,5 +968,14 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
public LoadBalancer newLoadBalancer(Helper helper) {
|
||||
return balancer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigOrError parseLoadBalancingPolicyConfig(
|
||||
Map<String, ?> rawLoadBalancingPolicyConfig) {
|
||||
if (nextParsedLbPolicyConfig == null) {
|
||||
return super.parseLoadBalancingPolicyConfig(rawLoadBalancingPolicyConfig);
|
||||
}
|
||||
return nextParsedLbPolicyConfig.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,981 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static io.grpc.LoadBalancer.ATTR_LOAD_BALANCING_CONFIG;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.AdditionalAnswers.delegatesTo;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.ArgumentMatchers.startsWith;
|
||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.grpc.Attributes;
|
||||
import io.grpc.ChannelLogger;
|
||||
import io.grpc.ChannelLogger.ChannelLogLevel;
|
||||
import io.grpc.ConnectivityState;
|
||||
import io.grpc.ConnectivityStateInfo;
|
||||
import io.grpc.EquivalentAddressGroup;
|
||||
import io.grpc.LoadBalancer;
|
||||
import io.grpc.LoadBalancer.CreateSubchannelArgs;
|
||||
import io.grpc.LoadBalancer.Helper;
|
||||
import io.grpc.LoadBalancer.ResolvedAddresses;
|
||||
import io.grpc.LoadBalancer.Subchannel;
|
||||
import io.grpc.LoadBalancer.SubchannelPicker;
|
||||
import io.grpc.LoadBalancer.SubchannelStateListener;
|
||||
import io.grpc.LoadBalancerProvider;
|
||||
import io.grpc.LoadBalancerRegistry;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.NameResolver.ConfigOrError;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.SynchronizationContext;
|
||||
import io.grpc.grpclb.GrpclbLoadBalancerProvider;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory2.AutoConfiguredLoadBalancer;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory2.PolicyException;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory2.PolicySelection;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory2.ResolvedPolicySelection;
|
||||
import io.grpc.util.ForwardingLoadBalancerHelper;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AutoConfiguredLoadBalancerFactory}.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
// TODO(creamsoup) remove backward compatible check when fully migrated
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AutoConfiguredLoadBalancerFactoryTest2 {
|
||||
private static final LoadBalancerRegistry defaultRegistry =
|
||||
LoadBalancerRegistry.getDefaultRegistry();
|
||||
private final AutoConfiguredLoadBalancerFactory2 lbf =
|
||||
new AutoConfiguredLoadBalancerFactory2(GrpcUtil.DEFAULT_LB_POLICY);
|
||||
|
||||
private final ChannelLogger channelLogger = mock(ChannelLogger.class);
|
||||
private final LoadBalancer testLbBalancer = mock(LoadBalancer.class);
|
||||
private final LoadBalancer testLbBalancer2 = mock(LoadBalancer.class);
|
||||
private final AtomicReference<ConfigOrError> nextParsedConfigOrError =
|
||||
new AtomicReference<>(ConfigOrError.fromConfig("default"));
|
||||
private final AtomicReference<ConfigOrError> nextParsedConfigOrError2 =
|
||||
new AtomicReference<>(ConfigOrError.fromConfig("default2"));
|
||||
private final FakeLoadBalancerProvider testLbBalancerProvider =
|
||||
mock(FakeLoadBalancerProvider.class,
|
||||
delegatesTo(
|
||||
new FakeLoadBalancerProvider("test_lb", testLbBalancer, nextParsedConfigOrError)));
|
||||
private final FakeLoadBalancerProvider testLbBalancerProvider2 =
|
||||
mock(FakeLoadBalancerProvider.class,
|
||||
delegatesTo(
|
||||
new FakeLoadBalancerProvider("test_lb2", testLbBalancer2, nextParsedConfigOrError2)));
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
when(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).thenCallRealMethod();
|
||||
assertThat(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).isFalse();
|
||||
when(testLbBalancer2.canHandleEmptyAddressListFromNameResolution()).thenReturn(true);
|
||||
defaultRegistry.register(testLbBalancerProvider);
|
||||
defaultRegistry.register(testLbBalancerProvider2);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
defaultRegistry.deregister(testLbBalancerProvider);
|
||||
defaultRegistry.deregister(testLbBalancerProvider2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newLoadBalancer_isAuto() {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
|
||||
assertThat(lb).isInstanceOf(AutoConfiguredLoadBalancer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultIsPickFirst() {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
|
||||
assertThat(lb.getDelegateProvider()).isInstanceOf(PickFirstLoadBalancerProvider.class);
|
||||
assertThat(lb.getDelegate().getClass().getName()).contains("PickFirst");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultIsConfigurable() {
|
||||
AutoConfiguredLoadBalancer lb = new AutoConfiguredLoadBalancerFactory2("test_lb")
|
||||
.newLoadBalancer(new TestHelper());
|
||||
|
||||
assertThat(lb.getDelegateProvider()).isSameInstanceAs(testLbBalancerProvider);
|
||||
assertThat(lb.getDelegate()).isSameInstanceAs(testLbBalancer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void forwardsCalls() {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
|
||||
final AtomicInteger calls = new AtomicInteger();
|
||||
TestLoadBalancer testlb = new TestLoadBalancer() {
|
||||
|
||||
@Override
|
||||
public void handleNameResolutionError(Status error) {
|
||||
calls.getAndSet(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSubchannelState(Subchannel subchannel, ConnectivityStateInfo stateInfo) {
|
||||
calls.getAndSet(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
calls.getAndSet(3);
|
||||
}
|
||||
};
|
||||
|
||||
lb.setDelegate(testlb);
|
||||
|
||||
lb.handleNameResolutionError(Status.RESOURCE_EXHAUSTED);
|
||||
assertThat(calls.getAndSet(0)).isEqualTo(1);
|
||||
|
||||
lb.handleSubchannelState(null, null);
|
||||
assertThat(calls.getAndSet(0)).isEqualTo(2);
|
||||
|
||||
lb.shutdown();
|
||||
assertThat(calls.getAndSet(0)).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleResolvedAddressGroups_keepOldBalancer() {
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
Helper helper = new TestHelper() {
|
||||
@Override
|
||||
public Subchannel createSubchannel(CreateSubchannelArgs args) {
|
||||
assertThat(args.getAddresses()).isEqualTo(servers);
|
||||
return new TestSubchannel(args);
|
||||
}
|
||||
};
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(helper);
|
||||
LoadBalancer oldDelegate = lb.getDelegate();
|
||||
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.setLoadBalancingPolicyConfig(null)
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
assertThat(lb.getDelegate()).isSameInstanceAs(oldDelegate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleResolvedAddressGroups_shutsDownOldBalancer() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": { } } ] }");
|
||||
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
Helper helper = new TestHelper() {
|
||||
@Override
|
||||
public Subchannel createSubchannel(CreateSubchannelArgs args) {
|
||||
assertThat(args.getAddresses()).isEqualTo(servers);
|
||||
return new TestSubchannel(args);
|
||||
}
|
||||
};
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(helper);
|
||||
final AtomicBoolean shutdown = new AtomicBoolean();
|
||||
TestLoadBalancer testlb = new TestLoadBalancer() {
|
||||
|
||||
@Override
|
||||
public void handleNameResolutionError(Status error) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
shutdown.set(true);
|
||||
}
|
||||
};
|
||||
lb.setDelegate(testlb);
|
||||
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
assertThat(lb.getDelegateProvider().getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
assertTrue(shutdown.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleResolvedAddressGroups_propagateLbConfigToDelegate() throws Exception {
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfigs.getConfig()).isNotNull();
|
||||
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(helper);
|
||||
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
verify(testLbBalancerProvider).newLoadBalancer(same(helper));
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
assertThat(lb.getDelegate()).isSameInstanceAs(testLbBalancer);
|
||||
ArgumentCaptor<ResolvedAddresses> resultCaptor =
|
||||
ArgumentCaptor.forClass(ResolvedAddresses.class);
|
||||
verify(testLbBalancer).handleResolvedAddresses(resultCaptor.capture());
|
||||
assertThat(resultCaptor.getValue().getAddresses()).containsExactlyElementsIn(servers).inOrder();
|
||||
assertThat(resultCaptor.getValue().getAttributes().get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(rawServiceConfig);
|
||||
verify(testLbBalancer, atLeast(0)).canHandleEmptyAddressListFromNameResolution();
|
||||
ArgumentCaptor<Map<String, ?>> lbConfigCaptor = ArgumentCaptor.forClass(Map.class);
|
||||
verify(testLbBalancerProvider).parseLoadBalancingPolicyConfig(lbConfigCaptor.capture());
|
||||
assertThat(lbConfigCaptor.getValue()).containsExactly("setting1", "high");
|
||||
verifyNoMoreInteractions(testLbBalancer);
|
||||
|
||||
rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"low\" } } ] }");
|
||||
lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
|
||||
handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
resultCaptor =
|
||||
ArgumentCaptor.forClass(ResolvedAddresses.class);
|
||||
verify(testLbBalancer, times(2)).handleResolvedAddresses(resultCaptor.capture());
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
assertThat(resultCaptor.getValue().getAddresses()).containsExactlyElementsIn(servers).inOrder();
|
||||
assertThat(resultCaptor.getValue().getAttributes().get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(rawServiceConfig);
|
||||
verify(testLbBalancerProvider, times(2))
|
||||
.parseLoadBalancingPolicyConfig(lbConfigCaptor.capture());
|
||||
assertThat(lbConfigCaptor.getValue()).containsExactly("setting1", "low");
|
||||
// Service config didn't change policy, thus the delegateLb is not swapped
|
||||
verifyNoMoreInteractions(testLbBalancer);
|
||||
verify(testLbBalancerProvider).newLoadBalancer(any(Helper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleResolvedAddressGroups_propagateOnlyBackendAddrsToDelegate() throws Exception {
|
||||
// This case only happens when grpclb is missing. We will use a local registry
|
||||
LoadBalancerRegistry registry = new LoadBalancerRegistry();
|
||||
registry.register(new PickFirstLoadBalancerProvider());
|
||||
registry.register(
|
||||
new FakeLoadBalancerProvider(
|
||||
"round_robin", testLbBalancer, /* nextParsedLbPolicyConfig= */ null));
|
||||
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
new EquivalentAddressGroup(new SocketAddress(){}),
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb = new AutoConfiguredLoadBalancerFactory2(
|
||||
registry, GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(helper);
|
||||
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
assertThat(lb.getDelegate()).isSameInstanceAs(testLbBalancer);
|
||||
verify(testLbBalancer).handleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(Collections.singletonList(servers.get(0)))
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleResolvedAddressGroups_delegateDoNotAcceptEmptyAddressList_nothing()
|
||||
throws Exception {
|
||||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(helper);
|
||||
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(serviceConfig, helper.getChannelLogger());
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(Collections.<EquivalentAddressGroup>emptyList())
|
||||
.setLoadBalancingPolicyConfig(lbConfig.getConfig())
|
||||
.build());
|
||||
|
||||
assertThat(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).isFalse();
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.UNAVAILABLE);
|
||||
assertThat(handleResult.getDescription()).startsWith("NameResolver returned no usable address");
|
||||
assertThat(lb.getDelegate()).isSameInstanceAs(testLbBalancer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleResolvedAddressGroups_delegateAcceptsEmptyAddressList()
|
||||
throws Exception {
|
||||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(helper);
|
||||
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb2\": { \"setting1\": \"high\" } } ] }");
|
||||
ConfigOrError lbConfigs =
|
||||
lbf.parseLoadBalancerPolicy(rawServiceConfig, helper.getChannelLogger());
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(Collections.<EquivalentAddressGroup>emptyList())
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
assertThat(lb.getDelegate()).isSameInstanceAs(testLbBalancer2);
|
||||
assertThat(testLbBalancer2.canHandleEmptyAddressListFromNameResolution()).isTrue();
|
||||
ArgumentCaptor<ResolvedAddresses> resultCaptor =
|
||||
ArgumentCaptor.forClass(ResolvedAddresses.class);
|
||||
verify(testLbBalancer2).handleResolvedAddresses(resultCaptor.capture());
|
||||
assertThat(resultCaptor.getValue().getAddresses()).isEmpty();
|
||||
assertThat(resultCaptor.getValue().getLoadBalancingPolicyConfig())
|
||||
.isEqualTo(nextParsedConfigOrError2.get().getConfig());
|
||||
assertThat(resultCaptor.getValue().getAttributes().get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(rawServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_noBalancerAddresses_noServiceConfig_pickFirst()
|
||||
throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
PolicySelection policySelection = null;
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.policySelection.provider)
|
||||
.isInstanceOf(PickFirstLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_noBalancerAddresses_noServiceConfig_customDefault()
|
||||
throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = new AutoConfiguredLoadBalancerFactory2("test_lb")
|
||||
.newLoadBalancer(new TestHelper());
|
||||
PolicySelection policySelection = null;
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.policySelection.provider).isSameInstanceAs(testLbBalancerProvider);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_oneBalancer_noServiceConfig_grpclb() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
PolicySelection policySelection = null;
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.policySelection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbPolicy() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingPolicy\": \"round_robin\"}");
|
||||
|
||||
ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfig.getConfig()).isNotNull();
|
||||
PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()),
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){}));
|
||||
List<EquivalentAddressGroup> backends = Arrays.asList(servers.get(1));
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.policySelection.provider.getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
assertThat(selection.serverList).isEqualTo(backends);
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbConfig() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [{\"round_robin\": {}}]}");
|
||||
|
||||
ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfig.getConfig()).isNotNull();
|
||||
PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()),
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){}));
|
||||
List<EquivalentAddressGroup> backends = Arrays.asList(servers.get(1));
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.policySelection.provider.getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
assertThat(selection.serverList).isEqualTo(backends);
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_grpclbConfigPropagated() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"grpclb\": {\"childPolicy\": [ {\"pick_first\": {} } ] } }"
|
||||
+ "] }");
|
||||
ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfig.getConfig()).isNotNull();
|
||||
PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
|
||||
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.policySelection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.policySelection.config)
|
||||
.isEqualTo(((PolicySelection) lbConfig.getConfig()).config);
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_policyUnavailButGrpclbAddressPresent() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, null);
|
||||
|
||||
assertThat(selection.policySelection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
|
||||
assertThat(selection.serverList).isEqualTo(servers);
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_grpclbProviderNotFound_fallbackToRoundRobin()
|
||||
throws Exception {
|
||||
LoadBalancerRegistry registry = new LoadBalancerRegistry();
|
||||
registry.register(new PickFirstLoadBalancerProvider());
|
||||
LoadBalancerProvider fakeRoundRobinProvider =
|
||||
new FakeLoadBalancerProvider("round_robin", testLbBalancer, null);
|
||||
registry.register(fakeRoundRobinProvider);
|
||||
AutoConfiguredLoadBalancer lb = new AutoConfiguredLoadBalancerFactory2(
|
||||
registry, GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(new TestHelper());
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()),
|
||||
new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, null);
|
||||
|
||||
assertThat(selection.policySelection.provider).isSameInstanceAs(fakeRoundRobinProvider);
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.ERROR),
|
||||
startsWith("Found balancer addresses but grpclb runtime is missing"));
|
||||
|
||||
// Called for the second time, the warning is only logged once
|
||||
selection = lb.resolveLoadBalancerProvider(servers, null);
|
||||
|
||||
assertThat(selection.policySelection.provider).isSameInstanceAs(fakeRoundRobinProvider);
|
||||
assertThat(selection.policySelection.config).isNull();
|
||||
// Balancer addresses are filtered out in the server list passed to round_robin
|
||||
assertThat(selection.serverList).containsExactly(servers.get(1));
|
||||
verifyNoMoreInteractions(channelLogger);;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_grpclbProviderNotFound_noBackendAddress()
|
||||
throws Exception {
|
||||
LoadBalancerRegistry registry = new LoadBalancerRegistry();
|
||||
registry.register(new PickFirstLoadBalancerProvider());
|
||||
registry.register(new FakeLoadBalancerProvider("round_robin", testLbBalancer, null));
|
||||
AutoConfiguredLoadBalancer lb = new AutoConfiguredLoadBalancerFactory2(
|
||||
registry, GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(new TestHelper());
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
try {
|
||||
lb.resolveLoadBalancerProvider(servers, null);
|
||||
fail("Should throw");
|
||||
} catch (PolicyException e) {
|
||||
assertThat(e)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Received ONLY balancer addresses but grpclb runtime is missing");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_serviceConfigLbConfigOverridesDefault() throws Exception {
|
||||
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
|
||||
Map<String, ?> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
|
||||
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
|
||||
assertThat(lbConfigs.getConfig()).isNotNull();
|
||||
PolicySelection policySelection = (PolicySelection) lbConfigs.getConfig();
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
|
||||
ResolvedPolicySelection selection = lb.resolveLoadBalancerProvider(servers, policySelection);
|
||||
|
||||
assertThat(selection.policySelection.provider.getClass().getName()).isEqualTo(
|
||||
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
verifyZeroInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void channelTracing_lbPolicyChanged() throws Exception {
|
||||
final FakeClock clock = new FakeClock();
|
||||
List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
Helper helper = new TestHelper() {
|
||||
@Override
|
||||
@Deprecated
|
||||
public Subchannel createSubchannel(List<EquivalentAddressGroup> addrs, Attributes attrs) {
|
||||
return new TestSubchannel(CreateSubchannelArgs.newBuilder()
|
||||
.setAddresses(addrs)
|
||||
.setAttributes(attrs)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subchannel createSubchannel(CreateSubchannelArgs args) {
|
||||
return new TestSubchannel(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManagedChannel createOobChannel(EquivalentAddressGroup eag, String authority) {
|
||||
return mock(ManagedChannel.class, RETURNS_DEEP_STUBS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return "fake_authority";
|
||||
}
|
||||
|
||||
@Override
|
||||
public SynchronizationContext getSynchronizationContext() {
|
||||
return new SynchronizationContext(
|
||||
new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledExecutorService getScheduledExecutorService() {
|
||||
return clock.getScheduledExecutorService();
|
||||
}
|
||||
};
|
||||
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
new AutoConfiguredLoadBalancerFactory2(GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(helper);
|
||||
Status handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
|
||||
ConfigOrError testLbParsedConfig = ConfigOrError.fromConfig("foo");
|
||||
nextParsedConfigOrError.set(testLbParsedConfig);
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { } } ] }");
|
||||
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.INFO),
|
||||
eq("Load balancer changed from {0} to {1}"),
|
||||
eq("PickFirstLoadBalancer"),
|
||||
eq(testLbBalancer.getClass().getSimpleName()));
|
||||
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.DEBUG),
|
||||
eq("Load-balancing config: {0}"),
|
||||
eq(testLbParsedConfig.getConfig()));
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
|
||||
testLbParsedConfig = ConfigOrError.fromConfig("bar");
|
||||
nextParsedConfigOrError.set(testLbParsedConfig);
|
||||
serviceConfig = parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { } } ] }");
|
||||
lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
|
||||
.build());
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.DEBUG),
|
||||
eq("Load-balancing config: {0}"),
|
||||
eq(testLbParsedConfig.getConfig()));
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
|
||||
servers = Collections.singletonList(new EquivalentAddressGroup(
|
||||
new SocketAddress(){},
|
||||
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
|
||||
handleResult = lb.tryHandleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.build());
|
||||
|
||||
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.INFO),
|
||||
eq("Load balancer changed from {0} to {1}"),
|
||||
eq(testLbBalancer.getClass().getSimpleName()), eq("GrpclbLoadBalancer"));
|
||||
|
||||
verifyNoMoreInteractions(channelLogger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_failedOnUnknown() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"magic_balancer\": {} } ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed.getError()).isNotNull();
|
||||
assertThat(parsed.getError().getDescription())
|
||||
.isEqualTo("None of [magic_balancer] specified by Service Config are available.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerPolicy_failedOnUnknown() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingPolicy\": \"magic_balancer\"}");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed.getError()).isNotNull();
|
||||
assertThat(parsed.getError().getDescription())
|
||||
.isEqualTo("None of [magic_balancer] specified by Service Config are available.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_multipleValidPolicies() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"round_robin\": {}},"
|
||||
+ "{\"test_lb\": {} } ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getError()).isNull();
|
||||
assertThat(parsed.getConfig()).isInstanceOf(PolicySelection.class);
|
||||
assertThat(((PolicySelection) parsed.getConfig()).provider.getClass().getName())
|
||||
.isEqualTo("io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_policyShouldBeIgnoredIfConfigExists() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": [{\"round_robin\": {} } ],"
|
||||
+ "\"loadBalancingPolicy\": \"pick_first\" }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getError()).isNull();
|
||||
assertThat(parsed.getConfig()).isInstanceOf(PolicySelection.class);
|
||||
assertThat(((PolicySelection) parsed.getConfig()).provider.getClass().getName())
|
||||
.isEqualTo("io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_policyShouldBeIgnoredEvenIfUnknownPolicyExists()
|
||||
throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": [{\"magic_balancer\": {} } ],"
|
||||
+ "\"loadBalancingPolicy\": \"round_robin\" }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed.getError()).isNotNull();
|
||||
assertThat(parsed.getError().getDescription())
|
||||
.isEqualTo("None of [magic_balancer] specified by Service Config are available.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parseLoadBalancerConfig_firstInvalidPolicy() throws Exception {
|
||||
when(testLbBalancerProvider.parseLoadBalancingPolicyConfig(any(Map.class)))
|
||||
.thenReturn(ConfigOrError.fromError(Status.UNKNOWN));
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"test_lb\": {}},"
|
||||
+ "{\"round_robin\": {} } ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getConfig()).isNull();
|
||||
assertThat(parsed.getError()).isEqualTo(Status.UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parseLoadBalancerConfig_firstValidSecondInvalidPolicy() throws Exception {
|
||||
when(testLbBalancerProvider.parseLoadBalancingPolicyConfig(any(Map.class)))
|
||||
.thenReturn(ConfigOrError.fromError(Status.UNKNOWN));
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig(
|
||||
"{\"loadBalancingConfig\": ["
|
||||
+ "{\"round_robin\": {}},"
|
||||
+ "{\"test_lb\": {} } ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getConfig()).isNotNull();
|
||||
assertThat(((PolicySelection) parsed.getConfig()).config).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLoadBalancerConfig_someProvidesAreNotAvailable() throws Exception {
|
||||
Map<String, ?> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ "
|
||||
+ "{\"magic_balancer\": {} },"
|
||||
+ "{\"round_robin\": {}} ] }");
|
||||
ConfigOrError parsed = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
|
||||
assertThat(parsed).isNotNull();
|
||||
assertThat(parsed.getConfig()).isNotNull();
|
||||
assertThat(((PolicySelection) parsed.getConfig()).config).isNotNull();
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.DEBUG),
|
||||
eq("{0} specified by Service Config are not available"),
|
||||
eq(new ArrayList<>(Collections.singletonList("magic_balancer"))));
|
||||
}
|
||||
|
||||
|
||||
public static class ForwardingLoadBalancer extends LoadBalancer {
|
||||
private final LoadBalancer delegate;
|
||||
|
||||
public ForwardingLoadBalancer(LoadBalancer delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
protected LoadBalancer delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void handleResolvedAddressGroups(
|
||||
List<EquivalentAddressGroup> servers, Attributes attributes) {
|
||||
delegate().handleResolvedAddressGroups(servers, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
|
||||
delegate().handleResolvedAddresses(resolvedAddresses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNameResolutionError(Status error) {
|
||||
delegate().handleNameResolutionError(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
delegate().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, ?> parseConfig(String json) throws Exception {
|
||||
return (Map<String, ?>) JsonParser.parse(json);
|
||||
}
|
||||
|
||||
private static class TestLoadBalancer extends ForwardingLoadBalancer {
|
||||
TestLoadBalancer() {
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestHelper extends ForwardingLoadBalancerHelper {
|
||||
@Override
|
||||
protected Helper delegate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelLogger getChannelLogger() {
|
||||
return channelLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestSubchannel extends Subchannel {
|
||||
TestSubchannel(CreateSubchannelArgs args) {
|
||||
this.addrs = args.getAddresses();
|
||||
this.attrs = args.getAttributes();
|
||||
}
|
||||
|
||||
List<EquivalentAddressGroup> addrs;
|
||||
final Attributes attrs;
|
||||
|
||||
@Override
|
||||
public void start(SubchannelStateListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestConnection() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EquivalentAddressGroup> getAllAddresses() {
|
||||
return addrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attributes getAttributes() {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAddresses(List<EquivalentAddressGroup> addrs) {
|
||||
Preconditions.checkNotNull(addrs, "addrs");
|
||||
this.addrs = addrs;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FakeLoadBalancerProvider extends LoadBalancerProvider {
|
||||
private final String policyName;
|
||||
private final LoadBalancer balancer;
|
||||
private final AtomicReference<ConfigOrError> nextParsedLbPolicyConfig;
|
||||
|
||||
FakeLoadBalancerProvider(
|
||||
String policyName,
|
||||
LoadBalancer balancer,
|
||||
AtomicReference<ConfigOrError> nextParsedLbPolicyConfig) {
|
||||
this.policyName = policyName;
|
||||
this.balancer = balancer;
|
||||
this.nextParsedLbPolicyConfig = nextParsedLbPolicyConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPolicyName() {
|
||||
return policyName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancer newLoadBalancer(Helper helper) {
|
||||
return balancer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigOrError parseLoadBalancingPolicyConfig(
|
||||
Map<String, ?> rawLoadBalancingPolicyConfig) {
|
||||
if (nextParsedLbPolicyConfig == null) {
|
||||
return super.parseLoadBalancingPolicyConfig(rawLoadBalancingPolicyConfig);
|
||||
}
|
||||
return nextParsedLbPolicyConfig.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,10 +59,17 @@ public class HedgingPolicyTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> serviceConfig = (Map<String, ?>) serviceConfigObj;
|
||||
|
||||
ServiceConfigInterceptor serviceConfigInterceptor = new ServiceConfigInterceptor(
|
||||
/* retryEnabled = */ true, /* maxRetryAttemptsLimit = */ 3,
|
||||
/* maxHedgedAttemptsLimit = */ 4);
|
||||
serviceConfigInterceptor.handleUpdate(serviceConfig);
|
||||
ServiceConfigInterceptor serviceConfigInterceptor =
|
||||
new ServiceConfigInterceptor(/* retryEnabled= */ true);
|
||||
serviceConfigInterceptor
|
||||
.handleUpdate(
|
||||
ManagedChannelServiceConfig
|
||||
.fromServiceConfig(
|
||||
serviceConfig,
|
||||
/* retryEnabled= */ true,
|
||||
/* maxRetryAttemptsLimit= */ 3,
|
||||
/* maxHedgedAttemptsLimit= */ 4,
|
||||
/* loadBalancingConfig= */ null));
|
||||
|
||||
MethodDescriptor.Builder<Void, Void> builder = TestMethodDescriptors.voidMethod().toBuilder();
|
||||
|
||||
|
|
@ -131,10 +138,17 @@ public class HedgingPolicyTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> serviceConfig = (Map<String, ?>) serviceConfigObj;
|
||||
|
||||
ServiceConfigInterceptor serviceConfigInterceptor = new ServiceConfigInterceptor(
|
||||
/* retryEnabled = */ false, /* maxRetryAttemptsLimit = */ 3,
|
||||
/* maxHedgedAttemptsLimit = */ 4);
|
||||
serviceConfigInterceptor.handleUpdate(serviceConfig);
|
||||
ServiceConfigInterceptor serviceConfigInterceptor =
|
||||
new ServiceConfigInterceptor(/* retryEnabled= */ false);
|
||||
serviceConfigInterceptor
|
||||
.handleUpdate(
|
||||
ManagedChannelServiceConfig
|
||||
.fromServiceConfig(
|
||||
serviceConfig,
|
||||
/* retryEnabled= */ false,
|
||||
/* maxRetryAttemptsLimit= */ 3,
|
||||
/* maxHedgedAttemptsLimit= */ 4,
|
||||
/* loadBalancingConfig= */ null));
|
||||
|
||||
MethodDescriptor.Builder<Void, Void> builder = TestMethodDescriptors.voidMethod().toBuilder();
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static io.grpc.ConnectivityState.READY;
|
||||
import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
|
@ -87,7 +88,6 @@ import org.mockito.junit.MockitoRule;
|
|||
/**
|
||||
* Unit tests for {@link ManagedChannelImpl}'s idle mode.
|
||||
*/
|
||||
@Deprecated // migrate to ManagedChannelImplIdlenessTest2
|
||||
@RunWith(JUnit4.class)
|
||||
public class ManagedChannelImplIdlenessTest {
|
||||
@Rule
|
||||
|
|
@ -234,9 +234,12 @@ public class ManagedChannelImplIdlenessTest {
|
|||
.setAttributes(Attributes.EMPTY)
|
||||
.build();
|
||||
nameResolverListenerCaptor.getValue().onResult(resolutionResult);
|
||||
verify(mockLoadBalancer).handleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(Attributes.EMPTY)
|
||||
.build());
|
||||
|
||||
ArgumentCaptor<ResolvedAddresses> resolvedAddressCaptor =
|
||||
ArgumentCaptor.forClass(ResolvedAddresses.class);
|
||||
verify(mockLoadBalancer).handleResolvedAddresses(resolvedAddressCaptor.capture());
|
||||
assertThat(resolvedAddressCaptor.getValue().getAddresses())
|
||||
.containsExactlyElementsIn(servers);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -1,560 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static io.grpc.ConnectivityState.READY;
|
||||
import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.AdditionalAnswers.delegatesTo;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.grpc.Attributes;
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.ChannelLogger;
|
||||
import io.grpc.ClientCall;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.ConnectivityState;
|
||||
import io.grpc.EquivalentAddressGroup;
|
||||
import io.grpc.IntegerMarshaller;
|
||||
import io.grpc.LoadBalancer;
|
||||
import io.grpc.LoadBalancer.CreateSubchannelArgs;
|
||||
import io.grpc.LoadBalancer.Helper;
|
||||
import io.grpc.LoadBalancer.PickResult;
|
||||
import io.grpc.LoadBalancer.PickSubchannelArgs;
|
||||
import io.grpc.LoadBalancer.ResolvedAddresses;
|
||||
import io.grpc.LoadBalancer.Subchannel;
|
||||
import io.grpc.LoadBalancer.SubchannelPicker;
|
||||
import io.grpc.LoadBalancer.SubchannelStateListener;
|
||||
import io.grpc.LoadBalancerProvider;
|
||||
import io.grpc.LoadBalancerRegistry;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.MethodDescriptor.MethodType;
|
||||
import io.grpc.NameResolver;
|
||||
import io.grpc.NameResolver.ResolutionResult;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StringMarshaller;
|
||||
import io.grpc.internal.FakeClock.ScheduledTask;
|
||||
import io.grpc.internal.TestUtils.MockClientTransportInfo;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ManagedChannelImpl2}'s idle mode.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class ManagedChannelImplIdlenessTest2 {
|
||||
@Rule
|
||||
public final MockitoRule mocks = MockitoJUnit.rule();
|
||||
private final FakeClock timer = new FakeClock();
|
||||
private final FakeClock executor = new FakeClock();
|
||||
private final FakeClock oobExecutor = new FakeClock();
|
||||
private static final String AUTHORITY = "fakeauthority";
|
||||
private static final String USER_AGENT = "fakeagent";
|
||||
private static final long IDLE_TIMEOUT_SECONDS = 30;
|
||||
private static final String MOCK_POLICY_NAME = "mock_lb";
|
||||
private ManagedChannelImpl2 channel;
|
||||
|
||||
private final MethodDescriptor<String, Integer> method =
|
||||
MethodDescriptor.<String, Integer>newBuilder()
|
||||
.setType(MethodType.UNKNOWN)
|
||||
.setFullMethodName("service/method")
|
||||
.setRequestMarshaller(new StringMarshaller())
|
||||
.setResponseMarshaller(new IntegerMarshaller())
|
||||
.build();
|
||||
|
||||
private final List<EquivalentAddressGroup> servers = Lists.newArrayList();
|
||||
private final ObjectPool<Executor> executorPool =
|
||||
new FixedObjectPool<Executor>(executor.getScheduledExecutorService());
|
||||
private final ObjectPool<Executor> oobExecutorPool =
|
||||
new FixedObjectPool<Executor>(oobExecutor.getScheduledExecutorService());
|
||||
|
||||
@Mock private ClientTransportFactory mockTransportFactory;
|
||||
@Mock private LoadBalancer mockLoadBalancer;
|
||||
@Mock private SubchannelStateListener subchannelStateListener;
|
||||
private final LoadBalancerProvider mockLoadBalancerProvider =
|
||||
mock(LoadBalancerProvider.class, delegatesTo(new LoadBalancerProvider() {
|
||||
@Override
|
||||
public LoadBalancer newLoadBalancer(Helper helper) {
|
||||
return mockLoadBalancer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 999;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPolicyName() {
|
||||
return MOCK_POLICY_NAME;
|
||||
}
|
||||
}));
|
||||
|
||||
@Mock private NameResolver mockNameResolver;
|
||||
@Mock private NameResolver.Factory mockNameResolverFactory;
|
||||
@Mock private ClientCall.Listener<Integer> mockCallListener;
|
||||
@Mock private ClientCall.Listener<Integer> mockCallListener2;
|
||||
@Captor private ArgumentCaptor<NameResolver.Listener2> nameResolverListenerCaptor;
|
||||
private BlockingQueue<MockClientTransportInfo> newTransports;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("deprecation") // For NameResolver.Listener
|
||||
public void setUp() {
|
||||
LoadBalancerRegistry.getDefaultRegistry().register(mockLoadBalancerProvider);
|
||||
when(mockNameResolver.getServiceAuthority()).thenReturn(AUTHORITY);
|
||||
when(mockNameResolverFactory
|
||||
.newNameResolver(any(URI.class), any(NameResolver.Args.class)))
|
||||
.thenReturn(mockNameResolver);
|
||||
when(mockTransportFactory.getScheduledExecutorService())
|
||||
.thenReturn(timer.getScheduledExecutorService());
|
||||
|
||||
class Builder extends AbstractManagedChannelImplBuilder<Builder> {
|
||||
Builder(String target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
@Override protected ClientTransportFactory buildTransportFactory() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override public Builder usePlaintext() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
Builder builder = new Builder("fake://target")
|
||||
.nameResolverFactory(mockNameResolverFactory)
|
||||
.defaultLoadBalancingPolicy(MOCK_POLICY_NAME)
|
||||
.idleTimeout(IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.userAgent(USER_AGENT);
|
||||
builder.executorPool = executorPool;
|
||||
channel = new ManagedChannelImpl2(
|
||||
builder, mockTransportFactory, new FakeBackoffPolicyProvider(),
|
||||
oobExecutorPool, timer.getStopwatchSupplier(),
|
||||
Collections.<ClientInterceptor>emptyList(),
|
||||
TimeProvider.SYSTEM_TIME_PROVIDER);
|
||||
newTransports = TestUtils.captureTransports(mockTransportFactory);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ArrayList<SocketAddress> addrs = Lists.newArrayList();
|
||||
for (int j = 0; j < 2; j++) {
|
||||
addrs.add(new FakeSocketAddress("servergroup" + i + "server" + j));
|
||||
}
|
||||
servers.add(new EquivalentAddressGroup(addrs));
|
||||
}
|
||||
verify(mockNameResolverFactory).newNameResolver(any(URI.class), any(NameResolver.Args.class));
|
||||
// Verify the initial idleness
|
||||
verify(mockLoadBalancerProvider, never()).newLoadBalancer(any(Helper.class));
|
||||
verify(mockTransportFactory, never()).newClientTransport(
|
||||
any(SocketAddress.class),
|
||||
any(ClientTransportFactory.ClientTransportOptions.class),
|
||||
any(ChannelLogger.class));
|
||||
verify(mockNameResolver, never()).start(any(NameResolver.Listener.class));
|
||||
verify(mockNameResolver, never()).start(any(NameResolver.Listener2.class));
|
||||
}
|
||||
|
||||
@After
|
||||
public void allPendingTasksAreRun() {
|
||||
Collection<ScheduledTask> pendingTimerTasks = timer.getPendingTasks();
|
||||
for (ScheduledTask a : pendingTimerTasks) {
|
||||
assertFalse(Rescheduler.isEnabled(a.command));
|
||||
}
|
||||
assertEquals(executor.getPendingTasks() + " should be empty", 0, executor.numPendingTasks());
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
LoadBalancerRegistry.getDefaultRegistry().deregister(mockLoadBalancerProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newCallExitsIdleness() throws Exception {
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata());
|
||||
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
|
||||
|
||||
verify(mockNameResolver).start(nameResolverListenerCaptor.capture());
|
||||
// Simulate new address resolved to make sure the LoadBalancer is correctly linked to
|
||||
// the NameResolver.
|
||||
ResolutionResult resolutionResult =
|
||||
ResolutionResult.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.build();
|
||||
nameResolverListenerCaptor.getValue().onResult(resolutionResult);
|
||||
|
||||
ArgumentCaptor<ResolvedAddresses> resolvedAddressCaptor =
|
||||
ArgumentCaptor.forClass(ResolvedAddresses.class);
|
||||
verify(mockLoadBalancer).handleResolvedAddresses(resolvedAddressCaptor.capture());
|
||||
assertThat(resolvedAddressCaptor.getValue().getAddresses())
|
||||
.containsExactlyElementsIn(servers);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newCallRefreshesIdlenessTimer() throws Exception {
|
||||
// First call to exit the initial idleness, then immediately cancel the call.
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata());
|
||||
call.cancel("For testing", null);
|
||||
|
||||
// Verify that we have exited the idle mode
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// Move closer to idleness, but not yet.
|
||||
timer.forwardTime(IDLE_TIMEOUT_SECONDS - 1, TimeUnit.SECONDS);
|
||||
verify(mockLoadBalancer, never()).shutdown();
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// A new call would refresh the timer
|
||||
call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata());
|
||||
call.cancel("For testing", null);
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// ... so that passing the same length of time will not trigger idle mode
|
||||
timer.forwardTime(IDLE_TIMEOUT_SECONDS - 1, TimeUnit.SECONDS);
|
||||
verify(mockLoadBalancer, never()).shutdown();
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// ... until the time since last call has reached the timeout
|
||||
timer.forwardTime(1, TimeUnit.SECONDS);
|
||||
verify(mockLoadBalancer).shutdown();
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// Drain the app executor, which runs the call listeners
|
||||
verify(mockCallListener, never()).onClose(any(Status.class), any(Metadata.class));
|
||||
assertEquals(2, executor.runDueTasks());
|
||||
verify(mockCallListener, times(2)).onClose(any(Status.class), any(Metadata.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delayedTransportHoldsOffIdleness() throws Exception {
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata());
|
||||
assertTrue(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// As long as the delayed transport is in-use (by the pending RPC), the channel won't go idle.
|
||||
timer.forwardTime(IDLE_TIMEOUT_SECONDS * 2, TimeUnit.SECONDS);
|
||||
assertTrue(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// Cancelling the only RPC will reset the in-use state.
|
||||
assertEquals(0, executor.numPendingTasks());
|
||||
call.cancel("In test", null);
|
||||
assertEquals(1, executor.runDueTasks());
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
// And allow the channel to go idle.
|
||||
timer.forwardTime(IDLE_TIMEOUT_SECONDS - 1, TimeUnit.SECONDS);
|
||||
verify(mockLoadBalancer, never()).shutdown();
|
||||
timer.forwardTime(1, TimeUnit.SECONDS);
|
||||
verify(mockLoadBalancer).shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void realTransportsHoldsOffIdleness() throws Exception {
|
||||
final EquivalentAddressGroup addressGroup = servers.get(1);
|
||||
|
||||
// Start a call, which goes to delayed transport
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata());
|
||||
|
||||
// Verify that we have exited the idle mode
|
||||
ArgumentCaptor<Helper> helperCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(helperCaptor.capture());
|
||||
Helper helper = helperCaptor.getValue();
|
||||
assertTrue(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// Assume LoadBalancer has received an address, then create a subchannel.
|
||||
Subchannel subchannel = createSubchannelSafely(helper, addressGroup, Attributes.EMPTY);
|
||||
requestConnectionSafely(helper, subchannel);
|
||||
MockClientTransportInfo t0 = newTransports.poll();
|
||||
t0.listener.transportReady();
|
||||
|
||||
SubchannelPicker mockPicker = mock(SubchannelPicker.class);
|
||||
when(mockPicker.pickSubchannel(any(PickSubchannelArgs.class)))
|
||||
.thenReturn(PickResult.withSubchannel(subchannel));
|
||||
updateBalancingStateSafely(helper, READY, mockPicker);
|
||||
// Delayed transport creates real streams in the app executor
|
||||
executor.runDueTasks();
|
||||
|
||||
// Delayed transport exits in-use, while real transport has not entered in-use yet.
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// Now it's in-use
|
||||
t0.listener.transportInUse(true);
|
||||
assertTrue(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// As long as the transport is in-use, the channel won't go idle.
|
||||
timer.forwardTime(IDLE_TIMEOUT_SECONDS * 2, TimeUnit.SECONDS);
|
||||
assertTrue(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
t0.listener.transportInUse(false);
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
// And allow the channel to go idle.
|
||||
timer.forwardTime(IDLE_TIMEOUT_SECONDS - 1, TimeUnit.SECONDS);
|
||||
verify(mockLoadBalancer, never()).shutdown();
|
||||
timer.forwardTime(1, TimeUnit.SECONDS);
|
||||
verify(mockLoadBalancer).shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSubchannelAddresses_newAddressConnects() {
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata()); // Create LB
|
||||
ArgumentCaptor<Helper> helperCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(helperCaptor.capture());
|
||||
Helper helper = helperCaptor.getValue();
|
||||
Subchannel subchannel = createSubchannelSafely(helper, servers.get(0), Attributes.EMPTY);
|
||||
|
||||
requestConnectionSafely(helper, subchannel);
|
||||
MockClientTransportInfo t0 = newTransports.poll();
|
||||
t0.listener.transportReady();
|
||||
|
||||
updateSubchannelAddressesSafely(helper, subchannel, servers.get(1));
|
||||
|
||||
requestConnectionSafely(helper, subchannel);
|
||||
MockClientTransportInfo t1 = newTransports.poll();
|
||||
t1.listener.transportReady();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSubchannelAddresses_existingAddressDoesNotConnect() {
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata()); // Create LB
|
||||
ArgumentCaptor<Helper> helperCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(helperCaptor.capture());
|
||||
Helper helper = helperCaptor.getValue();
|
||||
Subchannel subchannel = createSubchannelSafely(helper, servers.get(0), Attributes.EMPTY);
|
||||
|
||||
requestConnectionSafely(helper, subchannel);
|
||||
MockClientTransportInfo t0 = newTransports.poll();
|
||||
t0.listener.transportReady();
|
||||
|
||||
List<SocketAddress> changedList = new ArrayList<>(servers.get(0).getAddresses());
|
||||
changedList.add(new FakeSocketAddress("aDifferentServer"));
|
||||
updateSubchannelAddressesSafely(helper, subchannel, new EquivalentAddressGroup(changedList));
|
||||
|
||||
requestConnectionSafely(helper, subchannel);
|
||||
assertNull(newTransports.poll());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oobTransportDoesNotAffectIdleness() {
|
||||
// Start a call, which goes to delayed transport
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata());
|
||||
|
||||
// Verify that we have exited the idle mode
|
||||
ArgumentCaptor<Helper> helperCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(helperCaptor.capture());
|
||||
Helper helper = helperCaptor.getValue();
|
||||
|
||||
// Fail the RPC
|
||||
SubchannelPicker failingPicker = mock(SubchannelPicker.class);
|
||||
when(failingPicker.pickSubchannel(any(PickSubchannelArgs.class)))
|
||||
.thenReturn(PickResult.withError(Status.UNAVAILABLE));
|
||||
updateBalancingStateSafely(helper, TRANSIENT_FAILURE, failingPicker);
|
||||
executor.runDueTasks();
|
||||
verify(mockCallListener).onClose(same(Status.UNAVAILABLE), any(Metadata.class));
|
||||
|
||||
// ... so that the channel resets its in-use state
|
||||
assertFalse(channel.inUseStateAggregator.isInUse());
|
||||
|
||||
// Now make an RPC on an OOB channel
|
||||
ManagedChannel oob = helper.createOobChannel(servers.get(0), "oobauthority");
|
||||
verify(mockTransportFactory, never())
|
||||
.newClientTransport(
|
||||
any(SocketAddress.class),
|
||||
eq(new ClientTransportFactory.ClientTransportOptions()
|
||||
.setAuthority("oobauthority")
|
||||
.setUserAgent(USER_AGENT)),
|
||||
any(ChannelLogger.class));
|
||||
ClientCall<String, Integer> oobCall = oob.newCall(method, CallOptions.DEFAULT);
|
||||
oobCall.start(mockCallListener2, new Metadata());
|
||||
verify(mockTransportFactory)
|
||||
.newClientTransport(
|
||||
any(SocketAddress.class),
|
||||
eq(new ClientTransportFactory.ClientTransportOptions()
|
||||
.setAuthority("oobauthority")
|
||||
.setUserAgent(USER_AGENT)),
|
||||
any(ChannelLogger.class));
|
||||
MockClientTransportInfo oobTransportInfo = newTransports.poll();
|
||||
assertEquals(0, newTransports.size());
|
||||
// The OOB transport reports in-use state
|
||||
oobTransportInfo.listener.transportInUse(true);
|
||||
|
||||
// But it won't stop the channel from going idle
|
||||
verify(mockLoadBalancer, never()).shutdown();
|
||||
timer.forwardTime(IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
verify(mockLoadBalancer).shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateOobChannelAddresses_newAddressConnects() {
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata()); // Create LB
|
||||
ArgumentCaptor<Helper> helperCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(helperCaptor.capture());
|
||||
Helper helper = helperCaptor.getValue();
|
||||
ManagedChannel oobChannel = helper.createOobChannel(servers.get(0), "localhost");
|
||||
|
||||
oobChannel.newCall(method, CallOptions.DEFAULT).start(mockCallListener, new Metadata());
|
||||
MockClientTransportInfo t0 = newTransports.poll();
|
||||
t0.listener.transportReady();
|
||||
|
||||
helper.updateOobChannelAddresses(oobChannel, servers.get(1));
|
||||
|
||||
oobChannel.newCall(method, CallOptions.DEFAULT).start(mockCallListener, new Metadata());
|
||||
MockClientTransportInfo t1 = newTransports.poll();
|
||||
t1.listener.transportReady();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateOobChannelAddresses_existingAddressDoesNotConnect() {
|
||||
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
|
||||
call.start(mockCallListener, new Metadata()); // Create LB
|
||||
ArgumentCaptor<Helper> helperCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(helperCaptor.capture());
|
||||
Helper helper = helperCaptor.getValue();
|
||||
ManagedChannel oobChannel = helper.createOobChannel(servers.get(0), "localhost");
|
||||
|
||||
oobChannel.newCall(method, CallOptions.DEFAULT).start(mockCallListener, new Metadata());
|
||||
MockClientTransportInfo t0 = newTransports.poll();
|
||||
t0.listener.transportReady();
|
||||
|
||||
List<SocketAddress> changedList = new ArrayList<>(servers.get(0).getAddresses());
|
||||
changedList.add(new FakeSocketAddress("aDifferentServer"));
|
||||
helper.updateOobChannelAddresses(oobChannel, new EquivalentAddressGroup(changedList));
|
||||
|
||||
oobChannel.newCall(method, CallOptions.DEFAULT).start(mockCallListener, new Metadata());
|
||||
assertNull(newTransports.poll());
|
||||
}
|
||||
|
||||
private static class FakeBackoffPolicyProvider implements BackoffPolicy.Provider {
|
||||
@Override
|
||||
public BackoffPolicy get() {
|
||||
return new BackoffPolicy() {
|
||||
@Override
|
||||
public long nextBackoffNanos() {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static class FakeSocketAddress extends SocketAddress {
|
||||
final String name;
|
||||
|
||||
FakeSocketAddress(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FakeSocketAddress-" + name;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods to call methods from SynchronizationContext
|
||||
private Subchannel createSubchannelSafely(
|
||||
final Helper helper, final EquivalentAddressGroup addressGroup, final Attributes attrs) {
|
||||
final AtomicReference<Subchannel> resultCapture = new AtomicReference<>();
|
||||
helper.getSynchronizationContext().execute(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Subchannel s = helper.createSubchannel(CreateSubchannelArgs.newBuilder()
|
||||
.setAddresses(addressGroup)
|
||||
.setAttributes(attrs)
|
||||
.build());
|
||||
s.start(subchannelStateListener);
|
||||
resultCapture.set(s);
|
||||
}
|
||||
});
|
||||
return resultCapture.get();
|
||||
}
|
||||
|
||||
private static void requestConnectionSafely(Helper helper, final Subchannel subchannel) {
|
||||
helper.getSynchronizationContext().execute(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
subchannel.requestConnection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void updateBalancingStateSafely(
|
||||
final Helper helper, final ConnectivityState state, final SubchannelPicker picker) {
|
||||
helper.getSynchronizationContext().execute(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
helper.updateBalancingState(state, picker);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void updateSubchannelAddressesSafely(
|
||||
final Helper helper, final Subchannel subchannel, final EquivalentAddressGroup addrs) {
|
||||
helper.getSynchronizationContext().execute(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
subchannel.updateAddresses(Collections.singletonList(addrs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,6 @@ import com.google.common.base.Throwables;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.truth.Truth;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
|
@ -103,6 +102,7 @@ import io.grpc.ServerMethodDefinition;
|
|||
import io.grpc.Status;
|
||||
import io.grpc.Status.Code;
|
||||
import io.grpc.StringMarshaller;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory.PolicySelection;
|
||||
import io.grpc.internal.ClientTransportFactory.ClientTransportOptions;
|
||||
import io.grpc.internal.InternalSubchannel.TransportLogger;
|
||||
import io.grpc.internal.ManagedChannelImpl.ScParser;
|
||||
|
|
@ -152,8 +152,9 @@ import org.mockito.junit.MockitoRule;
|
|||
import org.mockito.stubbing.Answer;
|
||||
|
||||
/** Unit tests for {@link ManagedChannelImpl}. */
|
||||
@Deprecated // to be migrated to ManagedChannelImplTest2
|
||||
@RunWith(JUnit4.class)
|
||||
// TODO(creamsoup) remove backward compatible check when fully migrated
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ManagedChannelImplTest {
|
||||
private static final int DEFAULT_PORT = 447;
|
||||
|
||||
|
|
@ -272,6 +273,8 @@ public class ManagedChannelImplTest {
|
|||
private boolean requestConnection = true;
|
||||
private BlockingQueue<MockClientTransportInfo> transports;
|
||||
private boolean panicExpected;
|
||||
@Captor
|
||||
private ArgumentCaptor<ResolvedAddresses> resolvedAddressCaptor;
|
||||
|
||||
private ArgumentCaptor<ClientStreamListener> streamListenerCaptor =
|
||||
ArgumentCaptor.forClass(ClientStreamListener.class);
|
||||
|
|
@ -749,11 +752,8 @@ public class ManagedChannelImplTest {
|
|||
|
||||
FakeNameResolverFactory.FakeNameResolver resolver = nameResolverFactory.resolvers.get(0);
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
|
||||
verify(mockLoadBalancer).handleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(Arrays.asList(addressGroup))
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.build());
|
||||
verify(mockLoadBalancer).handleResolvedAddresses(resolvedAddressCaptor.capture());
|
||||
assertThat(resolvedAddressCaptor.getValue().getAddresses()).containsExactly(addressGroup);
|
||||
|
||||
SubchannelStateListener stateListener1 = mock(SubchannelStateListener.class);
|
||||
SubchannelStateListener stateListener2 = mock(SubchannelStateListener.class);
|
||||
|
|
@ -970,13 +970,12 @@ public class ManagedChannelImplTest {
|
|||
// Pass a FakeNameResolverFactory with an empty list and LB config
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri).build();
|
||||
Map<String, Object> serviceConfig =
|
||||
Map<String, Object> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"mock_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextConfigOrError.set(ConfigOrError.fromConfig(parsedServiceConfig));
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
createChannel();
|
||||
|
||||
|
|
@ -985,7 +984,7 @@ public class ManagedChannelImplTest {
|
|||
verify(mockLoadBalancer).handleNameResolutionError(statusCaptor.capture());
|
||||
Status status = statusCaptor.getValue();
|
||||
assertSame(Status.Code.UNAVAILABLE, status.getCode());
|
||||
Truth.assertThat(status.getDescription()).startsWith(errorDescription);
|
||||
assertThat(status.getDescription()).startsWith(errorDescription);
|
||||
|
||||
// A resolution retry has been scheduled
|
||||
assertEquals(1, timer.numPendingTasks(NAME_RESOLVER_REFRESH_TASK_FILTER));
|
||||
|
|
@ -998,13 +997,18 @@ public class ManagedChannelImplTest {
|
|||
// Pass a FakeNameResolverFactory with an empty list and LB config
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri).build();
|
||||
Map<String, Object> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"mock_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
String rawLbConfig = "{ \"setting1\": \"high\" }";
|
||||
Map<String, Object> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"mock_lb\": " + rawLbConfig + " } ] }");
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(
|
||||
rawServiceConfig,
|
||||
new PolicySelection(
|
||||
mockLoadBalancerProvider,
|
||||
parseConfig(rawLbConfig),
|
||||
new Object()));
|
||||
nameResolverFactory.nextConfigOrError.set(ConfigOrError.fromConfig(parsedServiceConfig));
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
createChannel();
|
||||
|
||||
|
|
@ -1018,7 +1022,7 @@ public class ManagedChannelImplTest {
|
|||
Map<String, ?> lbConfig = actualAttrs.get(LoadBalancer.ATTR_LOAD_BALANCING_CONFIG);
|
||||
assertEquals(ImmutableMap.of("setting1", "high"), lbConfig);
|
||||
assertSame(
|
||||
serviceConfig, actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG));
|
||||
rawServiceConfig, actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG));
|
||||
|
||||
// A no resolution retry
|
||||
assertEquals(0, timer.numPendingTasks(NAME_RESOLVER_REFRESH_TASK_FILTER));
|
||||
|
|
@ -1099,10 +1103,8 @@ public class ManagedChannelImplTest {
|
|||
|
||||
// Simulate name resolution results
|
||||
EquivalentAddressGroup addressGroup = new EquivalentAddressGroup(resolvedAddrs);
|
||||
inOrder.verify(mockLoadBalancer).handleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(Arrays.asList(addressGroup))
|
||||
.build());
|
||||
inOrder.verify(mockLoadBalancer).handleResolvedAddresses(resolvedAddressCaptor.capture());
|
||||
assertThat(resolvedAddressCaptor.getValue().getAddresses()).containsExactly(addressGroup);
|
||||
Subchannel subchannel =
|
||||
createSubchannelSafely(helper, addressGroup, Attributes.EMPTY, subchannelStateListener);
|
||||
when(mockPicker.pickSubchannel(any(PickSubchannelArgs.class)))
|
||||
|
|
@ -1248,10 +1250,9 @@ public class ManagedChannelImplTest {
|
|||
|
||||
// Simulate name resolution results
|
||||
EquivalentAddressGroup addressGroup = new EquivalentAddressGroup(resolvedAddrs);
|
||||
inOrder.verify(mockLoadBalancer).handleResolvedAddresses(
|
||||
ResolvedAddresses.newBuilder()
|
||||
.setAddresses(Arrays.asList(addressGroup))
|
||||
.build());
|
||||
inOrder.verify(mockLoadBalancer).handleResolvedAddresses(resolvedAddressCaptor.capture());
|
||||
assertThat(resolvedAddressCaptor.getValue().getAddresses()).containsExactly(addressGroup);
|
||||
|
||||
Subchannel subchannel =
|
||||
createSubchannelSafely(helper, addressGroup, Attributes.EMPTY, subchannelStateListener);
|
||||
when(mockPicker.pickSubchannel(any(PickSubchannelArgs.class)))
|
||||
|
|
@ -2760,7 +2761,6 @@ public class ManagedChannelImplTest {
|
|||
.setAddresses(Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
Arrays.asList(new SocketAddress() {}, new SocketAddress() {}))))
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.build();
|
||||
nameResolverFactory.resolvers.get(0).listener.onResult(resolutionResult1);
|
||||
assertThat(getStats(channel).channelTrace.events).hasSize(prevSize);
|
||||
|
|
@ -2778,7 +2778,6 @@ public class ManagedChannelImplTest {
|
|||
.setAddresses(Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
Arrays.asList(new SocketAddress() {}, new SocketAddress() {}))))
|
||||
.setAttributes(Attributes.EMPTY)
|
||||
.build();
|
||||
nameResolverFactory.resolvers.get(0).listener.onResult(resolutionResult2);
|
||||
assertThat(getStats(channel).channelTrace.events).hasSize(prevSize + 1);
|
||||
|
|
@ -2800,11 +2799,16 @@ public class ManagedChannelImplTest {
|
|||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, new HashMap<String, Object>())
|
||||
.build();
|
||||
ManagedChannelServiceConfig mcsc1 = createManagedChannelServiceConfig(
|
||||
ImmutableMap.<String, Object>of(),
|
||||
new PolicySelection(
|
||||
mockLoadBalancerProvider, ImmutableMap.of("foo", "bar"), null));
|
||||
ResolutionResult resolutionResult1 = ResolutionResult.newBuilder()
|
||||
.setAddresses(Collections.singletonList(
|
||||
new EquivalentAddressGroup(
|
||||
Arrays.asList(new SocketAddress() {}, new SocketAddress() {}))))
|
||||
.setAttributes(attributes)
|
||||
.setServiceConfig(ConfigOrError.fromConfig(mcsc1))
|
||||
.build();
|
||||
nameResolverFactory.resolvers.get(0).listener.onResult(resolutionResult1);
|
||||
assertThat(getStats(channel).channelTrace.events).hasSize(prevSize + 1);
|
||||
|
|
@ -2821,6 +2825,7 @@ public class ManagedChannelImplTest {
|
|||
new EquivalentAddressGroup(
|
||||
Arrays.asList(new SocketAddress() {}, new SocketAddress() {}))))
|
||||
.setAttributes(attributes)
|
||||
.setServiceConfig(ConfigOrError.fromConfig(mcsc1))
|
||||
.build();
|
||||
nameResolverFactory.resolvers.get(0).listener.onResult(resolutionResult2);
|
||||
assertThat(getStats(channel).channelTrace.events).hasSize(prevSize);
|
||||
|
|
@ -2838,6 +2843,7 @@ public class ManagedChannelImplTest {
|
|||
new EquivalentAddressGroup(
|
||||
Arrays.asList(new SocketAddress() {}, new SocketAddress() {}))))
|
||||
.setAttributes(attributes)
|
||||
.setServiceConfig(ConfigOrError.fromConfig(ManagedChannelServiceConfig.empty()))
|
||||
.build();
|
||||
nameResolverFactory.resolvers.get(0).listener.onResult(resolutionResult3);
|
||||
assertThat(getStats(channel).channelTrace.events).hasSize(prevSize + 1);
|
||||
|
|
@ -3187,16 +3193,21 @@ public class ManagedChannelImplTest {
|
|||
name.put("service", "service");
|
||||
methodConfig.put("name", Arrays.<Object>asList(name));
|
||||
methodConfig.put("retryPolicy", retryPolicy);
|
||||
Map<String, Object> serviceConfig = new HashMap<>();
|
||||
serviceConfig.put("methodConfig", Arrays.<Object>asList(methodConfig));
|
||||
Map<String, Object> rawServiceConfig = new HashMap<>();
|
||||
rawServiceConfig.put("methodConfig", Arrays.<Object>asList(methodConfig));
|
||||
Attributes attributesWithRetryPolicy = Attributes
|
||||
.newBuilder().set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig).build();
|
||||
.newBuilder().set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, rawServiceConfig).build();
|
||||
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri)
|
||||
.setServers(Collections.singletonList(new EquivalentAddressGroup(socketAddress)))
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(attributesWithRetryPolicy);
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
nameResolverFactory.nextConfigOrError.set(
|
||||
ConfigOrError.fromConfig(managedChannelServiceConfig));
|
||||
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
channelBuilder.executor(MoreExecutors.directExecutor());
|
||||
channelBuilder.enableRetry();
|
||||
|
|
@ -3296,16 +3307,21 @@ public class ManagedChannelImplTest {
|
|||
name.put("service", "service");
|
||||
methodConfig.put("name", Arrays.<Object>asList(name));
|
||||
methodConfig.put("hedgingPolicy", hedgingPolicy);
|
||||
Map<String, Object> serviceConfig = new HashMap<>();
|
||||
serviceConfig.put("methodConfig", Arrays.<Object>asList(methodConfig));
|
||||
Map<String, Object> rawServiceConfig = new HashMap<>();
|
||||
rawServiceConfig.put("methodConfig", Arrays.<Object>asList(methodConfig));
|
||||
Attributes attributesWithRetryPolicy = Attributes
|
||||
.newBuilder().set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig).build();
|
||||
.newBuilder().set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, rawServiceConfig).build();
|
||||
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri)
|
||||
.setServers(Collections.singletonList(new EquivalentAddressGroup(socketAddress)))
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(attributesWithRetryPolicy);
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
nameResolverFactory.nextConfigOrError.set(
|
||||
ConfigOrError.fromConfig(managedChannelServiceConfig));
|
||||
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
channelBuilder.executor(MoreExecutors.directExecutor());
|
||||
channelBuilder.enableRetry();
|
||||
|
|
@ -3388,6 +3404,8 @@ public class ManagedChannelImplTest {
|
|||
|
||||
@Test
|
||||
public void badServiceConfigIsRecoverable() throws Exception {
|
||||
final Map<String, Object> invalidServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [{\"kaboom\": {}}]}");
|
||||
final List<EquivalentAddressGroup> addresses =
|
||||
ImmutableList.of(new EquivalentAddressGroup(new SocketAddress() {}));
|
||||
final class FakeNameResolver extends NameResolver {
|
||||
|
|
@ -3407,9 +3425,11 @@ public class ManagedChannelImplTest {
|
|||
.setAttributes(
|
||||
Attributes.newBuilder()
|
||||
.set(
|
||||
GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG,
|
||||
ImmutableMap.<String, Object>of("loadBalancingPolicy", "kaboom"))
|
||||
GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, invalidServiceConfig)
|
||||
.build())
|
||||
.setServiceConfig(
|
||||
ConfigOrError.fromError(
|
||||
Status.INTERNAL.withDescription("kaboom is invalid")))
|
||||
.build());
|
||||
}
|
||||
|
||||
|
|
@ -3454,23 +3474,31 @@ public class ManagedChannelImplTest {
|
|||
ListenableFuture<Void> future1 = ClientCalls.futureUnaryCall(call1, null);
|
||||
executor.runDueTasks();
|
||||
try {
|
||||
future1.get();
|
||||
future1.get(1, TimeUnit.SECONDS);
|
||||
Assert.fail();
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(Throwables.getStackTraceAsString(e.getCause())).contains("kaboom");
|
||||
}
|
||||
|
||||
// ok the service config is bad, let's fix it.
|
||||
|
||||
Map<String, Object> rawServiceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [{\"round_robin\": {}}]}");
|
||||
Object fakeLbConfig = new Object();
|
||||
PolicySelection lbConfigs =
|
||||
new PolicySelection(
|
||||
mockLoadBalancerProvider, rawServiceConfig, fakeLbConfig);
|
||||
mockLoadBalancerProvider.parseLoadBalancingPolicyConfig(rawServiceConfig);
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, lbConfigs);
|
||||
factory.resolver.listener.onResult(
|
||||
ResolutionResult.newBuilder()
|
||||
.setAddresses(addresses)
|
||||
.setAttributes(
|
||||
Attributes.newBuilder()
|
||||
.set(
|
||||
GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG,
|
||||
ImmutableMap.<String, Object>of("loadBalancingPolicy", "round_robin"))
|
||||
GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, rawServiceConfig)
|
||||
.build())
|
||||
.setServiceConfig(ConfigOrError.fromConfig(managedChannelServiceConfig))
|
||||
.build());
|
||||
|
||||
ClientCall<Void, Void> call2 = mychannel.newCall(
|
||||
|
|
@ -3621,7 +3649,8 @@ public class ManagedChannelImplTest {
|
|||
retryEnabled,
|
||||
maxRetryAttemptsLimit,
|
||||
maxHedgedAttemptsLimit,
|
||||
autoConfiguredLoadBalancerFactory);
|
||||
autoConfiguredLoadBalancerFactory,
|
||||
mock(ChannelLogger.class));
|
||||
|
||||
ConfigOrError coe = parser.parseServiceConfig(ImmutableMap.<String, Object>of());
|
||||
|
||||
|
|
@ -3643,7 +3672,8 @@ public class ManagedChannelImplTest {
|
|||
retryEnabled,
|
||||
maxRetryAttemptsLimit,
|
||||
maxHedgedAttemptsLimit,
|
||||
autoConfiguredLoadBalancerFactory);
|
||||
autoConfiguredLoadBalancerFactory,
|
||||
mock(ChannelLogger.class));
|
||||
|
||||
ConfigOrError coe =
|
||||
parser.parseServiceConfig(ImmutableMap.<String, Object>of("methodConfig", "bogus"));
|
||||
|
|
@ -3666,14 +3696,15 @@ public class ManagedChannelImplTest {
|
|||
retryEnabled,
|
||||
maxRetryAttemptsLimit,
|
||||
maxHedgedAttemptsLimit,
|
||||
autoConfiguredLoadBalancerFactory);
|
||||
autoConfiguredLoadBalancerFactory,
|
||||
mock(ChannelLogger.class));
|
||||
|
||||
ConfigOrError coe =
|
||||
parser.parseServiceConfig(ImmutableMap.of("loadBalancingConfig", ImmutableList.of()));
|
||||
|
||||
assertThat(coe.getError()).isNull();
|
||||
ManagedChannelServiceConfig cfg = (ManagedChannelServiceConfig) coe.getConfig();
|
||||
assertThat(cfg.getLoadBalancingConfig()).isEqualTo(null);
|
||||
assertThat(cfg.getLoadBalancingConfig()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -3686,15 +3717,15 @@ public class ManagedChannelImplTest {
|
|||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
channelBuilder.disableServiceConfigLookUp();
|
||||
|
||||
Map<String, Object> serviceConfig =
|
||||
Map<String, Object> rawServiceConfig =
|
||||
parseConfig("{\"methodConfig\":[{"
|
||||
+ "\"name\":[{\"service\":\"SimpleService1\"}],"
|
||||
+ "\"waitForReady\":true}]}");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
nameResolverFactory.nextConfigOrError.set(
|
||||
ConfigOrError.fromConfig(managedChannelServiceConfig));
|
||||
|
||||
createChannel();
|
||||
|
||||
|
|
@ -3703,7 +3734,7 @@ public class ManagedChannelImplTest {
|
|||
verify(mockLoadBalancer).handleResolvedAddresses(resultCaptor.capture());
|
||||
assertThat(resultCaptor.getValue().getAddresses()).containsExactly(addressGroup);
|
||||
Attributes actualAttrs = resultCaptor.getValue().getAttributes();
|
||||
assertThat(actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG)).isNull();
|
||||
assertThat(actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG)).isEmpty();
|
||||
verify(mockLoadBalancer, never()).handleNameResolutionError(any(Status.class));
|
||||
} finally {
|
||||
LoadBalancerRegistry.getDefaultRegistry().deregister(mockLoadBalancerProvider);
|
||||
|
|
@ -3725,12 +3756,12 @@ public class ManagedChannelImplTest {
|
|||
+ "\"waitForReady\":true}]}");
|
||||
channelBuilder.defaultServiceConfig(defaultServiceConfig);
|
||||
|
||||
Map<String, Object> serviceConfig = new HashMap<>();
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
Map<String, Object> rawServiceConfig = new HashMap<>();
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
nameResolverFactory.nextConfigOrError.set(
|
||||
ConfigOrError.fromConfig(managedChannelServiceConfig));
|
||||
|
||||
createChannel();
|
||||
|
||||
|
|
@ -3757,15 +3788,15 @@ public class ManagedChannelImplTest {
|
|||
.setServers(ImmutableList.of(addressGroup)).build();
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
|
||||
Map<String, Object> serviceConfig =
|
||||
Map<String, Object> rawServiceConfig =
|
||||
parseConfig("{\"methodConfig\":[{"
|
||||
+ "\"name\":[{\"service\":\"SimpleService1\"}],"
|
||||
+ "\"waitForReady\":true}]}");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
nameResolverFactory.nextConfigOrError.set(
|
||||
ConfigOrError.fromConfig(managedChannelServiceConfig));
|
||||
|
||||
createChannel();
|
||||
ArgumentCaptor<ResolvedAddresses> resultCaptor =
|
||||
|
|
@ -3775,19 +3806,19 @@ public class ManagedChannelImplTest {
|
|||
Attributes actualAttrs = resultCaptor.getValue().getAttributes();
|
||||
|
||||
assertThat(actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG))
|
||||
.isEqualTo(serviceConfig);
|
||||
.isEqualTo(rawServiceConfig);
|
||||
verify(mockLoadBalancer, never()).handleNameResolutionError(any(Status.class));
|
||||
|
||||
// new config
|
||||
serviceConfig =
|
||||
rawServiceConfig =
|
||||
parseConfig("{\"methodConfig\":[{"
|
||||
+ "\"name\":[{\"service\":\"SimpleService1\"}],"
|
||||
+ "\"waitForReady\":false}]}");
|
||||
serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
nameResolverFactory.nextConfigOrError.set(
|
||||
ConfigOrError.fromConfig(managedChannelServiceConfig));
|
||||
nameResolverFactory.allResolved();
|
||||
|
||||
resultCaptor = ArgumentCaptor.forClass(ResolvedAddresses.class);
|
||||
|
|
@ -3795,7 +3826,7 @@ public class ManagedChannelImplTest {
|
|||
assertThat(resultCaptor.getValue().getAddresses()).containsExactly(addressGroup);
|
||||
actualAttrs = resultCaptor.getValue().getAttributes();
|
||||
assertThat(actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG))
|
||||
.isEqualTo(serviceConfig);
|
||||
.isEqualTo(rawServiceConfig);
|
||||
verify(mockLoadBalancer, never()).handleNameResolutionError(any(Status.class));
|
||||
} finally {
|
||||
LoadBalancerRegistry.getDefaultRegistry().deregister(mockLoadBalancerProvider);
|
||||
|
|
@ -3816,15 +3847,15 @@ public class ManagedChannelImplTest {
|
|||
+ "\"waitForReady\":true}]}");
|
||||
channelBuilder.defaultServiceConfig(defaultServiceConfig);
|
||||
|
||||
Map<String, Object> serviceConfig =
|
||||
Map<String, Object> rawServiceConfig =
|
||||
parseConfig("{\"methodConfig\":[{"
|
||||
+ "\"name\":[{\"service\":\"SimpleService2\"}],"
|
||||
+ "\"waitForReady\":false}]}");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
nameResolverFactory.nextConfigOrError.set(
|
||||
ConfigOrError.fromConfig(managedChannelServiceConfig));
|
||||
|
||||
createChannel();
|
||||
ArgumentCaptor<ResolvedAddresses> resultCaptor =
|
||||
|
|
@ -3833,7 +3864,7 @@ public class ManagedChannelImplTest {
|
|||
assertThat(resultCaptor.getValue().getAddresses()).containsExactly(addressGroup);
|
||||
Attributes actualAttrs = resultCaptor.getValue().getAttributes();
|
||||
assertThat(actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG))
|
||||
.isEqualTo(serviceConfig);
|
||||
.isEqualTo(rawServiceConfig);
|
||||
verify(mockLoadBalancer, never()).handleNameResolutionError(any(Status.class));
|
||||
} finally {
|
||||
LoadBalancerRegistry.getDefaultRegistry().deregister(mockLoadBalancerProvider);
|
||||
|
|
@ -3855,8 +3886,8 @@ public class ManagedChannelImplTest {
|
|||
+ "\"waitForReady\":true}]}");
|
||||
channelBuilder.defaultServiceConfig(defaultServiceConfig);
|
||||
|
||||
Attributes serviceConfigAttrs = Attributes.EMPTY;
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
nameResolverFactory.nextRawServiceConfig.set(null);
|
||||
nameResolverFactory.nextConfigOrError.set(null);
|
||||
|
||||
createChannel();
|
||||
ArgumentCaptor<ResolvedAddresses> resultCaptor =
|
||||
|
|
@ -3881,8 +3912,12 @@ public class ManagedChannelImplTest {
|
|||
.setServers(ImmutableList.of(addressGroup)).build();
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
|
||||
Attributes serviceConfigAttrs = Attributes.EMPTY;
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
Map<String, Object> rawServiceConfig = Collections.emptyMap();
|
||||
ManagedChannelServiceConfig managedChannelServiceConfig =
|
||||
createManagedChannelServiceConfig(rawServiceConfig, null);
|
||||
nameResolverFactory.nextRawServiceConfig.set(rawServiceConfig);
|
||||
nameResolverFactory.nextConfigOrError.set(
|
||||
ConfigOrError.fromConfig(managedChannelServiceConfig));
|
||||
|
||||
createChannel();
|
||||
ArgumentCaptor<ResolvedAddresses> resultCaptor =
|
||||
|
|
@ -3890,7 +3925,7 @@ public class ManagedChannelImplTest {
|
|||
verify(mockLoadBalancer).handleResolvedAddresses(resultCaptor.capture());
|
||||
assertThat(resultCaptor.getValue().getAddresses()).containsExactly(addressGroup);
|
||||
Attributes actualAttrs = resultCaptor.getValue().getAttributes();
|
||||
assertThat(actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG)).isNull();
|
||||
assertThat(actualAttrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG)).isEmpty();
|
||||
verify(mockLoadBalancer, never()).handleNameResolutionError(any(Status.class));
|
||||
} finally {
|
||||
LoadBalancerRegistry.getDefaultRegistry().deregister(mockLoadBalancerProvider);
|
||||
|
|
@ -3983,10 +4018,9 @@ public class ManagedChannelImplTest {
|
|||
final List<EquivalentAddressGroup> servers;
|
||||
final boolean resolvedAtStart;
|
||||
final Status error;
|
||||
final ArrayList<FakeNameResolver> resolvers = new ArrayList<>();
|
||||
// The Attributes argument of the next invocation of listener.onAddresses(servers, attrs)
|
||||
final AtomicReference<Attributes> nextResolvedAttributes =
|
||||
new AtomicReference<>(Attributes.EMPTY);
|
||||
final ArrayList<FakeNameResolverFactory.FakeNameResolver> resolvers = new ArrayList<>();
|
||||
final AtomicReference<ConfigOrError> nextConfigOrError = new AtomicReference<>();
|
||||
final AtomicReference<Map<String, ?>> nextRawServiceConfig = new AtomicReference<>();
|
||||
|
||||
FakeNameResolverFactory(
|
||||
URI expectedUri,
|
||||
|
|
@ -4005,7 +4039,8 @@ public class ManagedChannelImplTest {
|
|||
return null;
|
||||
}
|
||||
assertEquals(DEFAULT_PORT, args.getDefaultPort());
|
||||
FakeNameResolver resolver = new FakeNameResolver(error);
|
||||
FakeNameResolverFactory.FakeNameResolver resolver =
|
||||
new FakeNameResolverFactory.FakeNameResolver(error);
|
||||
resolvers.add(resolver);
|
||||
return resolver;
|
||||
}
|
||||
|
|
@ -4016,7 +4051,7 @@ public class ManagedChannelImplTest {
|
|||
}
|
||||
|
||||
void allResolved() {
|
||||
for (FakeNameResolver resolver : resolvers) {
|
||||
for (FakeNameResolverFactory.FakeNameResolver resolver : resolvers) {
|
||||
resolver.resolved();
|
||||
}
|
||||
}
|
||||
|
|
@ -4052,11 +4087,22 @@ public class ManagedChannelImplTest {
|
|||
listener.onError(error);
|
||||
return;
|
||||
}
|
||||
listener.onResult(
|
||||
ResolutionResult.Builder builder =
|
||||
ResolutionResult.newBuilder()
|
||||
.setAddresses(servers)
|
||||
.setAttributes(nextResolvedAttributes.get())
|
||||
.build());
|
||||
.setAddresses(servers);
|
||||
ConfigOrError configOrError = nextConfigOrError.get();
|
||||
Map<String, ?> rawServiceConfig = nextRawServiceConfig.get();
|
||||
if (configOrError != null) {
|
||||
builder.setServiceConfig(configOrError);
|
||||
}
|
||||
if (rawServiceConfig != null) {
|
||||
builder.setAttributes(
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, rawServiceConfig)
|
||||
.build());
|
||||
}
|
||||
|
||||
listener.onResult(builder.build());
|
||||
}
|
||||
|
||||
@Override public void shutdown() {
|
||||
|
|
@ -4079,17 +4125,17 @@ public class ManagedChannelImplTest {
|
|||
this.expectedUri = expectedUri;
|
||||
}
|
||||
|
||||
Builder setServers(List<EquivalentAddressGroup> servers) {
|
||||
FakeNameResolverFactory.Builder setServers(List<EquivalentAddressGroup> servers) {
|
||||
this.servers = servers;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setResolvedAtStart(boolean resolvedAtStart) {
|
||||
FakeNameResolverFactory.Builder setResolvedAtStart(boolean resolvedAtStart) {
|
||||
this.resolvedAtStart = resolvedAtStart;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setError(Status error) {
|
||||
FakeNameResolverFactory.Builder setError(Status error) {
|
||||
this.error = error;
|
||||
return this;
|
||||
}
|
||||
|
|
@ -4207,4 +4253,11 @@ public class ManagedChannelImplTest {
|
|||
private static Map<String, Object> parseConfig(String json) throws Exception {
|
||||
return (Map<String, Object>) JsonParser.parse(json);
|
||||
}
|
||||
|
||||
private static ManagedChannelServiceConfig createManagedChannelServiceConfig(
|
||||
Map<String, Object> rawServiceConfig, PolicySelection policySelection) {
|
||||
// Provides dummy variable for retry related params (not used in this test class)
|
||||
return ManagedChannelServiceConfig
|
||||
.fromServiceConfig(rawServiceConfig, true, 3, 4, policySelection);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -62,10 +62,17 @@ public class RetryPolicyTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> serviceConfig = (Map<String, ?>) serviceConfigObj;
|
||||
|
||||
ServiceConfigInterceptor serviceConfigInterceptor = new ServiceConfigInterceptor(
|
||||
/* retryEnabled = */ true, /* maxRetryAttemptsLimit = */ 4,
|
||||
/* maxHedgedAttemptsLimit = */ 3);
|
||||
serviceConfigInterceptor.handleUpdate(serviceConfig);
|
||||
ServiceConfigInterceptor serviceConfigInterceptor =
|
||||
new ServiceConfigInterceptor(/* retryEnabled= */ true);
|
||||
serviceConfigInterceptor
|
||||
.handleUpdate(
|
||||
ManagedChannelServiceConfig
|
||||
.fromServiceConfig(
|
||||
serviceConfig,
|
||||
/* retryEnabled= */ true,
|
||||
/* maxRetryAttemptsLimit= */ 4,
|
||||
/* maxHedgedAttemptsLimit= */ 3,
|
||||
/* loadBalancingConfig= */ null));
|
||||
|
||||
MethodDescriptor.Builder<Void, Void> builder = TestMethodDescriptors.voidMethod().toBuilder();
|
||||
|
||||
|
|
@ -140,10 +147,17 @@ public class RetryPolicyTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> serviceConfig = (Map<String, ?>) serviceConfigObj;
|
||||
|
||||
ServiceConfigInterceptor serviceConfigInterceptor = new ServiceConfigInterceptor(
|
||||
/* retryEnabled = */ false, /* maxRetryAttemptsLimit = */ 4,
|
||||
/* maxHedgedAttemptsLimit = */ 3);
|
||||
serviceConfigInterceptor.handleUpdate(serviceConfig);
|
||||
ServiceConfigInterceptor serviceConfigInterceptor =
|
||||
new ServiceConfigInterceptor(/* retryEnabled= */ false);
|
||||
serviceConfigInterceptor
|
||||
.handleUpdate(
|
||||
ManagedChannelServiceConfig
|
||||
.fromServiceConfig(
|
||||
serviceConfig,
|
||||
/* retryEnabled= */ false,
|
||||
/* maxRetryAttemptsLimit= */ 4,
|
||||
/* maxHedgedAttemptsLimit= */ 3,
|
||||
/* loadBalancingConfig= */ null));
|
||||
|
||||
MethodDescriptor.Builder<Void, Void> builder = TestMethodDescriptors.voidMethod().toBuilder();
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ public class ServiceConfigErrorHandlingTest {
|
|||
@Override
|
||||
public boolean shouldAccept(Runnable command) {
|
||||
return command.toString().contains(
|
||||
ManagedChannelImpl2.DelayedNameResolverRefresh.class.getName());
|
||||
ManagedChannelImpl.DelayedNameResolverRefresh.class.getName());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ public class ServiceConfigErrorHandlingTest {
|
|||
@Rule public final ExpectedException thrown = ExpectedException.none();
|
||||
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
|
||||
|
||||
private ManagedChannelImpl2 channel;
|
||||
private ManagedChannelImpl channel;
|
||||
private final AtomicReference<Status> nextLbPolicyConfigError = new AtomicReference<>();
|
||||
|
||||
private FakeLoadBalancer mockLoadBalancer =
|
||||
|
|
@ -157,7 +157,7 @@ public class ServiceConfigErrorHandlingTest {
|
|||
checkState(channel == null);
|
||||
|
||||
channel =
|
||||
new ManagedChannelImpl2(
|
||||
new ManagedChannelImpl(
|
||||
channelBuilder,
|
||||
mockTransportFactory,
|
||||
new FakeBackoffPolicyProvider(),
|
||||
|
|
@ -175,7 +175,7 @@ public class ServiceConfigErrorHandlingTest {
|
|||
channel.exitIdleMode();
|
||||
}
|
||||
});
|
||||
if (channelBuilder.idleTimeoutMillis != ManagedChannelImpl2.IDLE_TIMEOUT_MILLIS_DISABLE) {
|
||||
if (channelBuilder.idleTimeoutMillis != ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE) {
|
||||
numExpectedTasks += 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ import org.mockito.MockitoAnnotations;
|
|||
/**
|
||||
* Unit tests for {@link ServiceConfigInterceptor}.
|
||||
*/
|
||||
@Deprecated // migrate to ServiceConfigInterceptor(Test)?2
|
||||
@RunWith(JUnit4.class)
|
||||
public class ServiceConfigInterceptorTest {
|
||||
|
||||
|
|
@ -61,8 +60,8 @@ public class ServiceConfigInterceptorTest {
|
|||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
private final ServiceConfigInterceptor interceptor = new ServiceConfigInterceptor(
|
||||
/* retryEnabled = */ true, /* maxRetryAttemptsLimit = */ 5, /* maxHedgedAttemptsLimit = */ 6);
|
||||
private final ServiceConfigInterceptor interceptor =
|
||||
new ServiceConfigInterceptor(/* retryEnabled = */ true);
|
||||
|
||||
private final String fullMethodName =
|
||||
MethodDescriptor.generateFullMethodName("service", "method");
|
||||
|
|
@ -72,8 +71,6 @@ public class ServiceConfigInterceptorTest {
|
|||
.setFullMethodName(fullMethodName)
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
private static final class JsonObj extends HashMap<String, Object> {
|
||||
private JsonObj(Object ... kv) {
|
||||
for (int i = 0; i < kv.length; i += 2) {
|
||||
|
|
@ -93,8 +90,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "waitForReady", true);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT.withoutWaitForReady(), channel);
|
||||
|
||||
|
|
@ -108,7 +107,7 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "waitForReady", true);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(createManagedChannelServiceConfig(serviceConfig));
|
||||
interceptor.handleUpdate(null);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT.withoutWaitForReady(), channel);
|
||||
|
|
@ -134,8 +133,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxRequestMessageBytes", 1d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
|
||||
|
|
@ -148,8 +149,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxRequestMessageBytes", 10d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withMaxOutboundMessageSize(5), channel);
|
||||
|
|
@ -163,8 +166,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxRequestMessageBytes", 5d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withMaxOutboundMessageSize(10), channel);
|
||||
|
|
@ -178,8 +183,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxResponseMessageBytes", 1d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
|
||||
|
|
@ -192,8 +199,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxResponseMessageBytes", 5d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withMaxInboundMessageSize(10), channel);
|
||||
|
|
@ -207,8 +216,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxResponseMessageBytes", 10d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withMaxInboundMessageSize(5), channel);
|
||||
|
|
@ -222,8 +233,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "waitForReady", false);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT.withWaitForReady(), channel);
|
||||
|
||||
|
|
@ -241,8 +254,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj methodConfig2 = new JsonObj("name", new JsonList(name2), "timeout", "1s");
|
||||
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig1, methodConfig2));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
|
||||
|
|
@ -255,8 +270,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "timeout", "100000s");
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
Deadline existingDeadline = Deadline.after(1000, TimeUnit.NANOSECONDS);
|
||||
interceptor.interceptCall(
|
||||
|
|
@ -273,8 +290,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "timeout", "1s");
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
Deadline existingDeadline = Deadline.after(1234567890, TimeUnit.NANOSECONDS);
|
||||
interceptor.interceptCall(
|
||||
|
|
@ -284,7 +303,6 @@ public class ServiceConfigInterceptorTest {
|
|||
assertThat(callOptionsCap.getValue().getDeadline()).isNotEqualTo(existingDeadline);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnMissingServiceName() {
|
||||
JsonObj name = new JsonObj("method", "method");
|
||||
|
|
@ -294,9 +312,11 @@ public class ServiceConfigInterceptorTest {
|
|||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("missing service");
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
}
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnDuplicateMethod() {
|
||||
|
|
@ -308,7 +328,10 @@ public class ServiceConfigInterceptorTest {
|
|||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate method");
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -319,7 +342,10 @@ public class ServiceConfigInterceptorTest {
|
|||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("no names in method config");
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -332,7 +358,10 @@ public class ServiceConfigInterceptorTest {
|
|||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate service");
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -346,7 +375,10 @@ public class ServiceConfigInterceptorTest {
|
|||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate service");
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -358,13 +390,17 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name2 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj methodConfig2 = new JsonObj("name", new JsonList(name2));
|
||||
JsonObj serviceConfig2 = new JsonObj("methodConfig", new JsonList(methodConfig2));
|
||||
ManagedChannelServiceConfig parsedServiceConfig1 =
|
||||
createManagedChannelServiceConfig(serviceConfig1);
|
||||
ManagedChannelServiceConfig parsedServiceConfig2 =
|
||||
createManagedChannelServiceConfig(serviceConfig2);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig1);
|
||||
interceptor.handleUpdate(parsedServiceConfig1);
|
||||
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isNotEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isEmpty();
|
||||
|
||||
interceptor.handleUpdate(serviceConfig2);
|
||||
interceptor.handleUpdate(parsedServiceConfig2);
|
||||
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isNotEmpty();
|
||||
|
|
@ -376,8 +412,10 @@ public class ServiceConfigInterceptorTest {
|
|||
JsonObj name2 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name1, name2));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap())
|
||||
.containsExactly(
|
||||
|
|
@ -387,7 +425,6 @@ public class ServiceConfigInterceptorTest {
|
|||
"service2", new MethodInfo(methodConfig, false, 1, 1));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void methodInfo_validateDeadline() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
|
|
@ -408,7 +445,6 @@ public class ServiceConfigInterceptorTest {
|
|||
assertThat(info.timeoutNanos).isEqualTo(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void methodInfo_badMaxRequestSize() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
|
|
@ -431,6 +467,17 @@ public class ServiceConfigInterceptorTest {
|
|||
new MethodInfo(methodConfig, false, 1, 1);
|
||||
}
|
||||
|
||||
private static ManagedChannelServiceConfig createManagedChannelServiceConfig(
|
||||
JsonObj rawServiceConfig) {
|
||||
// current tests doesn't use any other values except rawServiceConfig, so provide dummy values.
|
||||
return ManagedChannelServiceConfig.fromServiceConfig(
|
||||
rawServiceConfig,
|
||||
/* retryEnabled= */ true,
|
||||
/* maxRetryAttemptsLimit= */ 3,
|
||||
/* maxHedgedAttemptsLimit= */ 4,
|
||||
/* loadBalancingConfig= */ null);
|
||||
}
|
||||
|
||||
private static final class NoopMarshaller implements MethodDescriptor.Marshaller<Void> {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,493 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static io.grpc.internal.ServiceConfigInterceptor2.HEDGING_POLICY_KEY;
|
||||
import static io.grpc.internal.ServiceConfigInterceptor2.RETRY_POLICY_KEY;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.Deadline;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.MethodDescriptor.MethodType;
|
||||
import io.grpc.internal.ManagedChannelServiceConfig2.MethodInfo;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ServiceConfigInterceptor2}.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class ServiceConfigInterceptorTest2 {
|
||||
|
||||
@Rule public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Mock private Channel channel;
|
||||
@Captor private ArgumentCaptor<CallOptions> callOptionsCap;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
private final ServiceConfigInterceptor2 interceptor =
|
||||
new ServiceConfigInterceptor2(/* retryEnabled = */ true);
|
||||
|
||||
private final String fullMethodName =
|
||||
MethodDescriptor.generateFullMethodName("service", "method");
|
||||
private final MethodDescriptor<Void, Void> methodDescriptor =
|
||||
MethodDescriptor.newBuilder(new NoopMarshaller(), new NoopMarshaller())
|
||||
.setType(MethodType.UNARY)
|
||||
.setFullMethodName(fullMethodName)
|
||||
.build();
|
||||
|
||||
private static final class JsonObj extends HashMap<String, Object> {
|
||||
private JsonObj(Object ... kv) {
|
||||
for (int i = 0; i < kv.length; i += 2) {
|
||||
put((String) kv[i], kv[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class JsonList extends ArrayList<Object> {
|
||||
private JsonList(Object ... values) {
|
||||
addAll(Arrays.asList(values));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withWaitForReady() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "waitForReady", true);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT.withoutWaitForReady(), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().isWaitForReady()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleNullConfig() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "waitForReady", true);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
interceptor.handleUpdate(createManagedChannelServiceConfig(serviceConfig));
|
||||
interceptor.handleUpdate(null);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT.withoutWaitForReady(), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().isWaitForReady()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdateNotCalledBeforeInterceptCall() {
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT.withoutWaitForReady(), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().isWaitForReady()).isFalse();
|
||||
assertThat(callOptionsCap.getValue().getOption(RETRY_POLICY_KEY).get())
|
||||
.isEqualTo(RetryPolicy.DEFAULT);
|
||||
assertThat(callOptionsCap.getValue().getOption(HEDGING_POLICY_KEY).get())
|
||||
.isEqualTo(HedgingPolicy.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withMaxRequestSize() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxRequestMessageBytes", 1d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxOutboundMessageSize()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withMaxRequestSize_pickSmallerExisting() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxRequestMessageBytes", 10d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withMaxOutboundMessageSize(5), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxOutboundMessageSize()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withMaxRequestSize_pickSmallerNew() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxRequestMessageBytes", 5d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withMaxOutboundMessageSize(10), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxOutboundMessageSize()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withMaxResponseSize() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxResponseMessageBytes", 1d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxInboundMessageSize()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withMaxResponseSize_pickSmallerExisting() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxResponseMessageBytes", 5d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withMaxInboundMessageSize(10), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxInboundMessageSize()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withMaxResponseSize_pickSmallerNew() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxResponseMessageBytes", 10d);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withMaxInboundMessageSize(5), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxInboundMessageSize()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withoutWaitForReady() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "waitForReady", false);
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT.withWaitForReady(), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().isWaitForReady()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullMethodMatched() {
|
||||
// Put in service that matches, but has no deadline. It should be lower priority
|
||||
JsonObj name1 = new JsonObj("service", "service");
|
||||
JsonObj methodConfig1 = new JsonObj("name", new JsonList(name1));
|
||||
|
||||
JsonObj name2 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj methodConfig2 = new JsonObj("name", new JsonList(name2), "timeout", "1s");
|
||||
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig1, methodConfig2));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
interceptor.interceptCall(methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getDeadline()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nearerDeadlineKept_existing() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "timeout", "100000s");
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
Deadline existingDeadline = Deadline.after(1000, TimeUnit.NANOSECONDS);
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withDeadline(existingDeadline), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getDeadline()).isEqualTo(existingDeadline);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nearerDeadlineKept_new() {
|
||||
// TODO(carl-mastrangelo): the deadlines are very large because they change over time.
|
||||
// This should be fixed, and is tracked in https://github.com/grpc/grpc-java/issues/2531
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "timeout", "1s");
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
Deadline existingDeadline = Deadline.after(1234567890, TimeUnit.NANOSECONDS);
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT.withDeadline(existingDeadline), channel);
|
||||
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getDeadline()).isNotEqualTo(existingDeadline);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnMissingServiceName() {
|
||||
JsonObj name = new JsonObj("method", "method");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("missing service");
|
||||
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnDuplicateMethod() {
|
||||
JsonObj name1 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj name2 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name1, name2));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate method");
|
||||
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnEmptyName() {
|
||||
JsonObj methodConfig = new JsonObj();
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("no names in method config");
|
||||
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnDuplicateService() {
|
||||
JsonObj name1 = new JsonObj("service", "service");
|
||||
JsonObj name2 = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name1, name2));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate service");
|
||||
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnDuplicateServiceMultipleConfig() {
|
||||
JsonObj name1 = new JsonObj("service", "service");
|
||||
JsonObj name2 = new JsonObj("service", "service");
|
||||
JsonObj methodConfig1 = new JsonObj("name", new JsonList(name1));
|
||||
JsonObj methodConfig2 = new JsonObj("name", new JsonList(name2));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig1, methodConfig2));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate service");
|
||||
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_replaceExistingConfig() {
|
||||
JsonObj name1 = new JsonObj("service", "service");
|
||||
JsonObj methodConfig1 = new JsonObj("name", new JsonList(name1));
|
||||
JsonObj serviceConfig1 = new JsonObj("methodConfig", new JsonList(methodConfig1));
|
||||
|
||||
JsonObj name2 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj methodConfig2 = new JsonObj("name", new JsonList(name2));
|
||||
JsonObj serviceConfig2 = new JsonObj("methodConfig", new JsonList(methodConfig2));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig1 =
|
||||
createManagedChannelServiceConfig(serviceConfig1);
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig2 =
|
||||
createManagedChannelServiceConfig(serviceConfig2);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig1);
|
||||
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isNotEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isEmpty();
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig2);
|
||||
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_matchNames() {
|
||||
JsonObj name1 = new JsonObj("service", "service2");
|
||||
JsonObj name2 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name1, name2));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
ManagedChannelServiceConfig2 parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap())
|
||||
.containsExactly(
|
||||
methodDescriptor.getFullMethodName(),
|
||||
new MethodInfo(methodConfig, false, 1, 1));
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).containsExactly(
|
||||
"service2", new MethodInfo(methodConfig, false, 1, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodInfo_validateDeadline() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "timeout", "10000000000000000s");
|
||||
|
||||
thrown.expectMessage("Duration value is out of range");
|
||||
|
||||
new MethodInfo(methodConfig, false, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodInfo_saturateDeadline() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "timeout", "315576000000s");
|
||||
|
||||
MethodInfo info = new MethodInfo(methodConfig, false, 1, 1);
|
||||
|
||||
assertThat(info.timeoutNanos).isEqualTo(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodInfo_badMaxRequestSize() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxRequestMessageBytes", -1d);
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("exceeds bounds");
|
||||
|
||||
new MethodInfo(methodConfig, false, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodInfo_badMaxResponseSize() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name), "maxResponseMessageBytes", -1d);
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("exceeds bounds");
|
||||
|
||||
new MethodInfo(methodConfig, false, 1, 1);
|
||||
}
|
||||
|
||||
private static ManagedChannelServiceConfig2 createManagedChannelServiceConfig(
|
||||
JsonObj rawServiceConfig) {
|
||||
// current tests doesn't use any other values except rawServiceConfig, so provide dummy values.
|
||||
return ManagedChannelServiceConfig2.fromServiceConfig(
|
||||
rawServiceConfig,
|
||||
/* retryEnabled= */ true,
|
||||
/* maxRetryAttemptsLimit= */ 3,
|
||||
/* maxHedgedAttemptsLimit= */ 4,
|
||||
/* loadBalancingConfig= */ null);
|
||||
}
|
||||
|
||||
private static final class NoopMarshaller implements MethodDescriptor.Marshaller<Void> {
|
||||
|
||||
@Override
|
||||
public InputStream stream(Void value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void parse(InputStream stream) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue