core: promote ServiceConfigErrorHandling (#6633)

This commit is contained in:
Jihun Cho 2020-01-23 17:59:27 -08:00 committed by GitHub
parent 5b7f5b8c3b
commit 751faa6faa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 899 additions and 9838 deletions

View File

@ -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(),

View File

@ -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 {

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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.
*/

View File

@ -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));
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();

View File

@ -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

View File

@ -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));
}
});
}
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}
}