From 751faa6faa3fbccf46af74c6597218438f39c2ca Mon Sep 17 00:00:00 2001
From: Jihun Cho
Date: Thu, 23 Jan 2020 17:59:27 -0800
Subject: [PATCH] core: promote ServiceConfigErrorHandling (#6633)
---
.../AbstractManagedChannelImplBuilder.java | 30 -
.../AutoConfiguredLoadBalancerFactory.java | 215 +-
.../AutoConfiguredLoadBalancerFactory2.java | 447 --
.../io/grpc/internal/ManagedChannelImpl.java | 225 +-
.../io/grpc/internal/ManagedChannelImpl2.java | 2007 --------
.../internal/ManagedChannelServiceConfig.java | 47 +-
.../ManagedChannelServiceConfig2.java | 322 --
.../internal/ServiceConfigInterceptor.java | 26 +-
.../internal/ServiceConfigInterceptor2.java | 199 -
...AbstractManagedChannelImplBuilderTest.java | 14 -
...AutoConfiguredLoadBalancerFactoryTest.java | 467 +-
...utoConfiguredLoadBalancerFactoryTest2.java | 981 ----
.../io/grpc/internal/HedgingPolicyTest.java | 30 +-
.../ManagedChannelImplIdlenessTest.java | 11 +-
.../ManagedChannelImplIdlenessTest2.java | 560 ---
.../grpc/internal/ManagedChannelImplTest.java | 257 +-
.../internal/ManagedChannelImplTest2.java | 4263 -----------------
.../io/grpc/internal/RetryPolicyTest.java | 30 +-
.../ServiceConfigErrorHandlingTest.java | 8 +-
.../ServiceConfigInterceptorTest.java | 105 +-
.../ServiceConfigInterceptorTest2.java | 493 --
21 files changed, 899 insertions(+), 9838 deletions(-)
delete mode 100644 core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory2.java
delete mode 100644 core/src/main/java/io/grpc/internal/ManagedChannelImpl2.java
delete mode 100644 core/src/main/java/io/grpc/internal/ManagedChannelServiceConfig2.java
delete mode 100644 core/src/main/java/io/grpc/internal/ServiceConfigInterceptor2.java
delete mode 100644 core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest2.java
delete mode 100644 core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest2.java
delete mode 100644 core/src/test/java/io/grpc/internal/ManagedChannelImplTest2.java
delete mode 100644 core/src/test/java/io/grpc/internal/ServiceConfigInterceptorTest2.java
diff --git a/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java b/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java
index 507b5d185f..6bc54d62d0 100644
--- a/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java
+++ b/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java
@@ -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(),
diff --git a/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java b/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
index ac926693f8..263a20d4a8 100644
--- a/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
+++ b/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
@@ -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 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 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:
- *
- *
- * User provided lb on the channel. This is a degenerate case and not handled here.
- * This options is deprecated.
- * Policy from "loadBalancingConfig" if present. This is not covered here.
- * "grpclb" if any gRPC LB balancer addresses are present
- * The policy from "loadBalancingPolicy" if present
- * "pick_first" if the service config choice does not specify
- *
+ * 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 servers, @Nullable Map config)
+ ResolvedPolicySelection resolveLoadBalancerProvider(
+ List servers, @Nullable PolicySelection policySelection)
throws PolicyException {
// Check for balancer addresses
boolean haveBalancerAddress = false;
@@ -229,36 +231,10 @@ public final class AutoConfiguredLoadBalancerFactory {
}
}
- List lbConfigs = null;
- if (config != null) {
- List> rawLbConfigs =
- ServiceConfigUtil.getLoadBalancingConfigsFromServiceConfig(config);
- lbConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList(rawLbConfigs);
- }
- if (lbConfigs != null && !lbConfigs.isEmpty()) {
- LinkedHashSet 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.
+ *
+ * Order of policy preference:
+ *
+ *
+ * Policy from "loadBalancingConfig" if present
+ * The policy from deprecated "loadBalancingPolicy" if present
+ *
+ *
+ *
+ * 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 serviceConfig) {
+ ConfigOrError parseLoadBalancerPolicy(Map serviceConfig, ChannelLogger channelLogger) {
try {
List 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 serverList;
- // TODO(carl-mastrangelo): make this the non-raw service config object.
- @Nullable final Map config;
-
- PolicySelection(
- LoadBalancerProvider provider, List serverList,
- @Nullable Map config) {
- this.provider = checkNotNull(provider, "provider");
- this.serverList = Collections.unmodifiableList(checkNotNull(serverList, "serverList"));
- this.config = config;
- }
+ @Nullable final Map rawConfig;
+ @Nullable final Object config;
PolicySelection(
LoadBalancerProvider provider,
- @Nullable Map config) {
+ @Nullable Map 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 serverList;
+
+ ResolvedPolicySelection(
+ PolicySelection policySelection, List 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 {
diff --git a/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory2.java b/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory2.java
deleted file mode 100644
index d463293659..0000000000
--- a/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory2.java
+++ /dev/null
@@ -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 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 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 servers, @Nullable PolicySelection policySelection)
- throws PolicyException {
- // Check for balancer addresses
- boolean haveBalancerAddress = false;
- List 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.
- *
- * Order of policy preference:
- *
- *
- * Policy from "loadBalancingConfig" if present
- * The policy from deprecated "loadBalancingPolicy" if present
- *
- *
- *
- * 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 serviceConfig, ChannelLogger channelLogger) {
- try {
- List loadBalancerConfigs = null;
- if (serviceConfig != null) {
- List> rawLbConfigs =
- ServiceConfigUtil.getLoadBalancingConfigsFromServiceConfig(serviceConfig);
- loadBalancerConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList(rawLbConfigs);
- }
- if (loadBalancerConfigs != null && !loadBalancerConfigs.isEmpty()) {
- List 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 rawConfig;
- @Nullable final Object config;
-
- PolicySelection(
- LoadBalancerProvider provider,
- @Nullable Map 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 serverList;
-
- ResolvedPolicySelection(
- PolicySelection policySelection, List 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);
- }
- }
-}
diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
index 86def9eb0b..062acc3a59 100644
--- a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
+++ b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
@@ -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.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 lastServiceConfig; // used for channel tracing when value changed
- @Nullable
- private final Map 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 {
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 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 serviceConfig =
- attrs.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG);
- Map 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 rawServiceConfig;
+ ManagedChannelServiceConfig managedChannelServiceConfig;
+
+ ServiceConfigHolder(
+ Map 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();
+ }
+ }
}
diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImpl2.java b/core/src/main/java/io/grpc/internal/ManagedChannelImpl2.java
deleted file mode 100644
index d2444f1713..0000000000
--- a/core/src/main/java/io/grpc/internal/ManagedChannelImpl2.java
+++ /dev/null
@@ -1,2007 +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.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static io.grpc.ConnectivityState.IDLE;
-import static io.grpc.ConnectivityState.SHUTDOWN;
-import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
-import static io.grpc.internal.ServiceConfigInterceptor2.HEDGING_POLICY_KEY;
-import static io.grpc.internal.ServiceConfigInterceptor2.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;
-import com.google.common.util.concurrent.SettableFuture;
-import io.grpc.Attributes;
-import io.grpc.CallOptions;
-import io.grpc.Channel;
-import io.grpc.ChannelLogger;
-import io.grpc.ChannelLogger.ChannelLogLevel;
-import io.grpc.ClientCall;
-import io.grpc.ClientInterceptor;
-import io.grpc.ClientInterceptors;
-import io.grpc.ClientStreamTracer;
-import io.grpc.CompressorRegistry;
-import io.grpc.ConnectivityState;
-import io.grpc.ConnectivityStateInfo;
-import io.grpc.Context;
-import io.grpc.DecompressorRegistry;
-import io.grpc.EquivalentAddressGroup;
-import io.grpc.InternalChannelz;
-import io.grpc.InternalChannelz.ChannelStats;
-import io.grpc.InternalChannelz.ChannelTrace;
-import io.grpc.InternalInstrumented;
-import io.grpc.InternalLogId;
-import io.grpc.InternalWithLogId;
-import io.grpc.LoadBalancer;
-import io.grpc.LoadBalancer.CreateSubchannelArgs;
-import io.grpc.LoadBalancer.PickResult;
-import io.grpc.LoadBalancer.PickSubchannelArgs;
-import io.grpc.LoadBalancer.ResolvedAddresses;
-import io.grpc.LoadBalancer.SubchannelPicker;
-import io.grpc.LoadBalancer.SubchannelStateListener;
-import io.grpc.ManagedChannel;
-import io.grpc.Metadata;
-import io.grpc.MethodDescriptor;
-import io.grpc.NameResolver;
-import io.grpc.NameResolver.ConfigOrError;
-import io.grpc.NameResolver.ResolutionResult;
-import io.grpc.NameResolverRegistry;
-import io.grpc.ProxyDetector;
-import io.grpc.Status;
-import io.grpc.SynchronizationContext;
-import io.grpc.SynchronizationContext.ScheduledHandle;
-import io.grpc.internal.AutoConfiguredLoadBalancerFactory2.AutoConfiguredLoadBalancer;
-import io.grpc.internal.ClientCallImpl.ClientTransportProvider;
-import io.grpc.internal.RetriableStream.ChannelBufferMeter;
-import io.grpc.internal.RetriableStream.Throttle;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.ThreadSafe;
-
-/** A communication channel for making outgoing RPCs. */
-@ThreadSafe
-final class ManagedChannelImpl2 extends ManagedChannel implements
- InternalInstrumented {
- static final Logger logger = Logger.getLogger(ManagedChannelImpl2.class.getName());
-
- // Matching this pattern means the target string is a URI target or at least intended to be one.
- // A URI target must be an absolute hierarchical URI.
- // From RFC 2396: scheme = alpha *( alpha | digit | "+" | "-" | "." )
- @VisibleForTesting
- static final Pattern URI_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9+.-]*:/.*");
-
- static final long IDLE_TIMEOUT_MILLIS_DISABLE = -1;
-
- @VisibleForTesting
- static final long SUBCHANNEL_SHUTDOWN_DELAY_SECONDS = 5;
-
- @VisibleForTesting
- static final Status SHUTDOWN_NOW_STATUS =
- Status.UNAVAILABLE.withDescription("Channel shutdownNow invoked");
-
- @VisibleForTesting
- static final Status SHUTDOWN_STATUS =
- Status.UNAVAILABLE.withDescription("Channel shutdown invoked");
-
- @VisibleForTesting
- static final Status SUBCHANNEL_SHUTDOWN_STATUS =
- Status.UNAVAILABLE.withDescription("Subchannel shutdown invoked");
-
- private static final ServiceConfigHolder EMPTY_SERVICE_CONFIG =
- new ServiceConfigHolder(
- Collections.emptyMap(),
- ManagedChannelServiceConfig2.empty());
-
- private final InternalLogId logId;
- private final String target;
- private final NameResolverRegistry nameResolverRegistry;
- private final NameResolver.Factory nameResolverFactory;
- private final NameResolver.Args nameResolverArgs;
- private final AutoConfiguredLoadBalancerFactory2 loadBalancerFactory;
- private final ClientTransportFactory transportFactory;
- private final RestrictedScheduledExecutor scheduledExecutor;
- private final Executor executor;
- private final ObjectPool extends Executor> executorPool;
- private final ObjectPool extends Executor> balancerRpcExecutorPool;
- private final ExecutorHolder balancerRpcExecutorHolder;
- private final ExecutorHolder offloadExecutorHolder;
- private final TimeProvider timeProvider;
- private final int maxTraceEvents;
-
- @VisibleForTesting
- final SynchronizationContext syncContext = new SynchronizationContext(
- new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread t, Throwable e) {
- logger.log(
- Level.SEVERE,
- "[" + getLogId() + "] Uncaught exception in the SynchronizationContext. Panic!",
- e);
- panic(e);
- }
- });
-
- private boolean fullStreamDecompression;
-
- private final DecompressorRegistry decompressorRegistry;
- private final CompressorRegistry compressorRegistry;
-
- private final Supplier stopwatchSupplier;
- /** The timout before entering idle mode. */
- private final long idleTimeoutMillis;
-
- private final ConnectivityStateManager channelStateManager = new ConnectivityStateManager();
-
- private final ServiceConfigInterceptor2 serviceConfigInterceptor;
-
- private final BackoffPolicy.Provider backoffPolicyProvider;
-
- /**
- * We delegate to this channel, so that we can have interceptors as necessary. If there aren't
- * any interceptors and the {@link io.grpc.BinaryLog} is {@code null} then this will just be a
- * {@link RealChannel}.
- */
- private final Channel interceptorChannel;
- @Nullable private final String userAgent;
-
- // Only null after channel is terminated. Must be assigned from the syncContext.
- private NameResolver nameResolver;
-
- // Must be accessed from the syncContext.
- private boolean nameResolverStarted;
-
- // null when channel is in idle mode. Must be assigned from syncContext.
- @Nullable
- private LbHelperImpl lbHelper;
-
- // Must ONLY be assigned from updateSubchannelPicker(), which is called from syncContext.
- // null if channel is in idle mode.
- @Nullable
- private volatile SubchannelPicker subchannelPicker;
-
- // Must be accessed from the syncContext
- private boolean panicMode;
-
- // Must be mutated from syncContext
- // If any monitoring hook to be added later needs to get a snapshot of this Set, we could
- // switch to a ConcurrentHashMap.
- private final Set subchannels = new HashSet<>(16, .75f);
-
- // Must be mutated from syncContext
- private final Set oobChannels = new HashSet<>(1, .75f);
-
- // reprocess() must be run from syncContext
- private final DelayedClientTransport delayedTransport;
- private final UncommittedRetriableStreamsRegistry uncommittedRetriableStreamsRegistry
- = new UncommittedRetriableStreamsRegistry();
-
- // Shutdown states.
- //
- // Channel's shutdown process:
- // 1. shutdown(): stop accepting new calls from applications
- // 1a shutdown <- true
- // 1b subchannelPicker <- null
- // 1c delayedTransport.shutdown()
- // 2. delayedTransport terminated: stop stream-creation functionality
- // 2a terminating <- true
- // 2b loadBalancer.shutdown()
- // * LoadBalancer will shutdown subchannels and OOB channels
- // 2c loadBalancer <- null
- // 2d nameResolver.shutdown()
- // 2e nameResolver <- null
- // 3. All subchannels and OOB channels terminated: Channel considered terminated
-
- private final AtomicBoolean shutdown = new AtomicBoolean(false);
- // Must only be mutated and read from syncContext
- private boolean shutdownNowed;
- // Must only be mutated from syncContext
- private volatile boolean terminating;
- // Must be mutated from syncContext
- private volatile boolean terminated;
- private final CountDownLatch terminatedLatch = new CountDownLatch(1);
-
- private final CallTracer.Factory callTracerFactory;
- private final CallTracer channelCallTracer;
- private final ChannelTracer channelTracer;
- private final ChannelLogger channelLogger;
- private final InternalChannelz channelz;
-
- // Must be mutated and read from syncContext
- // a flag for doing channel tracing when flipped
- private ResolutionState lastResolutionState = ResolutionState.NO_RESOLUTION;
- // Must be mutated and read from constructor or syncContext
- // used for channel tracing when value changed
- private ServiceConfigHolder lastServiceConfig = EMPTY_SERVICE_CONFIG;
- @Nullable
- private final ServiceConfigHolder defaultServiceConfig;
- // Must be mutated and read from constructor or syncContext
- private boolean serviceConfigUpdated = false;
- private final boolean lookUpServiceConfig;
-
- // One instance per channel.
- private final ChannelBufferMeter channelBufferUsed = new ChannelBufferMeter();
-
- private final long perRpcBufferLimit;
- private final long channelBufferLimit;
-
- // Temporary false flag that can skip the retry code path.
- private final boolean retryEnabled;
-
- // Called from syncContext
- private final ManagedClientTransport.Listener delayedTransportListener =
- new DelayedTransportListener();
-
- // Must be called from syncContext
- private void maybeShutdownNowSubchannels() {
- if (shutdownNowed) {
- for (InternalSubchannel subchannel : subchannels) {
- subchannel.shutdownNow(SHUTDOWN_NOW_STATUS);
- }
- for (OobChannel oobChannel : oobChannels) {
- oobChannel.getInternalSubchannel().shutdownNow(SHUTDOWN_NOW_STATUS);
- }
- }
- }
-
- // Must be accessed from syncContext
- @VisibleForTesting
- final InUseStateAggregator inUseStateAggregator = new IdleModeStateAggregator();
-
- @Override
- public ListenableFuture getStats() {
- final SettableFuture ret = SettableFuture.create();
- final class StatsFetcher implements Runnable {
- @Override
- public void run() {
- ChannelStats.Builder builder = new InternalChannelz.ChannelStats.Builder();
- channelCallTracer.updateBuilder(builder);
- channelTracer.updateBuilder(builder);
- builder.setTarget(target).setState(channelStateManager.getState());
- List children = new ArrayList<>();
- children.addAll(subchannels);
- children.addAll(oobChannels);
- builder.setSubchannels(children);
- ret.set(builder.build());
- }
- }
-
- // subchannels and oobchannels can only be accessed from syncContext
- syncContext.execute(new StatsFetcher());
- return ret;
- }
-
- @Override
- public InternalLogId getLogId() {
- return logId;
- }
-
- // Run from syncContext
- private class IdleModeTimer implements Runnable {
-
- @Override
- public void run() {
- enterIdleMode();
- }
- }
-
- // Must be called from syncContext
- private void shutdownNameResolverAndLoadBalancer(boolean channelIsActive) {
- syncContext.throwIfNotInThisSynchronizationContext();
- if (channelIsActive) {
- checkState(nameResolverStarted, "nameResolver is not started");
- checkState(lbHelper != null, "lbHelper is null");
- }
- if (nameResolver != null) {
- cancelNameResolverBackoff();
- nameResolver.shutdown();
- nameResolverStarted = false;
- if (channelIsActive) {
- nameResolver = getNameResolver(target, nameResolverFactory, nameResolverArgs);
- } else {
- nameResolver = null;
- }
- }
- if (lbHelper != null) {
- lbHelper.lb.shutdown();
- lbHelper = null;
- }
- subchannelPicker = null;
- }
-
- /**
- * Make the channel exit idle mode, if it's in it.
- *
- * Must be called from syncContext
- */
- @VisibleForTesting
- void exitIdleMode() {
- syncContext.throwIfNotInThisSynchronizationContext();
- if (shutdown.get() || panicMode) {
- return;
- }
- if (inUseStateAggregator.isInUse()) {
- // Cancel the timer now, so that a racing due timer will not put Channel on idleness
- // when the caller of exitIdleMode() is about to use the returned loadBalancer.
- cancelIdleTimer(false);
- } else {
- // exitIdleMode() may be called outside of inUseStateAggregator.handleNotInUse() while
- // isInUse() == false, in which case we still need to schedule the timer.
- rescheduleIdleTimer();
- }
- if (lbHelper != null) {
- return;
- }
- channelLogger.log(ChannelLogLevel.INFO, "Exiting idle mode");
- LbHelperImpl lbHelper = new LbHelperImpl();
- lbHelper.lb = loadBalancerFactory.newLoadBalancer(lbHelper);
- // Delay setting lbHelper until fully initialized, since loadBalancerFactory is user code and
- // may throw. We don't want to confuse our state, even if we will enter panic mode.
- this.lbHelper = lbHelper;
-
- NameResolverListener listener = new NameResolverListener(lbHelper, nameResolver);
- nameResolver.start(listener);
- nameResolverStarted = true;
- }
-
- // Must be run from syncContext
- private void enterIdleMode() {
- // nameResolver and loadBalancer are guaranteed to be non-null. If any of them were null,
- // either the idleModeTimer ran twice without exiting the idle mode, or the task in shutdown()
- // did not cancel idleModeTimer, or enterIdle() ran while shutdown or in idle, all of
- // which are bugs.
- shutdownNameResolverAndLoadBalancer(true);
- delayedTransport.reprocess(null);
- channelLogger.log(ChannelLogLevel.INFO, "Entering IDLE state");
- channelStateManager.gotoState(IDLE);
- if (inUseStateAggregator.isInUse()) {
- exitIdleMode();
- }
- }
-
- // Must be run from syncContext
- private void cancelIdleTimer(boolean permanent) {
- idleTimer.cancel(permanent);
- }
-
- // Always run from syncContext
- private void rescheduleIdleTimer() {
- if (idleTimeoutMillis == IDLE_TIMEOUT_MILLIS_DISABLE) {
- return;
- }
- idleTimer.reschedule(idleTimeoutMillis, TimeUnit.MILLISECONDS);
- }
-
- // Run from syncContext
- @VisibleForTesting
- class DelayedNameResolverRefresh implements Runnable {
- @Override
- public void run() {
- scheduledNameResolverRefresh = null;
- refreshNameResolution();
- }
- }
-
- // Must be used from syncContext
- @Nullable private ScheduledHandle scheduledNameResolverRefresh;
- // The policy to control backoff between name resolution attempts. Non-null when an attempt is
- // scheduled. Must be used from syncContext
- @Nullable private BackoffPolicy nameResolverBackoffPolicy;
-
- // Must be run from syncContext
- private void cancelNameResolverBackoff() {
- syncContext.throwIfNotInThisSynchronizationContext();
- if (scheduledNameResolverRefresh != null) {
- scheduledNameResolverRefresh.cancel();
- scheduledNameResolverRefresh = null;
- nameResolverBackoffPolicy = null;
- }
- }
-
- /**
- * Force name resolution refresh to happen immediately and reset refresh back-off. Must be run
- * from syncContext.
- */
- private void refreshAndResetNameResolution() {
- syncContext.throwIfNotInThisSynchronizationContext();
- cancelNameResolverBackoff();
- refreshNameResolution();
- }
-
- private void refreshNameResolution() {
- syncContext.throwIfNotInThisSynchronizationContext();
- if (nameResolverStarted) {
- nameResolver.refresh();
- }
- }
-
- private final class ChannelTransportProvider implements ClientTransportProvider {
- @Override
- public ClientTransport get(PickSubchannelArgs args) {
- SubchannelPicker pickerCopy = subchannelPicker;
- if (shutdown.get()) {
- // If channel is shut down, delayedTransport is also shut down which will fail the stream
- // properly.
- return delayedTransport;
- }
- if (pickerCopy == null) {
- final class ExitIdleModeForTransport implements Runnable {
- @Override
- public void run() {
- exitIdleMode();
- }
- }
-
- syncContext.execute(new ExitIdleModeForTransport());
- return delayedTransport;
- }
- // There is no need to reschedule the idle timer here.
- //
- // pickerCopy != null, which means idle timer has not expired when this method starts.
- // Even if idle timer expires right after we grab pickerCopy, and it shuts down LoadBalancer
- // which calls Subchannel.shutdown(), the InternalSubchannel will be actually shutdown after
- // SUBCHANNEL_SHUTDOWN_DELAY_SECONDS, which gives the caller time to start RPC on it.
- //
- // In most cases the idle timer is scheduled to fire after the transport has created the
- // stream, which would have reported in-use state to the channel that would have cancelled
- // the idle timer.
- PickResult pickResult = pickerCopy.pickSubchannel(args);
- ClientTransport transport = GrpcUtil.getTransportFromPickResult(
- pickResult, args.getCallOptions().isWaitForReady());
- if (transport != null) {
- return transport;
- }
- return delayedTransport;
- }
-
- @Override
- public ClientStream newRetriableStream(
- final MethodDescriptor method,
- final CallOptions callOptions,
- final Metadata headers,
- final Context context) {
- checkState(retryEnabled, "retry should be enabled");
- final Throttle throttle = lastServiceConfig.managedChannelServiceConfig.getRetryThrottling();
- final class RetryStream extends RetriableStream {
- RetryStream() {
- super(
- method,
- headers,
- channelBufferUsed,
- perRpcBufferLimit,
- channelBufferLimit,
- getCallExecutor(callOptions),
- transportFactory.getScheduledExecutorService(),
- callOptions.getOption(RETRY_POLICY_KEY),
- callOptions.getOption(HEDGING_POLICY_KEY),
- throttle);
- }
-
- @Override
- Status prestart() {
- return uncommittedRetriableStreamsRegistry.add(this);
- }
-
- @Override
- void postCommit() {
- uncommittedRetriableStreamsRegistry.remove(this);
- }
-
- @Override
- ClientStream newSubstream(ClientStreamTracer.Factory tracerFactory, Metadata newHeaders) {
- CallOptions newOptions = callOptions.withStreamTracerFactory(tracerFactory);
- ClientTransport transport =
- get(new PickSubchannelArgsImpl(method, newHeaders, newOptions));
- Context origContext = context.attach();
- try {
- return transport.newStream(method, newHeaders, newOptions);
- } finally {
- context.detach(origContext);
- }
- }
- }
-
- return new RetryStream();
- }
- }
-
- private final ClientTransportProvider transportProvider = new ChannelTransportProvider();
-
- private final Rescheduler idleTimer;
-
- ManagedChannelImpl2(
- AbstractManagedChannelImplBuilder> builder,
- ClientTransportFactory clientTransportFactory,
- BackoffPolicy.Provider backoffPolicyProvider,
- ObjectPool extends Executor> balancerRpcExecutorPool,
- Supplier stopwatchSupplier,
- List interceptors,
- final TimeProvider timeProvider) {
- this.target = checkNotNull(builder.target, "target");
- this.logId = InternalLogId.allocate("Channel", target);
- this.timeProvider = checkNotNull(timeProvider, "timeProvider");
- this.executorPool = checkNotNull(builder.executorPool, "executorPool");
- this.executor = checkNotNull(executorPool.getObject(), "executor");
- this.transportFactory =
- new CallCredentialsApplyingTransportFactory(clientTransportFactory, this.executor);
- this.scheduledExecutor =
- new RestrictedScheduledExecutor(transportFactory.getScheduledExecutorService());
- maxTraceEvents = builder.maxTraceEvents;
- channelTracer = new ChannelTracer(
- logId, builder.maxTraceEvents, timeProvider.currentTimeNanos(),
- "Channel for '" + target + "'");
- channelLogger = new ChannelLoggerImpl(channelTracer, timeProvider);
- this.nameResolverFactory = builder.getNameResolverFactory();
- ProxyDetector proxyDetector =
- builder.proxyDetector != null ? builder.proxyDetector : GrpcUtil.DEFAULT_PROXY_DETECTOR;
- this.retryEnabled = builder.retryEnabled && !builder.temporarilyDisableRetry;
- this.loadBalancerFactory = new AutoConfiguredLoadBalancerFactory2(builder.defaultLbPolicy);
- this.offloadExecutorHolder =
- 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(serviceConfigParser)
- .setChannelLogger(channelLogger)
- .setOffloadExecutor(
- // Avoid creating the offloadExecutor until it is first used
- new Executor() {
- @Override
- public void execute(Runnable command) {
- offloadExecutorHolder.getExecutor().execute(command);
- }
- })
- .build();
- this.nameResolver = getNameResolver(target, nameResolverFactory, nameResolverArgs);
- this.balancerRpcExecutorPool = checkNotNull(balancerRpcExecutorPool, "balancerRpcExecutorPool");
- this.balancerRpcExecutorHolder = new ExecutorHolder(balancerRpcExecutorPool);
- this.delayedTransport = new DelayedClientTransport(this.executor, this.syncContext);
- this.delayedTransport.start(delayedTransportListener);
- this.backoffPolicyProvider = backoffPolicyProvider;
-
- serviceConfigInterceptor = new ServiceConfigInterceptor2(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,
- (ManagedChannelServiceConfig2) 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);
- if (builder.binlog != null) {
- channel = builder.binlog.wrapChannel(channel);
- }
- this.interceptorChannel = ClientInterceptors.intercept(channel, interceptors);
- this.stopwatchSupplier = checkNotNull(stopwatchSupplier, "stopwatchSupplier");
- if (builder.idleTimeoutMillis == IDLE_TIMEOUT_MILLIS_DISABLE) {
- this.idleTimeoutMillis = builder.idleTimeoutMillis;
- } else {
- checkArgument(
- builder.idleTimeoutMillis
- >= AbstractManagedChannelImplBuilder.IDLE_MODE_MIN_TIMEOUT_MILLIS,
- "invalid idleTimeoutMillis %s", builder.idleTimeoutMillis);
- this.idleTimeoutMillis = builder.idleTimeoutMillis;
- }
-
- idleTimer = new Rescheduler(
- new IdleModeTimer(),
- syncContext,
- transportFactory.getScheduledExecutorService(),
- stopwatchSupplier.get());
- this.fullStreamDecompression = builder.fullStreamDecompression;
- this.decompressorRegistry = checkNotNull(builder.decompressorRegistry, "decompressorRegistry");
- this.compressorRegistry = checkNotNull(builder.compressorRegistry, "compressorRegistry");
- this.userAgent = builder.userAgent;
-
- this.channelBufferLimit = builder.retryBufferSize;
- this.perRpcBufferLimit = builder.perRpcBufferLimit;
- final class ChannelCallTracerFactory implements CallTracer.Factory {
- @Override
- public CallTracer create() {
- return new CallTracer(timeProvider);
- }
- }
-
- this.callTracerFactory = new ChannelCallTracerFactory();
- channelCallTracer = callTracerFactory.create();
- this.channelz = checkNotNull(builder.channelz);
- channelz.addRootChannel(this);
-
- if (!lookUpServiceConfig) {
- if (defaultServiceConfig != null) {
- channelLogger.log(
- ChannelLogLevel.INFO, "Service config look-up disabled, using default service config");
- }
- handleServiceConfigUpdate();
- }
- }
-
- // May only be called in constructor or syncContext
- private void handleServiceConfigUpdate() {
- serviceConfigUpdated = true;
- serviceConfigInterceptor.handleUpdate(lastServiceConfig.managedChannelServiceConfig);
- }
-
- @VisibleForTesting
- static NameResolver getNameResolver(String target, NameResolver.Factory nameResolverFactory,
- NameResolver.Args nameResolverArgs) {
- // Finding a NameResolver. Try using the target string as the URI. If that fails, try prepending
- // "dns:///".
- URI targetUri = null;
- StringBuilder uriSyntaxErrors = new StringBuilder();
- try {
- targetUri = new URI(target);
- // For "localhost:8080" this would likely cause newNameResolver to return null, because
- // "localhost" is parsed as the scheme. Will fall into the next branch and try
- // "dns:///localhost:8080".
- } catch (URISyntaxException e) {
- // Can happen with ip addresses like "[::1]:1234" or 127.0.0.1:1234.
- uriSyntaxErrors.append(e.getMessage());
- }
- if (targetUri != null) {
- NameResolver resolver = nameResolverFactory.newNameResolver(targetUri, nameResolverArgs);
- if (resolver != null) {
- return resolver;
- }
- // "foo.googleapis.com:8080" cause resolver to be null, because "foo.googleapis.com" is an
- // unmapped scheme. Just fall through and will try "dns:///foo.googleapis.com:8080"
- }
-
- // If we reached here, the targetUri couldn't be used.
- if (!URI_PATTERN.matcher(target).matches()) {
- // It doesn't look like a URI target. Maybe it's an authority string. Try with the default
- // scheme from the factory.
- try {
- targetUri = new URI(nameResolverFactory.getDefaultScheme(), "", "/" + target, null);
- } catch (URISyntaxException e) {
- // Should not be possible.
- throw new IllegalArgumentException(e);
- }
- NameResolver resolver = nameResolverFactory.newNameResolver(targetUri, nameResolverArgs);
- if (resolver != null) {
- return resolver;
- }
- }
- throw new IllegalArgumentException(String.format(
- "cannot find a NameResolver for %s%s",
- target, uriSyntaxErrors.length() > 0 ? " (" + uriSyntaxErrors + ")" : ""));
- }
-
- /**
- * Initiates an orderly shutdown in which preexisting calls continue but new calls are immediately
- * cancelled.
- */
- @Override
- public ManagedChannelImpl2 shutdown() {
- channelLogger.log(ChannelLogLevel.DEBUG, "shutdown() called");
- if (!shutdown.compareAndSet(false, true)) {
- return this;
- }
-
- // Put gotoState(SHUTDOWN) as early into the syncContext's queue as possible.
- // delayedTransport.shutdown() may also add some tasks into the queue. But some things inside
- // delayedTransport.shutdown() like setting delayedTransport.shutdown = true are not run in the
- // syncContext's queue and should not be blocked, so we do not drain() immediately here.
- final class Shutdown implements Runnable {
- @Override
- public void run() {
- channelLogger.log(ChannelLogLevel.INFO, "Entering SHUTDOWN state");
- channelStateManager.gotoState(SHUTDOWN);
- }
- }
-
- syncContext.executeLater(new Shutdown());
-
- uncommittedRetriableStreamsRegistry.onShutdown(SHUTDOWN_STATUS);
- final class CancelIdleTimer implements Runnable {
- @Override
- public void run() {
- cancelIdleTimer(/* permanent= */ true);
- }
- }
-
- syncContext.execute(new CancelIdleTimer());
- return this;
- }
-
- /**
- * Initiates a forceful shutdown in which preexisting and new calls are cancelled. Although
- * forceful, the shutdown process is still not instantaneous; {@link #isTerminated()} will likely
- * return {@code false} immediately after this method returns.
- */
- @Override
- public ManagedChannelImpl2 shutdownNow() {
- channelLogger.log(ChannelLogLevel.DEBUG, "shutdownNow() called");
- shutdown();
- uncommittedRetriableStreamsRegistry.onShutdownNow(SHUTDOWN_NOW_STATUS);
- final class ShutdownNow implements Runnable {
- @Override
- public void run() {
- if (shutdownNowed) {
- return;
- }
- shutdownNowed = true;
- maybeShutdownNowSubchannels();
- }
- }
-
- syncContext.execute(new ShutdownNow());
- return this;
- }
-
- // Called from syncContext
- @VisibleForTesting
- void panic(final Throwable t) {
- if (panicMode) {
- // Preserve the first panic information
- return;
- }
- panicMode = true;
- cancelIdleTimer(/* permanent= */ true);
- shutdownNameResolverAndLoadBalancer(false);
- final class PanicSubchannelPicker extends SubchannelPicker {
- private final PickResult panicPickResult =
- PickResult.withDrop(
- Status.INTERNAL.withDescription("Panic! This is a bug!").withCause(t));
-
- @Override
- public PickResult pickSubchannel(PickSubchannelArgs args) {
- return panicPickResult;
- }
- }
-
- updateSubchannelPicker(new PanicSubchannelPicker());
- channelLogger.log(ChannelLogLevel.ERROR, "PANIC! Entering TRANSIENT_FAILURE");
- channelStateManager.gotoState(TRANSIENT_FAILURE);
- }
-
- @VisibleForTesting
- boolean isInPanicMode() {
- return panicMode;
- }
-
- // Called from syncContext
- private void updateSubchannelPicker(SubchannelPicker newPicker) {
- subchannelPicker = newPicker;
- delayedTransport.reprocess(newPicker);
- }
-
- @Override
- public boolean isShutdown() {
- return shutdown.get();
- }
-
- @Override
- public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
- return terminatedLatch.await(timeout, unit);
- }
-
- @Override
- public boolean isTerminated() {
- return terminated;
- }
-
- /*
- * Creates a new outgoing call on the channel.
- */
- @Override
- public ClientCall newCall(MethodDescriptor method,
- CallOptions callOptions) {
- return interceptorChannel.newCall(method, callOptions);
- }
-
- @Override
- public String authority() {
- return interceptorChannel.authority();
- }
-
- private Executor getCallExecutor(CallOptions callOptions) {
- Executor executor = callOptions.getExecutor();
- if (executor == null) {
- executor = this.executor;
- }
- return executor;
- }
-
- private class RealChannel extends Channel {
- // Set when the NameResolver is initially created. When we create a new NameResolver for the
- // same target, the new instance must have the same value.
- private final String authority;
-
- private RealChannel(String authority) {
- this.authority = checkNotNull(authority, "authority");
- }
-
- @Override
- public ClientCall newCall(MethodDescriptor method,
- CallOptions callOptions) {
- return new ClientCallImpl<>(
- method,
- getCallExecutor(callOptions),
- callOptions,
- transportProvider,
- terminated ? null : transportFactory.getScheduledExecutorService(),
- channelCallTracer,
- retryEnabled)
- .setFullStreamDecompression(fullStreamDecompression)
- .setDecompressorRegistry(decompressorRegistry)
- .setCompressorRegistry(compressorRegistry);
- }
-
- @Override
- public String authority() {
- return authority;
- }
- }
-
- /**
- * Terminate the channel if termination conditions are met.
- */
- // Must be run from syncContext
- private void maybeTerminateChannel() {
- if (terminated) {
- return;
- }
- if (shutdown.get() && subchannels.isEmpty() && oobChannels.isEmpty()) {
- channelLogger.log(ChannelLogLevel.INFO, "Terminated");
- channelz.removeRootChannel(this);
- executorPool.returnObject(executor);
- balancerRpcExecutorHolder.release();
- offloadExecutorHolder.release();
- // Release the transport factory so that it can deallocate any resources.
- transportFactory.close();
-
- terminated = true;
- terminatedLatch.countDown();
- }
- }
-
- // Must be called from syncContext
- private void handleInternalSubchannelState(ConnectivityStateInfo newState) {
- if (newState.getState() == TRANSIENT_FAILURE || newState.getState() == IDLE) {
- refreshAndResetNameResolution();
- }
- }
-
- @Override
- @SuppressWarnings("deprecation")
- public ConnectivityState getState(boolean requestConnection) {
- ConnectivityState savedChannelState = channelStateManager.getState();
- if (requestConnection && savedChannelState == IDLE) {
- final class RequestConnection implements Runnable {
- @Override
- public void run() {
- exitIdleMode();
- if (subchannelPicker != null) {
- subchannelPicker.requestConnection();
- }
- if (lbHelper != null) {
- lbHelper.lb.requestConnection();
- }
- }
- }
-
- syncContext.execute(new RequestConnection());
- }
- return savedChannelState;
- }
-
- @Override
- public void notifyWhenStateChanged(final ConnectivityState source, final Runnable callback) {
- final class NotifyStateChanged implements Runnable {
- @Override
- public void run() {
- channelStateManager.notifyWhenStateChanged(callback, executor, source);
- }
- }
-
- syncContext.execute(new NotifyStateChanged());
- }
-
- @Override
- public void resetConnectBackoff() {
- final class ResetConnectBackoff implements Runnable {
- @Override
- public void run() {
- if (shutdown.get()) {
- return;
- }
- if (scheduledNameResolverRefresh != null && scheduledNameResolverRefresh.isPending()) {
- checkState(nameResolverStarted, "name resolver must be started");
- refreshAndResetNameResolution();
- }
- for (InternalSubchannel subchannel : subchannels) {
- subchannel.resetConnectBackoff();
- }
- for (OobChannel oobChannel : oobChannels) {
- oobChannel.resetConnectBackoff();
- }
- }
- }
-
- syncContext.execute(new ResetConnectBackoff());
- }
-
- @Override
- public void enterIdle() {
- final class PrepareToLoseNetworkRunnable implements Runnable {
- @Override
- public void run() {
- if (shutdown.get() || lbHelper == null) {
- return;
- }
- cancelIdleTimer(/* permanent= */ false);
- enterIdleMode();
- }
- }
-
- syncContext.execute(new PrepareToLoseNetworkRunnable());
- }
-
- /**
- * A registry that prevents channel shutdown from killing existing retry attempts that are in
- * backoff.
- */
- private final class UncommittedRetriableStreamsRegistry {
- // TODO(zdapeng): This means we would acquire a lock for each new retry-able stream,
- // it's worthwhile to look for a lock-free approach.
- final Object lock = new Object();
-
- @GuardedBy("lock")
- Collection uncommittedRetriableStreams = new HashSet<>();
-
- @GuardedBy("lock")
- Status shutdownStatus;
-
- void onShutdown(Status reason) {
- boolean shouldShutdownDelayedTransport = false;
- synchronized (lock) {
- if (shutdownStatus != null) {
- return;
- }
- shutdownStatus = reason;
- // Keep the delayedTransport open until there is no more uncommitted streams, b/c those
- // retriable streams, which may be in backoff and not using any transport, are already
- // started RPCs.
- if (uncommittedRetriableStreams.isEmpty()) {
- shouldShutdownDelayedTransport = true;
- }
- }
-
- if (shouldShutdownDelayedTransport) {
- delayedTransport.shutdown(reason);
- }
- }
-
- void onShutdownNow(Status reason) {
- onShutdown(reason);
- Collection streams;
-
- synchronized (lock) {
- streams = new ArrayList<>(uncommittedRetriableStreams);
- }
-
- for (ClientStream stream : streams) {
- stream.cancel(reason);
- }
- delayedTransport.shutdownNow(reason);
- }
-
- /**
- * Registers a RetriableStream and return null if not shutdown, otherwise just returns the
- * shutdown Status.
- */
- @Nullable
- Status add(RetriableStream> retriableStream) {
- synchronized (lock) {
- if (shutdownStatus != null) {
- return shutdownStatus;
- }
- uncommittedRetriableStreams.add(retriableStream);
- return null;
- }
- }
-
- void remove(RetriableStream> retriableStream) {
- Status shutdownStatusCopy = null;
-
- synchronized (lock) {
- uncommittedRetriableStreams.remove(retriableStream);
- if (uncommittedRetriableStreams.isEmpty()) {
- shutdownStatusCopy = shutdownStatus;
- // Because retriable transport is long-lived, we take this opportunity to down-size the
- // hashmap.
- uncommittedRetriableStreams = new HashSet<>();
- }
- }
-
- if (shutdownStatusCopy != null) {
- delayedTransport.shutdown(shutdownStatusCopy);
- }
- }
- }
-
- private class LbHelperImpl extends LoadBalancer.Helper {
- AutoConfiguredLoadBalancer lb;
-
- @Deprecated
- @Override
- public AbstractSubchannel createSubchannel(
- List addressGroups, Attributes attrs) {
- logWarningIfNotInSyncContext("createSubchannel()");
- // TODO(ejona): can we be even stricter? Like loadBalancer == null?
- checkNotNull(addressGroups, "addressGroups");
- checkNotNull(attrs, "attrs");
- final SubchannelImpl subchannel = createSubchannelInternal(
- CreateSubchannelArgs.newBuilder()
- .setAddresses(addressGroups)
- .setAttributes(attrs)
- .build());
-
- final SubchannelStateListener listener =
- new LoadBalancer.SubchannelStateListener() {
- @Override
- public void onSubchannelState(ConnectivityStateInfo newState) {
- // Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match.
- if (LbHelperImpl.this != ManagedChannelImpl2.this.lbHelper) {
- return;
- }
- lb.handleSubchannelState(subchannel, newState);
- }
- };
-
- subchannel.internalStart(listener);
- return subchannel;
- }
-
- @Override
- public AbstractSubchannel createSubchannel(CreateSubchannelArgs args) {
- syncContext.throwIfNotInThisSynchronizationContext();
- return createSubchannelInternal(args);
- }
-
- private SubchannelImpl createSubchannelInternal(CreateSubchannelArgs args) {
- // TODO(ejona): can we be even stricter? Like loadBalancer == null?
- checkState(!terminated, "Channel is terminated");
- return new SubchannelImpl(args, this);
- }
-
- @Override
- public void updateBalancingState(
- final ConnectivityState newState, final SubchannelPicker newPicker) {
- checkNotNull(newState, "newState");
- checkNotNull(newPicker, "newPicker");
- logWarningIfNotInSyncContext("updateBalancingState()");
- final class UpdateBalancingState implements Runnable {
- @Override
- public void run() {
- if (LbHelperImpl.this != lbHelper) {
- return;
- }
- updateSubchannelPicker(newPicker);
- // It's not appropriate to report SHUTDOWN state from lb.
- // Ignore the case of newState == SHUTDOWN for now.
- if (newState != SHUTDOWN) {
- channelLogger.log(ChannelLogLevel.INFO, "Entering {0} state", newState);
- channelStateManager.gotoState(newState);
- }
- }
- }
-
- syncContext.execute(new UpdateBalancingState());
- }
-
- @Override
- public void refreshNameResolution() {
- logWarningIfNotInSyncContext("refreshNameResolution()");
- final class LoadBalancerRefreshNameResolution implements Runnable {
- @Override
- public void run() {
- refreshAndResetNameResolution();
- }
- }
-
- syncContext.execute(new LoadBalancerRefreshNameResolution());
- }
-
- @Deprecated
- @Override
- public void updateSubchannelAddresses(
- LoadBalancer.Subchannel subchannel, List addrs) {
- checkArgument(subchannel instanceof SubchannelImpl,
- "subchannel must have been returned from createSubchannel");
- logWarningIfNotInSyncContext("updateSubchannelAddresses()");
- ((InternalSubchannel) subchannel.getInternalSubchannel()).updateAddresses(addrs);
- }
-
- @Override
- public ManagedChannel createOobChannel(EquivalentAddressGroup addressGroup, String authority) {
- // TODO(ejona): can we be even stricter? Like terminating?
- checkState(!terminated, "Channel is terminated");
- long oobChannelCreationTime = timeProvider.currentTimeNanos();
- InternalLogId oobLogId = InternalLogId.allocate("OobChannel", /*details=*/ null);
- InternalLogId subchannelLogId =
- InternalLogId.allocate("Subchannel-OOB", /*details=*/ authority);
- ChannelTracer oobChannelTracer =
- new ChannelTracer(
- oobLogId, maxTraceEvents, oobChannelCreationTime,
- "OobChannel for " + addressGroup);
- final OobChannel oobChannel = new OobChannel(
- authority, balancerRpcExecutorPool, transportFactory.getScheduledExecutorService(),
- syncContext, callTracerFactory.create(), oobChannelTracer, channelz, timeProvider);
- channelTracer.reportEvent(new ChannelTrace.Event.Builder()
- .setDescription("Child OobChannel created")
- .setSeverity(ChannelTrace.Event.Severity.CT_INFO)
- .setTimestampNanos(oobChannelCreationTime)
- .setChannelRef(oobChannel)
- .build());
- ChannelTracer subchannelTracer =
- new ChannelTracer(subchannelLogId, maxTraceEvents, oobChannelCreationTime,
- "Subchannel for " + addressGroup);
- ChannelLogger subchannelLogger = new ChannelLoggerImpl(subchannelTracer, timeProvider);
- final class ManagedOobChannelCallback extends InternalSubchannel.Callback {
- @Override
- void onTerminated(InternalSubchannel is) {
- oobChannels.remove(oobChannel);
- channelz.removeSubchannel(is);
- oobChannel.handleSubchannelTerminated();
- maybeTerminateChannel();
- }
-
- @Override
- void onStateChange(InternalSubchannel is, ConnectivityStateInfo newState) {
- handleInternalSubchannelState(newState);
- oobChannel.handleSubchannelStateChange(newState);
- }
- }
-
- final InternalSubchannel internalSubchannel = new InternalSubchannel(
- Collections.singletonList(addressGroup),
- authority, userAgent, backoffPolicyProvider, transportFactory,
- transportFactory.getScheduledExecutorService(), stopwatchSupplier, syncContext,
- // All callback methods are run from syncContext
- new ManagedOobChannelCallback(),
- channelz,
- callTracerFactory.create(),
- subchannelTracer,
- subchannelLogId,
- subchannelLogger);
- oobChannelTracer.reportEvent(new ChannelTrace.Event.Builder()
- .setDescription("Child Subchannel created")
- .setSeverity(ChannelTrace.Event.Severity.CT_INFO)
- .setTimestampNanos(oobChannelCreationTime)
- .setSubchannelRef(internalSubchannel)
- .build());
- channelz.addSubchannel(oobChannel);
- channelz.addSubchannel(internalSubchannel);
- oobChannel.setSubchannel(internalSubchannel);
- final class AddOobChannel implements Runnable {
- @Override
- public void run() {
- if (terminating) {
- oobChannel.shutdown();
- }
- if (!terminated) {
- // If channel has not terminated, it will track the subchannel and block termination
- // for it.
- oobChannels.add(oobChannel);
- }
- }
- }
-
- syncContext.execute(new AddOobChannel());
- return oobChannel;
- }
-
- @Override
- public void updateOobChannelAddresses(ManagedChannel channel, EquivalentAddressGroup eag) {
- checkArgument(channel instanceof OobChannel,
- "channel must have been returned from createOobChannel");
- ((OobChannel) channel).updateAddresses(eag);
- }
-
- @Override
- public String getAuthority() {
- return ManagedChannelImpl2.this.authority();
- }
-
- @Deprecated
- @Override
- public NameResolver.Factory getNameResolverFactory() {
- return nameResolverFactory;
- }
-
- @Override
- public SynchronizationContext getSynchronizationContext() {
- return syncContext;
- }
-
- @Override
- public ScheduledExecutorService getScheduledExecutorService() {
- return scheduledExecutor;
- }
-
- @Override
- public ChannelLogger getChannelLogger() {
- return channelLogger;
- }
-
- @Override
- public NameResolver.Args getNameResolverArgs() {
- return nameResolverArgs;
- }
-
- @Override
- public NameResolverRegistry getNameResolverRegistry() {
- return nameResolverRegistry;
- }
- }
-
- private final class NameResolverListener extends NameResolver.Listener2 {
- final LbHelperImpl helper;
- final NameResolver resolver;
-
- NameResolverListener(LbHelperImpl helperImpl, NameResolver resolver) {
- this.helper = checkNotNull(helperImpl, "helperImpl");
- this.resolver = checkNotNull(resolver, "resolver");
- }
-
- @Override
- public void onResult(final ResolutionResult resolutionResult) {
- final class NamesResolved implements Runnable {
-
- @SuppressWarnings({"ReferenceEquality", "deprecation"})
- @Override
- public void run() {
- List servers = resolutionResult.getAddresses();
- Attributes attrs = resolutionResult.getAttributes();
- channelLogger.log(
- ChannelLogLevel.DEBUG, "Resolved address: {0}, config={1}", servers, attrs);
- ResolutionState lastResolutionStateCopy = lastResolutionState;
-
- if (lastResolutionState != ResolutionState.SUCCESS) {
- channelLogger.log(ChannelLogLevel.INFO, "Address resolved: {0}", servers);
- lastResolutionState = ResolutionState.SUCCESS;
- }
-
- nameResolverBackoffPolicy = null;
- ConfigOrError configOrError = resolutionResult.getServiceConfig();
- ServiceConfigHolder validServiceConfig = null;
- Status serviceConfigError = null;
- if (configOrError != null) {
- Map rawServiceConfig =
- resolutionResult.getAttributes().get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG);
- validServiceConfig = configOrError.getConfig() == null
- ? null
- : new ServiceConfigHolder(
- rawServiceConfig, (ManagedChannelServiceConfig2) configOrError.getConfig());
- serviceConfigError = configOrError.getError();
- }
-
- ServiceConfigHolder effectiveServiceConfig;
- if (!lookUpServiceConfig) {
- if (validServiceConfig != null) {
- channelLogger.log(
- ChannelLogLevel.INFO,
- "Service config from name resolver discarded by channel settings");
- }
- 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 (validServiceConfig != null) {
- effectiveServiceConfig = validServiceConfig;
- } else if (defaultServiceConfig != null) {
- effectiveServiceConfig = defaultServiceConfig;
- 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,
- "Fallback to error due to invalid first service config without default config");
- onError(configOrError.getError());
- return;
- } else {
- effectiveServiceConfig = lastServiceConfig;
- }
- } else {
- effectiveServiceConfig = EMPTY_SERVICE_CONFIG;
- }
- 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(
- Level.WARNING,
- "[" + getLogId() + "] Unexpected exception from parsing service config",
- re);
- }
- }
-
- // Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match.
- if (NameResolverListener.this.helper == ManagedChannelImpl2.this.lbHelper) {
- Attributes effectiveAttrs = attrs;
- if (effectiveServiceConfig != validServiceConfig) {
- effectiveAttrs = attrs.toBuilder()
- .set(
- GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG,
- effectiveServiceConfig.rawServiceConfig)
- .build();
- }
-
- Status handleResult = helper.lb.tryHandleResolvedAddresses(
- ResolvedAddresses.newBuilder()
- .setAddresses(servers)
- .setAttributes(effectiveAttrs)
- .setLoadBalancingPolicyConfig(
- effectiveServiceConfig.managedChannelServiceConfig.getLoadBalancingConfig())
- .build());
-
- if (!handleResult.isOk()) {
- 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"));
- }
- }
- }
- }
- }
-
- syncContext.execute(new NamesResolved());
- }
-
- @Override
- public void onError(final Status error) {
- checkArgument(!error.isOk(), "the error status must not be OK");
- final class NameResolverErrorHandler implements Runnable {
- @Override
- public void run() {
- handleErrorInSyncContext(error);
- }
- }
-
- syncContext.execute(new NameResolverErrorHandler());
- }
-
- private void handleErrorInSyncContext(Status error) {
- logger.log(Level.WARNING, "[{0}] Failed to resolve name. status={1}",
- new Object[] {getLogId(), error});
- if (lastResolutionState != ResolutionState.ERROR) {
- channelLogger.log(ChannelLogLevel.WARNING, "Failed to resolve name: {0}", error);
- lastResolutionState = ResolutionState.ERROR;
- }
- // Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match.
- if (NameResolverListener.this.helper != ManagedChannelImpl2.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
- // TODO(ericgribkoff) Update contract of NameResolver.Listener or decide if we
- // want to reset the backoff interval upon repeated onError() calls
- return;
- }
- if (nameResolverBackoffPolicy == null) {
- nameResolverBackoffPolicy = backoffPolicyProvider.get();
- }
- long delayNanos = nameResolverBackoffPolicy.nextBackoffNanos();
- channelLogger.log(
- ChannelLogLevel.DEBUG,
- "Scheduling DNS resolution backoff for {0} ns", delayNanos);
- scheduledNameResolverRefresh =
- syncContext.schedule(
- new DelayedNameResolverRefresh(), delayNanos, TimeUnit.NANOSECONDS,
- transportFactory .getScheduledExecutorService());
- }
- }
-
- private final class SubchannelImpl extends AbstractSubchannel {
- final CreateSubchannelArgs args;
- final LbHelperImpl helper;
- final InternalLogId subchannelLogId;
- final ChannelLoggerImpl subchannelLogger;
- final ChannelTracer subchannelTracer;
- SubchannelStateListener listener;
- InternalSubchannel subchannel;
- boolean started;
- boolean shutdown;
- ScheduledHandle delayedShutdownTask;
-
- SubchannelImpl(CreateSubchannelArgs args, LbHelperImpl helper) {
- this.args = checkNotNull(args, "args");
- this.helper = checkNotNull(helper, "helper");
- subchannelLogId = InternalLogId.allocate("Subchannel", /*details=*/ authority());
- subchannelTracer = new ChannelTracer(
- subchannelLogId, maxTraceEvents, timeProvider.currentTimeNanos(),
- "Subchannel for " + args.getAddresses());
- subchannelLogger = new ChannelLoggerImpl(subchannelTracer, timeProvider);
- }
-
- // This can be called either in or outside of syncContext
- // TODO(zhangkun83): merge it back into start() once the caller createSubchannel() is deleted.
- private void internalStart(final SubchannelStateListener listener) {
- checkState(!started, "already started");
- checkState(!shutdown, "already shutdown");
- started = true;
- this.listener = listener;
- // TODO(zhangkun): possibly remove the volatile of terminating when this whole method is
- // required to be called from syncContext
- if (terminating) {
- syncContext.execute(new Runnable() {
- @Override
- public void run() {
- listener.onSubchannelState(ConnectivityStateInfo.forNonError(SHUTDOWN));
- }
- });
- return;
- }
- final class ManagedInternalSubchannelCallback extends InternalSubchannel.Callback {
- // All callbacks are run in syncContext
- @Override
- void onTerminated(InternalSubchannel is) {
- subchannels.remove(is);
- channelz.removeSubchannel(is);
- maybeTerminateChannel();
- }
-
- @Override
- void onStateChange(InternalSubchannel is, ConnectivityStateInfo newState) {
- handleInternalSubchannelState(newState);
- checkState(listener != null, "listener is null");
- listener.onSubchannelState(newState);
- }
-
- @Override
- void onInUse(InternalSubchannel is) {
- inUseStateAggregator.updateObjectInUse(is, true);
- }
-
- @Override
- void onNotInUse(InternalSubchannel is) {
- inUseStateAggregator.updateObjectInUse(is, false);
- }
- }
-
- final InternalSubchannel internalSubchannel = new InternalSubchannel(
- args.getAddresses(),
- authority(),
- userAgent,
- backoffPolicyProvider,
- transportFactory,
- transportFactory.getScheduledExecutorService(),
- stopwatchSupplier,
- syncContext,
- new ManagedInternalSubchannelCallback(),
- channelz,
- callTracerFactory.create(),
- subchannelTracer,
- subchannelLogId,
- subchannelLogger);
-
- channelTracer.reportEvent(new ChannelTrace.Event.Builder()
- .setDescription("Child Subchannel started")
- .setSeverity(ChannelTrace.Event.Severity.CT_INFO)
- .setTimestampNanos(timeProvider.currentTimeNanos())
- .setSubchannelRef(internalSubchannel)
- .build());
-
- this.subchannel = internalSubchannel;
- // TODO(zhangkun83): no need to schedule on syncContext when this whole method is required
- // to be called from syncContext
- syncContext.execute(new Runnable() {
- @Override
- public void run() {
- channelz.addSubchannel(internalSubchannel);
- subchannels.add(internalSubchannel);
- }
- });
- }
-
- @Override
- public void start(SubchannelStateListener listener) {
- syncContext.throwIfNotInThisSynchronizationContext();
- internalStart(listener);
- }
-
- @Override
- InternalInstrumented getInstrumentedInternalSubchannel() {
- checkState(started, "not started");
- return subchannel;
- }
-
- @Override
- public void shutdown() {
- // TODO(zhangkun83): replace shutdown() with internalShutdown() to turn the warning into an
- // exception.
- logWarningIfNotInSyncContext("Subchannel.shutdown()");
- syncContext.execute(new Runnable() {
- @Override
- public void run() {
- internalShutdown();
- }
- });
- }
-
- private void internalShutdown() {
- syncContext.throwIfNotInThisSynchronizationContext();
- if (subchannel == null) {
- // start() was not successful
- shutdown = true;
- return;
- }
- if (shutdown) {
- if (terminating && delayedShutdownTask != null) {
- // shutdown() was previously called when terminating == false, thus a delayed shutdown()
- // was scheduled. Now since terminating == true, We should expedite the shutdown.
- delayedShutdownTask.cancel();
- delayedShutdownTask = null;
- // Will fall through to the subchannel.shutdown() at the end.
- } else {
- return;
- }
- } else {
- shutdown = true;
- }
- // Add a delay to shutdown to deal with the race between 1) a transport being picked and
- // newStream() being called on it, and 2) its Subchannel is shut down by LoadBalancer (e.g.,
- // because of address change, or because LoadBalancer is shutdown by Channel entering idle
- // mode). If (2) wins, the app will see a spurious error. We work around this by delaying
- // shutdown of Subchannel for a few seconds here.
- //
- // TODO(zhangkun83): consider a better approach
- // (https://github.com/grpc/grpc-java/issues/2562).
- if (!terminating) {
- final class ShutdownSubchannel implements Runnable {
- @Override
- public void run() {
- subchannel.shutdown(SUBCHANNEL_SHUTDOWN_STATUS);
- }
- }
-
- delayedShutdownTask = syncContext.schedule(
- new LogExceptionRunnable(new ShutdownSubchannel()),
- SUBCHANNEL_SHUTDOWN_DELAY_SECONDS, TimeUnit.SECONDS,
- transportFactory.getScheduledExecutorService());
- return;
- }
- // When terminating == true, no more real streams will be created. It's safe and also
- // desirable to shutdown timely.
- subchannel.shutdown(SHUTDOWN_STATUS);
- }
-
- @Override
- public void requestConnection() {
- logWarningIfNotInSyncContext("Subchannel.requestConnection()");
- checkState(started, "not started");
- subchannel.obtainActiveTransport();
- }
-
- @Override
- public List getAllAddresses() {
- logWarningIfNotInSyncContext("Subchannel.getAllAddresses()");
- checkState(started, "not started");
- return subchannel.getAddressGroups();
- }
-
- @Override
- public Attributes getAttributes() {
- return args.getAttributes();
- }
-
- @Override
- public String toString() {
- return subchannelLogId.toString();
- }
-
- @Override
- public Channel asChannel() {
- checkState(started, "not started");
- return new SubchannelChannel(
- subchannel, balancerRpcExecutorHolder.getExecutor(),
- transportFactory.getScheduledExecutorService(),
- callTracerFactory.create());
- }
-
- @Override
- public Object getInternalSubchannel() {
- checkState(started, "Subchannel is not started");
- return subchannel;
- }
-
- @Override
- public ChannelLogger getChannelLogger() {
- return subchannelLogger;
- }
-
- @Override
- public void updateAddresses(List addrs) {
- syncContext.throwIfNotInThisSynchronizationContext();
- subchannel.updateAddresses(addrs);
- }
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("logId", logId.getId())
- .add("target", target)
- .toString();
- }
-
- /**
- * Called from syncContext.
- */
- private final class DelayedTransportListener implements ManagedClientTransport.Listener {
- @Override
- public void transportShutdown(Status s) {
- checkState(shutdown.get(), "Channel must have been shut down");
- }
-
- @Override
- public void transportReady() {
- // Don't care
- }
-
- @Override
- public void transportInUse(final boolean inUse) {
- inUseStateAggregator.updateObjectInUse(delayedTransport, inUse);
- }
-
- @Override
- public void transportTerminated() {
- checkState(shutdown.get(), "Channel must have been shut down");
- terminating = true;
- shutdownNameResolverAndLoadBalancer(false);
- // No need to call channelStateManager since we are already in SHUTDOWN state.
- // Until LoadBalancer is shutdown, it may still create new subchannels. We catch them
- // here.
- maybeShutdownNowSubchannels();
- maybeTerminateChannel();
- }
- }
-
- /**
- * Must be accessed from syncContext.
- */
- private final class IdleModeStateAggregator extends InUseStateAggregator {
- @Override
- protected void handleInUse() {
- exitIdleMode();
- }
-
- @Override
- protected void handleNotInUse() {
- if (shutdown.get()) {
- return;
- }
- rescheduleIdleTimer();
- }
- }
-
- /**
- * Lazily request for Executor from an executor pool.
- */
- private static final class ExecutorHolder {
- private final ObjectPool extends Executor> pool;
- private Executor executor;
-
- ExecutorHolder(ObjectPool extends Executor> executorPool) {
- this.pool = checkNotNull(executorPool, "executorPool");
- }
-
- synchronized Executor getExecutor() {
- if (executor == null) {
- executor = checkNotNull(pool.getObject(), "%s.getObject()", executor);
- }
- return executor;
- }
-
- synchronized void release() {
- if (executor != null) {
- executor = pool.returnObject(executor);
- }
- }
- }
-
- private static final class RestrictedScheduledExecutor implements ScheduledExecutorService {
- final ScheduledExecutorService delegate;
-
- private RestrictedScheduledExecutor(ScheduledExecutorService delegate) {
- this.delegate = checkNotNull(delegate, "delegate");
- }
-
- @Override
- public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) {
- return delegate.schedule(callable, delay, unit);
- }
-
- @Override
- public ScheduledFuture> schedule(Runnable cmd, long delay, TimeUnit unit) {
- return delegate.schedule(cmd, delay, unit);
- }
-
- @Override
- public ScheduledFuture> scheduleAtFixedRate(
- Runnable command, long initialDelay, long period, TimeUnit unit) {
- return delegate.scheduleAtFixedRate(command, initialDelay, period, unit);
- }
-
- @Override
- public ScheduledFuture> scheduleWithFixedDelay(
- Runnable command, long initialDelay, long delay, TimeUnit unit) {
- return delegate.scheduleWithFixedDelay(command, initialDelay, delay, unit);
- }
-
- @Override
- public boolean awaitTermination(long timeout, TimeUnit unit)
- throws InterruptedException {
- return delegate.awaitTermination(timeout, unit);
- }
-
- @Override
- public List> invokeAll(Collection extends Callable> tasks)
- throws InterruptedException {
- return delegate.invokeAll(tasks);
- }
-
- @Override
- public List> invokeAll(
- Collection extends Callable> tasks, long timeout, TimeUnit unit)
- throws InterruptedException {
- return delegate.invokeAll(tasks, timeout, unit);
- }
-
- @Override
- public T invokeAny(Collection extends Callable> tasks)
- throws InterruptedException, ExecutionException {
- return delegate.invokeAny(tasks);
- }
-
- @Override
- public T invokeAny(Collection extends Callable> tasks, long timeout, TimeUnit unit)
- throws InterruptedException, ExecutionException, TimeoutException {
- return delegate.invokeAny(tasks, timeout, unit);
- }
-
- @Override
- public boolean isShutdown() {
- return delegate.isShutdown();
- }
-
- @Override
- public boolean isTerminated() {
- return delegate.isTerminated();
- }
-
- @Override
- public void shutdown() {
- throw new UnsupportedOperationException("Restricted: shutdown() is not allowed");
- }
-
- @Override
- public List shutdownNow() {
- throw new UnsupportedOperationException("Restricted: shutdownNow() is not allowed");
- }
-
- @Override
- public Future submit(Callable task) {
- return delegate.submit(task);
- }
-
- @Override
- public Future> submit(Runnable task) {
- return delegate.submit(task);
- }
-
- @Override
- public Future submit(Runnable task, T result) {
- return delegate.submit(task, result);
- }
-
- @Override
- public void execute(Runnable command) {
- delegate.execute(command);
- }
- }
-
- @VisibleForTesting
- static final class ScParser extends NameResolver.ServiceConfigParser {
-
- private final boolean retryEnabled;
- private final int maxRetryAttemptsLimit;
- private final int maxHedgedAttemptsLimit;
- private final AutoConfiguredLoadBalancerFactory2 autoLoadBalancerFactory;
- private final ChannelLogger channelLogger;
-
- ScParser(
- boolean retryEnabled,
- int maxRetryAttemptsLimit,
- int maxHedgedAttemptsLimit,
- AutoConfiguredLoadBalancerFactory2 autoLoadBalancerFactory,
- ChannelLogger channelLogger) {
- this.retryEnabled = retryEnabled;
- this.maxRetryAttemptsLimit = maxRetryAttemptsLimit;
- this.maxHedgedAttemptsLimit = maxHedgedAttemptsLimit;
- this.autoLoadBalancerFactory =
- checkNotNull(autoLoadBalancerFactory, "autoLoadBalancerFactory");
- this.channelLogger = checkNotNull(channelLogger, "channelLogger");
- }
-
- @Override
- public ConfigOrError parseServiceConfig(Map rawServiceConfig) {
- try {
- Object loadBalancingPolicySelection;
- ConfigOrError choiceFromLoadBalancer =
- autoLoadBalancerFactory.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
- if (choiceFromLoadBalancer == null) {
- loadBalancingPolicySelection = null;
- } else if (choiceFromLoadBalancer.getError() != null) {
- return ConfigOrError.fromError(choiceFromLoadBalancer.getError());
- } else {
- loadBalancingPolicySelection = choiceFromLoadBalancer.getConfig();
- }
- return ConfigOrError.fromConfig(
- ManagedChannelServiceConfig2.fromServiceConfig(
- rawServiceConfig,
- retryEnabled,
- maxRetryAttemptsLimit,
- maxHedgedAttemptsLimit,
- loadBalancingPolicySelection));
- } catch (RuntimeException e) {
- return ConfigOrError.fromError(
- Status.UNKNOWN.withDescription("failed to parse service config").withCause(e));
- }
- }
- }
-
- private void logWarningIfNotInSyncContext(String method) {
- try {
- syncContext.throwIfNotInThisSynchronizationContext();
- } catch (IllegalStateException e) {
- logger.log(Level.WARNING,
- method + " should be called from SynchronizationContext. "
- + "This warning will become an exception in a future release. "
- + "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 rawServiceConfig;
- ManagedChannelServiceConfig2 managedChannelServiceConfig;
-
- ServiceConfigHolder(
- Map rawServiceConfig, ManagedChannelServiceConfig2 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();
- }
- }
-}
diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelServiceConfig.java b/core/src/main/java/io/grpc/internal/ManagedChannelServiceConfig.java
index e01bb1b524..32b9433b1e 100644
--- a/core/src/main/java/io/grpc/internal/ManagedChannelServiceConfig.java
+++ b/core/src/main/java/io/grpc/internal/ManagedChannelServiceConfig.java
@@ -39,9 +39,7 @@ final class ManagedChannelServiceConfig {
private final Map serviceMethodMap;
private final Map 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(),
+ new HashMap(),
+ /* 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.
*/
diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelServiceConfig2.java b/core/src/main/java/io/grpc/internal/ManagedChannelServiceConfig2.java
deleted file mode 100644
index e20336215c..0000000000
--- a/core/src/main/java/io/grpc/internal/ManagedChannelServiceConfig2.java
+++ /dev/null
@@ -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 serviceMethodMap;
- private final Map serviceMap;
- @Nullable
- private final Throttle retryThrottling;
- @Nullable
- private final Object loadBalancingConfig;
-
- ManagedChannelServiceConfig2(
- Map serviceMethodMap,
- Map 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(),
- new HashMap(),
- /* retryThrottling= */ null,
- /* loadBalancingConfig= */ null);
- }
-
- /**
- * Parses the Channel level config values (e.g. excludes load balancing)
- */
- static ManagedChannelServiceConfig2 fromServiceConfig(
- Map serviceConfig,
- boolean retryEnabled,
- int maxRetryAttemptsLimit,
- int maxHedgedAttemptsLimit,
- @Nullable Object loadBalancingConfig) {
- Throttle retryThrottling = null;
- if (retryEnabled) {
- retryThrottling = ServiceConfigUtil.getThrottlePolicy(serviceConfig);
- }
- Map serviceMethodMap = new HashMap<>();
- Map 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> methodConfigs =
- ServiceConfigUtil.getMethodConfigFromServiceConfig(serviceConfig);
-
- if (methodConfigs == null) {
- // this is surprising, but possible.
- return new ManagedChannelServiceConfig2(
- serviceMethodMap, serviceMap, retryThrottling, loadBalancingConfig);
- }
-
- for (Map methodConfig : methodConfigs) {
- MethodInfo info = new MethodInfo(
- methodConfig, retryEnabled, maxRetryAttemptsLimit, maxHedgedAttemptsLimit);
-
- List> nameList =
- ServiceConfigUtil.getNameListFromMethodConfig(methodConfig);
-
- checkArgument(
- nameList != null && !nameList.isEmpty(), "no names in method config %s", methodConfig);
- for (Map 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 getServiceMap() {
- return serviceMap;
- }
-
- /**
- * Returns the per-method configuration for the channel.
- */
- Map 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 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 retryPolicyMap =
- retryEnabled ? ServiceConfigUtil.getRetryPolicyFromMethodConfig(methodConfig) : null;
- retryPolicy = retryPolicyMap == null
- ? RetryPolicy.DEFAULT : retryPolicy(retryPolicyMap, maxRetryAttemptsLimit);
-
- Map 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 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 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));
- }
- }
-}
diff --git a/core/src/main/java/io/grpc/internal/ServiceConfigInterceptor.java b/core/src/main/java/io/grpc/internal/ServiceConfigInterceptor.java
index f33eaf39f7..f27f9efa78 100644
--- a/core/src/main/java/io/grpc/internal/ServiceConfigInterceptor.java
+++ b/core/src/main/java/io/grpc/internal/ServiceConfigInterceptor.java
@@ -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
- = new AtomicReference<>();
+ final AtomicReference 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 serviceConfig) {
- // TODO(carl-mastrangelo): delete this.
- ManagedChannelServiceConfig conf;
- if (serviceConfig == null) {
- conf = new ManagedChannelServiceConfig(
- new HashMap(), new HashMap(), null, null);
- } else {
- conf = ManagedChannelServiceConfig.fromServiceConfig(
- serviceConfig, retryEnabled, maxRetryAttemptsLimit, maxHedgedAttemptsLimit, null);
- }
- managedChannelServiceConfig.set(conf);
+ void handleUpdate(@Nullable ManagedChannelServiceConfig serviceConfig) {
+ managedChannelServiceConfig.set(serviceConfig);
initComplete = true;
}
diff --git a/core/src/main/java/io/grpc/internal/ServiceConfigInterceptor2.java b/core/src/main/java/io/grpc/internal/ServiceConfigInterceptor2.java
deleted file mode 100644
index bd1476d4b4..0000000000
--- a/core/src/main/java/io/grpc/internal/ServiceConfigInterceptor2.java
+++ /dev/null
@@ -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 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 RETRY_POLICY_KEY =
- CallOptions.Key.create("internal-retry-policy");
- static final CallOptions.Key HEDGING_POLICY_KEY =
- CallOptions.Key.create("internal-hedging-policy");
-
- @Override
- public ClientCall interceptCall(
- final MethodDescriptor 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.
- *
- * 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.
- *
- *
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;
- }
-}
diff --git a/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java b/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java
index 1a39671af5..f7f79f0b5f 100644
--- a/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java
+++ b/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java
@@ -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(String target) {
super(target);
diff --git a/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest.java b/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest.java
index 66e69bc4b2..9232772778 100644
--- a/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest.java
+++ b/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest.java
@@ -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 nextParsedConfigOrError =
+ new AtomicReference<>(ConfigOrError.fromConfig("default"));
+ private final AtomicReference 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 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 serviceConfig =
+ parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": { } } ] }");
+ ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
+
final List 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 serviceConfig =
+ Map 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 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> 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 servers =
Arrays.asList(
@@ -339,11 +349,11 @@ public class AutoConfiguredLoadBalancerFactoryTest {
Map serviceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
+ ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(serviceConfig, helper.getChannelLogger());
Status handleResult = lb.tryHandleResolvedAddresses(
ResolvedAddresses.newBuilder()
.setAddresses(Collections.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 serviceConfig =
+ Map rawServiceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb2\": { \"setting1\": \"high\" } } ] }");
+ ConfigOrError lbConfigs =
+ lbf.parseLoadBalancerPolicy(rawServiceConfig, helper.getChannelLogger());
Status handleResult = lb.tryHandleResolvedAddresses(
ResolvedAddresses.newBuilder()
.setAddresses(Collections.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 lbConfig = actualAttributes.get(LoadBalancer.ATTR_LOAD_BALANCING_CONFIG);
- assertThat(lbConfig).isEqualTo(Collections.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 serviceConfig = null;
+ PolicySelection policySelection = null;
List 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 serviceConfig = null;
+ PolicySelection policySelection = null;
List 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 serviceConfig = null;
+ PolicySelection policySelection = null;
List 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 serviceConfig = new HashMap<>();
- serviceConfig.put("loadBalancingPolicy", "round_robin");
+ Map rawServiceConfig =
+ parseConfig("{\"loadBalancingPolicy\": \"round_robin\"}");
+
+ ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
+ assertThat(lbConfig.getConfig()).isNotNull();
+ PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
List servers =
Arrays.asList(
new EquivalentAddressGroup(
@@ -443,21 +457,23 @@ public class AutoConfiguredLoadBalancerFactoryTest {
new EquivalentAddressGroup(
new SocketAddress(){}));
List 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.emptyMap());
verifyZeroInteractions(channelLogger);
}
- @SuppressWarnings("unchecked")
@Test
public void decideLoadBalancerProvider_serviceConfigLbConfig() throws Exception {
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
- Map serviceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
+ Map rawServiceConfig =
+ parseConfig("{\"loadBalancingConfig\": [{\"round_robin\": {}}]}");
+
+ ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
+ assertThat(lbConfig.getConfig()).isNotNull();
+ PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
List servers =
Arrays.asList(
new EquivalentAddressGroup(
@@ -466,55 +482,54 @@ public class AutoConfiguredLoadBalancerFactoryTest {
new EquivalentAddressGroup(
new SocketAddress(){}));
List 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.emptyMap());
verifyZeroInteractions(channelLogger);
}
@Test
public void decideLoadBalancerProvider_grpclbConfigPropagated() throws Exception {
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(new TestHelper());
- Map serviceConfig =
+ Map 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 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 serviceConfig =
- parseConfig(
- "{\"loadBalancingConfig\": ["
- + "{\"unavail\": {} }"
- + "] }");
+
List 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 serviceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"grpclb\": {} } ] }");
List 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 serviceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"grpclb\": {} } ] }");
List 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 serviceConfig = new HashMap<>();
- serviceConfig.put("loadBalancingPolicy", "round_robin");
- List 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 serviceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {\"setting1\": \"high\"} } ] }");
+ Map rawServiceConfig =
+ parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
+ ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
+ assertThat(lbConfigs.getConfig()).isNotNull();
+ PolicySelection policySelection = (PolicySelection) lbConfigs.getConfig();
List 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 serviceConfig = new HashMap<>();
- serviceConfig.put("loadBalancingPolicy", "MAGIC_BALANCER");
- List 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 serviceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"magic_balancer\": {} } ] }");
- List 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 serviceConfig =
- parseConfig(
- "{\"loadBalancingConfig\": [ {\"magic_balancer\": {} }, {\"round_robin\": {} } ] }");
- List 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 servers =
- Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
- PolicySelection selection = lb.decideLoadBalancerProvider(
- servers, Collections.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 servers =
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
@@ -734,38 +666,44 @@ public class AutoConfiguredLoadBalancerFactoryTest {
assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
verifyNoMoreInteractions(channelLogger);
- Map serviceConfig = new HashMap<>();
- serviceConfig.put("loadBalancingPolicy", "round_robin");
+ ConfigOrError testLbParsedConfig = ConfigOrError.fromConfig("foo");
+ nextParsedConfigOrError.set(testLbParsedConfig);
+ Map 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 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 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 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 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 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 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 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 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 nextParsedLbPolicyConfig;
- FakeLoadBalancerProvider(String policyName, LoadBalancer balancer) {
+ FakeLoadBalancerProvider(
+ String policyName,
+ LoadBalancer balancer,
+ AtomicReference 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 rawLoadBalancingPolicyConfig) {
+ if (nextParsedLbPolicyConfig == null) {
+ return super.parseLoadBalancingPolicyConfig(rawLoadBalancingPolicyConfig);
+ }
+ return nextParsedLbPolicyConfig.get();
+ }
}
}
diff --git a/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest2.java b/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest2.java
deleted file mode 100644
index c60c6eb439..0000000000
--- a/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest2.java
+++ /dev/null
@@ -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 nextParsedConfigOrError =
- new AtomicReference<>(ConfigOrError.fromConfig("default"));
- private final AtomicReference 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 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 serviceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": { } } ] }");
- ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig, channelLogger);
-
- final List 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 rawServiceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
- ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
- assertThat(lbConfigs.getConfig()).isNotNull();
-
- final List 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 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> 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 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 serviceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
- ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(serviceConfig, helper.getChannelLogger());
- Status handleResult = lb.tryHandleResolvedAddresses(
- ResolvedAddresses.newBuilder()
- .setAddresses(Collections.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 rawServiceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"test_lb2\": { \"setting1\": \"high\" } } ] }");
- ConfigOrError lbConfigs =
- lbf.parseLoadBalancerPolicy(rawServiceConfig, helper.getChannelLogger());
- Status handleResult = lb.tryHandleResolvedAddresses(
- ResolvedAddresses.newBuilder()
- .setAddresses(Collections.emptyList())
- .setLoadBalancingPolicyConfig(lbConfigs.getConfig())
- .build());
-
- assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
- assertThat(lb.getDelegate()).isSameInstanceAs(testLbBalancer2);
- assertThat(testLbBalancer2.canHandleEmptyAddressListFromNameResolution()).isTrue();
- ArgumentCaptor 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 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 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 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 rawServiceConfig =
- parseConfig("{\"loadBalancingPolicy\": \"round_robin\"}");
-
- ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
- assertThat(lbConfig.getConfig()).isNotNull();
- PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
- List servers =
- Arrays.asList(
- new EquivalentAddressGroup(
- new SocketAddress(){},
- Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()),
- new EquivalentAddressGroup(
- new SocketAddress(){}));
- List 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 rawServiceConfig =
- parseConfig("{\"loadBalancingConfig\": [{\"round_robin\": {}}]}");
-
- ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
- assertThat(lbConfig.getConfig()).isNotNull();
- PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
- List servers =
- Arrays.asList(
- new EquivalentAddressGroup(
- new SocketAddress(){},
- Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()),
- new EquivalentAddressGroup(
- new SocketAddress(){}));
- List 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 rawServiceConfig =
- parseConfig(
- "{\"loadBalancingConfig\": ["
- + "{\"grpclb\": {\"childPolicy\": [ {\"pick_first\": {} } ] } }"
- + "] }");
- ConfigOrError lbConfig = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
- assertThat(lbConfig.getConfig()).isNotNull();
- PolicySelection policySelection = (PolicySelection) lbConfig.getConfig();
-
- List 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 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 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 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 rawServiceConfig =
- parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
- ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig, channelLogger);
- assertThat(lbConfigs.getConfig()).isNotNull();
- PolicySelection policySelection = (PolicySelection) lbConfigs.getConfig();
- List 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 servers =
- Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
- Helper helper = new TestHelper() {
- @Override
- @Deprecated
- public Subchannel createSubchannel(List 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 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 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 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 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 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 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 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 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 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 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 parseConfig(String json) throws Exception {
- return (Map) 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 addrs;
- final Attributes attrs;
-
- @Override
- public void start(SubchannelStateListener listener) {
- }
-
- @Override
- public void shutdown() {
- }
-
- @Override
- public void requestConnection() {
- }
-
- @Override
- public List getAllAddresses() {
- return addrs;
- }
-
- @Override
- public Attributes getAttributes() {
- return attrs;
- }
-
- @Override
- public void updateAddresses(List 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 nextParsedLbPolicyConfig;
-
- FakeLoadBalancerProvider(
- String policyName,
- LoadBalancer balancer,
- AtomicReference 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 rawLoadBalancingPolicyConfig) {
- if (nextParsedLbPolicyConfig == null) {
- return super.parseLoadBalancingPolicyConfig(rawLoadBalancingPolicyConfig);
- }
- return nextParsedLbPolicyConfig.get();
- }
- }
-}
diff --git a/core/src/test/java/io/grpc/internal/HedgingPolicyTest.java b/core/src/test/java/io/grpc/internal/HedgingPolicyTest.java
index 8280623286..7c640862f8 100644
--- a/core/src/test/java/io/grpc/internal/HedgingPolicyTest.java
+++ b/core/src/test/java/io/grpc/internal/HedgingPolicyTest.java
@@ -59,10 +59,17 @@ public class HedgingPolicyTest {
@SuppressWarnings("unchecked")
Map serviceConfig = (Map) 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 builder = TestMethodDescriptors.voidMethod().toBuilder();
@@ -131,10 +138,17 @@ public class HedgingPolicyTest {
@SuppressWarnings("unchecked")
Map serviceConfig = (Map) 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 builder = TestMethodDescriptors.voidMethod().toBuilder();
diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java
index 191533333c..22cd9879e8 100644
--- a/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java
+++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java
@@ -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 resolvedAddressCaptor =
+ ArgumentCaptor.forClass(ResolvedAddresses.class);
+ verify(mockLoadBalancer).handleResolvedAddresses(resolvedAddressCaptor.capture());
+ assertThat(resolvedAddressCaptor.getValue().getAddresses())
+ .containsExactlyElementsIn(servers);
}
@Test
diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest2.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest2.java
deleted file mode 100644
index 840bdaa22d..0000000000
--- a/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest2.java
+++ /dev/null
@@ -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 method =
- MethodDescriptor.newBuilder()
- .setType(MethodType.UNKNOWN)
- .setFullMethodName("service/method")
- .setRequestMarshaller(new StringMarshaller())
- .setResponseMarshaller(new IntegerMarshaller())
- .build();
-
- private final List servers = Lists.newArrayList();
- private final ObjectPool executorPool =
- new FixedObjectPool(executor.getScheduledExecutorService());
- private final ObjectPool oobExecutorPool =
- new FixedObjectPool(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 mockCallListener;
- @Mock private ClientCall.Listener mockCallListener2;
- @Captor private ArgumentCaptor nameResolverListenerCaptor;
- private BlockingQueue 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(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.emptyList(),
- TimeProvider.SYSTEM_TIME_PROVIDER);
- newTransports = TestUtils.captureTransports(mockTransportFactory);
-
- for (int i = 0; i < 2; i++) {
- ArrayList 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 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 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 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 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