diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/BaseHostSelector.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/BaseHostSelector.java index 8f8b7daa62..ddf1f90e31 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/BaseHostSelector.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/BaseHostSelector.java @@ -39,12 +39,12 @@ abstract class BaseHostSelector selectConnection0(Predicate selector, @Nullable ContextMap context, + abstract Single selectConnection0(Predicate selector, @Nullable ContextMap context, boolean forceNewConnectionAndReserve); @Override public final Single selectConnection(Predicate selector, @Nullable ContextMap context, - boolean forceNewConnectionAndReserve) { + boolean forceNewConnectionAndReserve) { return hosts.isEmpty() ? noHostsFailure() : selectConnection0(selector, context, forceNewConnectionAndReserve); } @@ -60,15 +60,15 @@ public final boolean isHealthy() { return anyHealthy(hosts); } - protected final List> hosts() { + final List> hosts() { return hosts; } - protected final String lbDescription() { + final String lbDescription() { return lbDescription; } - protected final Single noActiveHostsFailure(List> usedHosts) { + final Single noActiveHostsFailure(List> usedHosts) { return failed(Exceptions.StacklessNoActiveHostException.newInstance( lbDescription() + ": Failed to pick an active host. Either all are busy, expired, or unhealthy: " + usedHosts, this.getClass(), "selectConnection(...)")); @@ -76,7 +76,7 @@ protected final Single noActiveHostsFailure(List selectFromHost(Host host, Predicate selector, + final Single selectFromHost(Host host, Predicate selector, boolean forceNewConnectionAndReserve, @Nullable ContextMap contextMap) { // First see if we can get an existing connection regardless of health status. if (!forceNewConnectionAndReserve) { diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/ConnectionPoolPolicies.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/ConnectionPoolPolicies.java index c27dfe8430..fda4acef80 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/ConnectionPoolPolicies.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/ConnectionPoolPolicies.java @@ -17,6 +17,9 @@ import io.servicetalk.client.api.LoadBalancedConnection; +/** + * A factory to create different {@link ConnectionPoolPolicy} variants. + */ public final class ConnectionPoolPolicies { private static final int DEFAULT_MAX_EFFORT = 5; private static final int DEFAULT_LINEAR_SEARCH_SPACE = 16; @@ -38,14 +41,15 @@ private ConnectionPoolPolicies() { * If the core pool cannot satisfy the load traffic can spill over to extra connections which are selected in-order. * This has the property of minimizing traffic to the latest elements added outside the core pool size, thus let * them idle out of the pool once they're no longer necessary. + * * @param corePoolSize the size of the core pool. * @param forceCorePool whether to avoid selecting connections from the core pool until it has reached the * configured core pool size. * @param the concrete type of the {@link LoadBalancedConnection} * @return the configured {@link ConnectionPoolPolicy}. */ - public static ConnectionPoolPolicy - corePool(final int corePoolSize, final boolean forceCorePool) { + public static ConnectionPoolPolicy corePool(final int corePoolSize, + final boolean forceCorePool) { return CorePoolConnectionSelector.factory(corePoolSize, forceCorePool); } @@ -56,6 +60,7 @@ private ConnectionPoolPolicies() { * traffic to connections in the order they were created in linear order up until a configured quantity. After * this linear pool is exhausted the remaining connections will be selected from at random. Prioritizing traffic * to the existing connections will let tailing connections be removed due to idleness. + * * @param the concrete type of the {@link LoadBalancedConnection} * @return the configured {@link ConnectionPoolPolicy}. */ @@ -70,12 +75,13 @@ public static ConnectionPoolPolicy linearS * traffic to connections in the order they were created in linear order up until a configured quantity. After * this linear pool is exhausted the remaining connections will be selected from at random. Prioritizing traffic * to the existing connections will let tailing connections be removed due to idleness. + * * @param linearSearchSpace the space to search linearly before resorting to random selection for remaining * connections. * @param the concrete type of the {@link LoadBalancedConnection} * @return the configured {@link ConnectionPoolPolicy}. */ - public static ConnectionPoolPolicy linearSearch(int linearSearchSpace) { + public static ConnectionPoolPolicy linearSearch(final int linearSearchSpace) { return LinearSearchConnectionSelector.factory(linearSearchSpace); } @@ -83,19 +89,25 @@ public static ConnectionPoolPolicy linearS * A {@link ConnectionPoolPolicy} that attempts to discern between the health of individual connections. * If individual connections have health data the P2C policy can be used to bias traffic toward the best * connections. This has the following algorithm: - * - Randomly select two connections from the 'core pool' (pick-two). - * - Try to select the 'best' of the two connections. - * - If we fail to select the best connection, try the other connection. - * - If both connections fail, repeat the pick-two operation for up to maxEffort attempts, begin linear iteration - * through the remaining connections searching for an acceptable connection. + *
    + *
  1. Randomly select two connections from the 'core pool' (pick-two). + *
      + *
    1. Try to select the 'best' of the two connections.
    2. + *
    3. If we fail to select the best connection, try the other connection.
    4. + *
    + *
  2. + *
  3. If both connections fail, repeat the pick-two operation for up to maxEffort attempts, begin linear + * iteration through the remaining connections searching for an acceptable connection.
  4. + *
+ * * @param corePoolSize the size of the core pool. * @param forceCorePool whether to avoid selecting connections from the core pool until it has reached the * configured core pool size. * @param the concrete type of the {@link LoadBalancedConnection} * @return the configured {@link ConnectionPoolPolicy}. */ - public static ConnectionPoolPolicy - p2c(int corePoolSize, boolean forceCorePool) { + public static ConnectionPoolPolicy p2c(final int corePoolSize, + final boolean forceCorePool) { return p2c(DEFAULT_MAX_EFFORT, corePoolSize, forceCorePool); } @@ -103,11 +115,17 @@ public static ConnectionPoolPolicy linearS * A {@link ConnectionPoolPolicy} that attempts to discern between the health of individual connections. * If individual connections have health data the P2C policy can be used to bias traffic toward the best * connections. This has the following algorithm: - * - Randomly select two connections from the 'core pool' (pick-two). - * - Try to select the 'best' of the two connections. - * - If we fail to select the best connection, try the other connection. - * - If both connections fail, repeat the pick-two operation for up to maxEffort attempts, begin linear iteration - * through the remaining connections searching for an acceptable connection. + *
    + *
  1. Randomly select two connections from the 'core pool' (pick-two). + *
      + *
    1. Try to select the 'best' of the two connections.
    2. + *
    3. If we fail to select the best connection, try the other connection.
    4. + *
    + *
  2. + *
  3. If both connections fail, repeat the pick-two operation for up to maxEffort attempts, begin linear + * iteration through the remaining connections searching for an acceptable connection.
  4. + *
+ * * @param maxEffort the maximum number of attempts to pick a healthy connection from the core pool. * @param corePoolSize the size of the core pool. * @param forceCorePool whether to avoid selecting connections from the core pool until it has reached the @@ -115,8 +133,9 @@ public static ConnectionPoolPolicy linearS * @param the concrete type of the {@link LoadBalancedConnection} * @return the configured {@link ConnectionPoolPolicy}. */ - public static - ConnectionPoolPolicy p2c(int maxEffort, int corePoolSize, boolean forceCorePool) { + public static ConnectionPoolPolicy p2c(final int maxEffort, + final int corePoolSize, + final boolean forceCorePool) { return P2CConnectionSelector.factory(maxEffort, corePoolSize, forceCorePool); } } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultHost.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultHost.java index e358e01c4a..91712b21a7 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultHost.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultHost.java @@ -232,10 +232,10 @@ public Single newConnection( // Just in case the connection is not closed add it to the host so we don't lose track, // duplicates will be filtered out. - return (addConnection(newCnx, null) ? + return (addConnection(newCnx) ? failedSingle : newCnx.closeAsync().concat(failedSingle)).shareContextOnSubscribe(); } - if (addConnection(newCnx, null)) { + if (addConnection(newCnx)) { return succeeded(newCnx).shareContextOnSubscribe(); } return newCnx.closeAsync().concat( @@ -317,7 +317,7 @@ public boolean canMakeNewConnections() { return state != State.EXPIRED && state != State.CLOSED; } - private boolean addConnection(final C connection, @Nullable final HealthCheck currentHealthCheck) { + private boolean addConnection(final C connection) { int addAttempt = 0; for (;;) { final ConnState previous = connStateUpdater.get(this); @@ -336,9 +336,7 @@ private boolean addConnection(final C connection, @Nullable final HealthCheck cu // connection or with passing a previous health-check (if SD turned it into ACTIVE state). In both // cases we have to cancel the "previous" ongoing health check. See "markHealthy" for more context. if (previous.isUnhealthy()) { - if (currentHealthCheck == null || previous.healthCheck != currentHealthCheck) { - cancelIfHealthCheck(previous); - } + cancelIfHealthCheck(previous); // If we transitioned from unhealth to healthy we need to let the observer know. hostObserver.onHostRevived(); } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultRequestTracker.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultRequestTracker.java index 80c057ecc7..073cd9e894 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultRequestTracker.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultRequestTracker.java @@ -69,7 +69,7 @@ abstract class DefaultRequestTracker implements RequestTracker, ScoreSupplier { * The current time in nanoseconds. * @return the current time in nanoseconds. */ - protected abstract long currentTimeNanos(); + abstract long currentTimeNanos(); @Override public final long beforeRequestStart() { diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DelegatingLoadBalancerBuilder.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DelegatingLoadBalancerBuilder.java index 9f5fd99107..65725edde8 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DelegatingLoadBalancerBuilder.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DelegatingLoadBalancerBuilder.java @@ -89,4 +89,9 @@ public LoadBalancerBuilder connectionPoolPolicy( public LoadBalancerFactory build() { return delegate.build(); } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}'; + } } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java index 7bad2548fd..2ef472a7c5 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java @@ -20,8 +20,7 @@ import io.servicetalk.concurrent.Cancellable; /** - * An abstraction used by a {@link Host} to interact with the {@link OutlierDetector} currently monitoring - * the host. + * An abstraction used by a {@link Host} to interact with the {@link OutlierDetector} currently monitoring the host. *

* This abstraction serves as a sort of two-way channel between a host and the health check system: the * health check system can give the host information about it's perceived health and the host can give the diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/HostSelector.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/HostSelector.java index b5b2d23955..489db08e46 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/HostSelector.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/HostSelector.java @@ -42,7 +42,7 @@ interface HostSelector { /** * Select or establish a new connection from an existing Host. - * + *

* This method will be called concurrently with other selectConnection calls and * hostSetChanged calls and must be thread safe under those conditions. */ @@ -72,6 +72,7 @@ Single selectConnection(Predicate selector, @Nullable ContextMap context, /** * The size of the host candidate pool for this host selector. + *

* Note that this is primarily for observability purposes. * @return the size of the host candidate pool for this host selector. */ diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java index 021fbe4521..6ec29ced67 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java @@ -64,6 +64,7 @@ public interface LoadBalancerBuilder { /** * Set the {@code loadBalancingPolicy} to use with this load balancer. + * * @param loadBalancingPolicy the {@code loadBalancingPolicy} to use * @return {@code this} */ @@ -72,6 +73,7 @@ LoadBalancerBuilder loadBalancingPolicy( /** * Set the {@link LoadBalancerObserverFactory} to use with this load balancer. + * * @param loadBalancerObserverFactory the {@link LoadBalancerObserverFactory} to use, or {@code null} to not use an * observer. * @return {code this} @@ -81,9 +83,11 @@ LoadBalancerBuilder loadBalancerObserver( /** * Set the {@link OutlierDetectorConfig} to use with this load balancer. + *

* The outlier detection system works in conjunction with the load balancing policy to attempt to avoid hosts * that have been determined to be unhealthy or slow. The details of the selection process are determined by the * {@link LoadBalancingPolicy} while the health status is determined by the outlier detection configuration. + * * @param outlierDetectorConfig the {@link OutlierDetectorConfig} to use, or {@code null} to use the default * outlier detection. * @return {code this} @@ -93,6 +97,7 @@ LoadBalancerBuilder loadBalancerObserver( /** * Set the {@link ConnectionPoolPolicy} to use with this load balancer. + * * @param connectionPoolPolicy the factory of connection pooling strategies to use. * @return {@code this} */ diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilderProvider.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilderProvider.java index ff29432d41..098cd78530 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilderProvider.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilderProvider.java @@ -18,8 +18,9 @@ import io.servicetalk.client.api.LoadBalancedConnection; /** - * Provider for {@link LoadBalancerBuilder}. + * Provider for {@link LoadBalancerBuilder} that can be registered using {@link java.util.ServiceLoader}. */ +@FunctionalInterface public interface LoadBalancerBuilderProvider { /** @@ -28,10 +29,9 @@ public interface LoadBalancerBuilderProvider { * This method may return the pre-initialized {@code builder} as-is, or apply custom builder settings before * returning it, or wrap it ({@link DelegatingLoadBalancerBuilder} may be helpful). * - * @param id a (unique) identifier used to identify the underlying {@link io.servicetalk.client.api.LoadBalancer}. + * @param id a (unique) identifier used to identify the underlying {@link LoadBalancerBuilder}. * @param builder pre-initialized {@link LoadBalancerBuilder}. - * @return a {@link LoadBalancerBuilder} based on the pre-initialized - * {@link LoadBalancerBuilder}. + * @return a {@link LoadBalancerBuilder} based on the pre-initialized {@link LoadBalancerBuilder}. * @param The resolved address type. * @param The type of connection. */ diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerObserver.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerObserver.java index 8e1b363a4b..e1406feb40 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerObserver.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancerObserver.java @@ -33,11 +33,6 @@ public interface LoadBalancerObserver { */ HostObserver hostObserver(Object resolvedAddress); - /** - * Callback for when connection selection fails due to no hosts being available. - */ - void onNoHostsAvailable(); - /** * Callback for monitoring the changes due to a service discovery update. * @param events the collection of {@link ServiceDiscovererEvent}s received by the load balancer. @@ -47,13 +42,6 @@ public interface LoadBalancerObserver { void onServiceDiscoveryEvent(Collection> events, int oldHostSetSize, int newHostSetSize); - /** - * Callback for when connection selection fails due to all hosts being inactive. - * @param hostSetSize the size of the current host set. - * @param exception an exception with more details about the failure. - */ - void onNoActiveHostsAvailable(int hostSetSize, NoActiveHostException exception); - /** * Callback for when the set of hosts used by the load balancer has changed. This set may not * exactly reflect the state of the service discovery system due to filtering of zero-weight @@ -64,6 +52,18 @@ void onServiceDiscoveryEvent(Collection> eve default void onHostSetChanged(Collection newHosts) { } + /** + * Callback for when connection selection fails due to no hosts being available. + */ + void onNoHostsAvailable(); + + /** + * Callback for when connection selection fails due to all hosts being inactive. + * @param hostSetSize the size of the current host set. + * @param exception an exception with more details about the failure. + */ + void onNoActiveHostsAvailable(int hostSetSize, NoActiveHostException exception); + /** * An observer for {@link io.servicetalk.loadbalancer.Host} events. */ diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancers.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancers.java index fd9c80ceb0..d0b9ef26a0 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancers.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancers.java @@ -16,6 +16,7 @@ package io.servicetalk.loadbalancer; import io.servicetalk.client.api.LoadBalancedConnection; +import io.servicetalk.client.api.LoadBalancer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,7 +26,7 @@ import static io.servicetalk.utils.internal.ServiceLoaderUtils.loadProviders; /** - * A factory to create {@link DefaultLoadBalancer DefaultLoadBalancers}. + * A factory to create a {@link LoadBalancer} or a {@link LoadBalancerBuilder} to customize one. */ public final class LoadBalancers { diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancingPolicies.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancingPolicies.java index 260e8008d0..cd64390017 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancingPolicies.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/LoadBalancingPolicies.java @@ -16,7 +16,7 @@ package io.servicetalk.loadbalancer; /** - * A collections of factories for constructing a {@link LoadBalancingPolicy}. + * A factory to create different {@link LoadBalancingPolicy} variants. */ public final class LoadBalancingPolicies { @@ -26,9 +26,11 @@ private LoadBalancingPolicies() { /** * Builder for the round-robin {@link LoadBalancingPolicy}. + *

* Round-robin load balancing is a strategy that maximizes fairness of the request distribution. This comes at the * cost of being unable to bias toward better performing hosts and can only leverage the course grained * healthy/unhealthy status of a host. + * * @return a builder for the round-robin {@link LoadBalancingPolicy}. */ public static RoundRobinLoadBalancingPolicyBuilder roundRobin() { @@ -37,9 +39,11 @@ public static RoundRobinLoadBalancingPolicyBuilder roundRobin() { /** * Builder for the power of two choices (P2C) {@link LoadBalancingPolicy}. + *

* Power of Two Choices (P2C) leverages both course grained healthy/unhealthy status of a host plus additional * fine-grained scoring to prioritize hosts that are both healthy and better performing. See the * {@link P2CLoadBalancingPolicy} for more details. + * * @return a builder for the power of two choices (P2C) {@link LoadBalancingPolicy}. */ public static P2CLoadBalancingPolicyBuilder p2c() { diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/NoopOutlierDetector.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/NoopOutlierDetector.java index 0affce4a3a..c5d5539a2a 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/NoopOutlierDetector.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/NoopOutlierDetector.java @@ -83,7 +83,7 @@ public void onConnectError(long beforeConnectStart, ConnectTracker.ErrorClass er } @Override - protected long currentTimeNanos() { + long currentTimeNanos() { return executor.currentTime(TimeUnit.NANOSECONDS); } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetector.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetector.java index a0a17c35ab..0b7762ef7f 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetector.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetector.java @@ -29,9 +29,11 @@ interface OutlierDetector ext /** * Construct a new {@link HealthIndicator}. - * @note The most common use case will be to use the indicator to track a newly constructed {@link Host} but + *

+ * The most common use case will be to use the indicator to track a newly constructed {@link Host} but * it's possible that a currently live host may have its indicator replaced due to load balancing configuration * changes, so it's not safe to assume that the host that will use this tracker is exactly new. + * * @param address the resolved address of the destination. * @return new {@link HealthIndicator}. */ @@ -40,9 +42,12 @@ interface OutlierDetector ext /** * Stream of events that signal that the health status has changed for one or more of the hosts observed by the - * {@link OutlierDetector}. The events signal scenarios where hosts have transitioned from healthy to unhealthy and - * vise versa. The {@link OutlierDetector} may choose to send these events at regular intervals or immediately when - * a host has been detected unhealthy. + * {@link OutlierDetector}. + *

+ * The events signal scenarios where hosts have transitioned from healthy to unhealthy and vise versa. + * The {@link OutlierDetector} may choose to send these events at regular intervals or immediately when a host has + * been detected unhealthy. + * * @return a {@link Publisher} that represents the stream of health status changes. */ Publisher healthStatusChanged(); diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetectorConfig.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetectorConfig.java index 3fe4b9793c..453c76fe04 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetectorConfig.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetectorConfig.java @@ -17,6 +17,7 @@ import io.servicetalk.client.api.LoadBalancer; import io.servicetalk.client.api.ServiceDiscoverer; +import io.servicetalk.utils.internal.DurationUtils; import java.time.Duration; import java.util.concurrent.ThreadLocalRandom; @@ -113,8 +114,10 @@ private OutlierDetectorConfig(final Duration ewmaHalfLife, final int ewmaCancell /** * The Exponentially Weighted Moving Average (EWMA) half-life. + *

* In the context of an exponentially weighted moving average, the half-life means the time during which * historical data has the same weight as a new sample. + * * @return the Exponentially Weighted Moving Average (EWMA) half-life. */ public Duration ewmaHalfLife() { @@ -123,7 +126,9 @@ public Duration ewmaHalfLife() { /** * The penalty factor for local cancellation of requests. + *

* The latency of the cancelled request is multiplied by the provided penalty before incorporating it into the EWMA. + * * @return the penalty factor for local cancellation of requests. */ public int ewmaCancellationPenalty() { @@ -132,6 +137,7 @@ public int ewmaCancellationPenalty() { /** * Determines whether a cancellation is considered to be an error. + * * @return whether a cancellation is considered to be an error. */ public boolean cancellationIsError() { @@ -140,7 +146,9 @@ public boolean cancellationIsError() { /** * The penalty factor for requests that were classified as an error. + *

* The latency of the failed request is multiplied by the provided penalty before incorporating it into the EWMA. + * * @return the penalty factor for requests that were classified as an error. */ public int ewmaErrorPenalty() { @@ -149,8 +157,10 @@ public int ewmaErrorPenalty() { /** * The penalty factory to apply to concurrent requests. + *

* The EWMA penalty to apply to endpoints when there are concurrent requests. By penalizing endpoints with * concurrent load the traffic distribution will be smoother for algorithms that consider load metrics. + * * @return the penalty factory to use for concurrent load. */ public int concurrentRequestPenalty() { @@ -159,6 +169,7 @@ public int concurrentRequestPenalty() { /** * The threshold for consecutive connection failures to a host. + * * @return the threshold for consecutive connection failures to a host. * @see Builder#failedConnectionsThreshold(int) */ @@ -168,6 +179,7 @@ public int failedConnectionsThreshold() { /** * The jitter used along with the configured interval to determine duration between outlier detector checks. + * * @return the jitter used along with the configured interval to determine duration between outlier detector checks. * @see #failureDetectorInterval() * @see Builder#failureDetectorInterval(Duration, Duration) @@ -178,6 +190,7 @@ public Duration failureDetectorIntervalJitter() { /** * The interval between service discovery resubscribes. + * * @return the interval between service discovery resubscribes. * @see #serviceDiscoveryResubscribeJitter() * @see Builder#serviceDiscoveryResubscribeInterval(Duration, Duration) @@ -188,6 +201,7 @@ public Duration serviceDiscoveryResubscribeInterval() { /** * The jitter to use along with the service discovery resubscribe interval. + * * @return the jitter to use along with the service discovery resubscribe interval. * @see #serviceDiscoveryResubscribeInterval() * @see Builder#serviceDiscoveryResubscribeInterval(Duration, Duration) @@ -198,6 +212,7 @@ public Duration serviceDiscoveryResubscribeJitter() { /** * The number of consecutive failures before the attempt to suspect the host. + * * @return the number of consecutive failures before the attempt to suspect the host. */ public int consecutive5xx() { @@ -206,9 +221,11 @@ public int consecutive5xx() { /** * The interval on which to run failure detectors. + *

* Failure percentage and success rate outlier detectors perform periodic scans to detect outliers. Active * revival mechanisms such as the layer-4 connectivity detector also use this interval to perform their periodic * health check to see if a host can be considered revived. + * * @return the interval on which to run failure percentage and success rate failure detectors. */ public Duration failureDetectorInterval() { @@ -217,8 +234,10 @@ public Duration failureDetectorInterval() { /** * The base ejection time. + *

* The base ejection time is multiplied by the number of consecutive times the host has been ejected to get the * total ejection time, capped by the {@link #maxEjectionTime()}. + * * @return the base ejection time. * @see #ejectionTimeJitter() */ @@ -228,6 +247,7 @@ public Duration baseEjectionTime() { /** * The maximum percentage of hosts that can be ejected due to outlier detection. + * * @return the maximum percentage of hosts that can be ejected due to outlier detection. */ public int maxEjectionPercentage() { @@ -237,6 +257,7 @@ public int maxEjectionPercentage() { /** * The probability in percentage that a host will be marked as unhealthy when a host reaches the * {@link #consecutive5xx()} threshold. + * * @return the probability with which the host should be marked as unhealthy. */ public int enforcingConsecutive5xx() { @@ -246,6 +267,7 @@ public int enforcingConsecutive5xx() { /** * The probability in percentage that a host will be marked as unhealthy when a host exceeds the success rate * outlier detectors threshold. + * * @return the probability with which the host should be marked as unhealthy. */ public int enforcingSuccessRate() { @@ -254,6 +276,7 @@ public int enforcingSuccessRate() { /** * The minimum number of hosts required to perform the success rate outlier detector analysis. + * * @return the minimum number of hosts required to perform the success rate outlier detector analysis. */ public int successRateMinimumHosts() { @@ -263,6 +286,7 @@ public int successRateMinimumHosts() { /** * The minimum number of requests in an outlier detector interval required to include it in the success rate * outlier detector analysis. + * * @return the minimum number of request required. */ public int successRateRequestVolume() { @@ -272,6 +296,7 @@ public int successRateRequestVolume() { /** * The value divided by 1000 and then multiplied against the success rate standard deviation which sets the * threshold for ejection in the success rate outlier detector. + * * @return the stdev factor divided by 1000 used to determine the statistical outliers. */ public int successRateStdevFactor() { @@ -280,6 +305,7 @@ public int successRateStdevFactor() { /** * The failure threshold in percentage for ejecting a host. + * * @return the failure threshold in percentage for ejecting a host. */ public int failurePercentageThreshold() { @@ -289,6 +315,7 @@ public int failurePercentageThreshold() { /** * The probability in percentage that a host will be marked as unhealthy when a host exceeds the failure percentage * outlier detectors threshold. + * * @return the probability with which the host should be marked as unhealthy. */ public int enforcingFailurePercentage() { @@ -297,6 +324,7 @@ public int enforcingFailurePercentage() { /** * The minimum number of hosts required to perform the failure percentage outlier detector analysis. + * * @return the minimum number of hosts required to perform the failure percentage outlier detector analysis. */ public int failurePercentageMinimumHosts() { @@ -306,6 +334,7 @@ public int failurePercentageMinimumHosts() { /** * The minimum number of requests in an outlier detector interval required to include it in the failure percentage * outlier detector analysis. + * * @return the minimum number of request required. */ public int failurePercentageRequestVolume() { @@ -314,6 +343,7 @@ public int failurePercentageRequestVolume() { /** * The maximum amount of time a host can be ejected regardless of the number of consecutive ejections. + * * @return the maximum amount of time a host can be ejected. */ public Duration maxEjectionTime() { @@ -322,8 +352,10 @@ public Duration maxEjectionTime() { /** * The amount of jitter to add to the ejection time. + *

* An additional amount of 'jitter' is added to the ejection time to prevent connection storms if multiple hosts * are ejected at the time. + * * @return the amount of jitter to add to the ejection time. * @see #baseEjectionTime() */ @@ -365,6 +397,7 @@ public String toString() { */ public static final class Builder { + // ServiceTalk specific settings. static final Duration DEFAULT_EWMA_HALF_LIFE = Duration.ofSeconds(10); static final int DEFAULT_CANCEL_PENALTY = 5; static final int DEFAULT_ERROR_PENALTY = 10; @@ -458,6 +491,7 @@ public Builder() { /** * Build the OutlierDetectorConfig. + * * @return the OutlierDetectorConfig. */ public OutlierDetectorConfig build() { @@ -476,24 +510,28 @@ public OutlierDetectorConfig build() { /** * Set the Exponentially Weighted Moving Average (EWMA) half-life. + *

* In the context of an exponentially weighted moving average, the half-life means the time during which * historical data has the same weight as a new sample. + *

* Defaults to 10 seconds. + * * @param ewmaHalfLife the half-life for latency data. * @return {@code this} */ public Builder ewmaHalfLife(final Duration ewmaHalfLife) { - requireNonNull(ewmaHalfLife, "ewmaHalfLife"); - ensureNonNegative(ewmaHalfLife.toNanos(), "ewmaHalfLife"); - this.ewmaHalfLife = ewmaHalfLife; + this.ewmaHalfLife = DurationUtils.ensureNonNegative(ewmaHalfLife, "ewmaHalfLife"); return this; } /** * Set the penalty factor for local cancellation of requests. + *

* The latency of the cancelled request is multiplied by the provided penalty before incorporating it into the * EWMA. + *

* Defaults to {@value DEFAULT_CANCEL_PENALTY}. + * * @param ewmaCancellationPenalty the penalty factor for local cancellation of requests. * @return {@code this} */ @@ -504,12 +542,15 @@ public Builder ewmaCancellationPenalty(final int ewmaCancellationPenalty) { /** * Set the penalty factor for requests that were classified as an error. + *

* The latency of the failed request is multiplied by the provided penalty before incorporating it into the * EWMA. + *

* Defaults to {@value DEFAULT_ERROR_PENALTY}. - * See {@link OutlierDetectorConfig#ewmaErrorPenalty()}. + * * @param ewmaErrorPenalty the penalty factor for requests that were classified as an error. * @return {@code this} + * @see OutlierDetectorConfig#ewmaErrorPenalty() */ public Builder ewmaErrorPenalty(final int ewmaErrorPenalty) { this.ewmaErrorPenalty = ensureNonNegative(ewmaErrorPenalty, "ewmaErrorPenalty"); @@ -518,6 +559,7 @@ public Builder ewmaErrorPenalty(final int ewmaErrorPenalty) { /** * Set whether a cancellation is considered to be an error by the outlier detector. + * * @param cancellationIsError whether a cancellation is considered to be an error by the outlier detector. * @return {@code this} */ @@ -528,12 +570,15 @@ public Builder cancellationIsError(final boolean cancellationIsError) { /** * Set the penalty factory to apply to concurrent requests. + *

* The EWMA penalty to apply to endpoints when there are concurrent requests. By penalizing endpoints with * concurrent load the traffic distribution will be more fair for algorithms that consider load metrics. * Larger penalties will favor a more even request distribution while lower penalties will bias traffic toward * endpoints with better performance. A value of 0 disables the penalty, 1 is an intermediate value, and larger * values such as 10 or more will strongly favor fairness over performance. + *

* Defaults to {@value DEFAULT_CONCURRENT_REQUEST_PENALTY}. + * * @param ewmaConcurrentRequestPenalty the penalty factory to apply for concurrent load. * @return {@code this} */ @@ -558,7 +603,7 @@ public Builder ewmaConcurrentRequestPenalty(final int ewmaConcurrentRequestPenal * @param jitter the amount of jitter to apply to each re-subscribe {@code interval}. * @return {@code this}. */ - public Builder serviceDiscoveryResubscribeInterval(Duration interval, Duration jitter) { + public Builder serviceDiscoveryResubscribeInterval(final Duration interval, final Duration jitter) { validateHealthCheckIntervals(interval, jitter); this.serviceDiscoveryResubscribeInterval = interval; this.serviceDiscoveryResubscribeJitter = jitter; @@ -566,11 +611,13 @@ public Builder serviceDiscoveryResubscribeInterval(Duration interval, Duration j } /** - * Configure a threshold for consecutive connection failures to a host. When the {@link LoadBalancer} - * consecutively fails to open connections in the amount greater or equal to the specified value, - * the host will be marked as unhealthy and connection establishment will take place in the background - * repeatedly on the {@link #failureDetectorInterval()} (with jitter {@link #failureDetectorIntervalJitter()}) until a connection is - * established. During that time, the host will not take part in load balancing selection. + * Configure a threshold for consecutive connection failures to a host. + *

+ * When the {@link LoadBalancer} consecutively fails to open connections in the amount greater or equal to the + * specified value, the host will be marked as unhealthy and connection establishment will take place in the + * background repeatedly on the {@link #failureDetectorInterval()} (with jitter + * {@link #failureDetectorIntervalJitter()}) until a connection is established. During that time, the host will + * not take part in load balancing selection. *

* Use a negative value of the argument to disable health checking. * @@ -582,14 +629,16 @@ public Builder serviceDiscoveryResubscribeInterval(Duration interval, Duration j public Builder failedConnectionsThreshold(int failedConnectionsThreshold) { this.failedConnectionsThreshold = failedConnectionsThreshold; if (failedConnectionsThreshold == 0) { - throw new IllegalArgumentException("Not valid value: 0"); + throw new IllegalArgumentException("Not valid value: 0 (expected: positive or negative)"); } return this; } /** * Set the threshold for consecutive failures before a host is ejected. + *

* Defaults to {@value DEFAULT_CONSECUTIVE_5XX}. + * * @param consecutive5xx the threshold for consecutive failures before a host is ejected. * @return {@code this} */ @@ -601,22 +650,28 @@ public Builder consecutive5xx(final int consecutive5xx) { /** * Set the failure detector interval on which the outlier detector will perform periodic tasks. + *

* These tasks can include detection of outlier or the active revival checks. + *

* This method will also use either the default jitter or the provided interval, whichever is smaller. + *

* Defaults to 10 second interval with 3 second jitter. + * * @param interval the interval on which to run failure percentage and success rate failure detectors. * @return {@code this} */ public Builder failureDetectorInterval(final Duration interval) { - this.failureDetectorInterval = requireNonNull(interval, "interval"); return failureDetectorInterval(interval, interval.compareTo(DEFAULT_HEALTH_CHECK_INTERVAL) < 0 ? interval.dividedBy(2) : DEFAULT_HEALTH_CHECK_JITTER); } /** * Set the interval on which to run failure percentage and success rate failure detectors. + *

* These tasks can include detection of outlier or the active revival checks. + *

* Defaults to 10 second interval with 3 second jitter. + * * @param interval the interval on which to run failure percentage and success rate failure detectors. * @param jitter the jitter of the time interval. The next interval will have a duration of * [interval - jitter, interval + jitter]. @@ -624,42 +679,45 @@ public Builder failureDetectorInterval(final Duration interval) { */ public Builder failureDetectorInterval(final Duration interval, final Duration jitter) { validateHealthCheckIntervals(interval, jitter); - this.failureDetectorInterval = requireNonNull(interval, "interval"); + this.failureDetectorInterval = interval; this.intervalJitter = jitter; return this; } /** * Set the base ejection time. + *

* Defaults to 30 seconds. + * * @param baseEjectionTime the base ejection time. * @return {@code this}. * @see #ejectionTimeJitter(Duration) */ public Builder baseEjectionTime(final Duration baseEjectionTime) { - this.baseEjectionTime = requireNonNull(baseEjectionTime, "baseEjectionTime"); - ensurePositive(baseEjectionTime.toNanos(), "baseEjectionTime"); + this.baseEjectionTime = DurationUtils.ensurePositive(baseEjectionTime, "baseEjectionTime"); return this; } /** * Set the ejection time jitter. + *

* Defaults to 3 seconds. + * * @param ejectionTimeJitter the jitter to add to the calculated ejection time. * @return {@code this}. * @see #baseEjectionTime(Duration) */ public Builder ejectionTimeJitter(final Duration ejectionTimeJitter) { - ensureNonNegative(requireNonNull(ejectionTimeJitter, "ejectionTimeJitter").toNanos(), - "ejectionTimeJitter"); - this.ejectionTimeJitter = ejectionTimeJitter; + this.ejectionTimeJitter = DurationUtils.ensureNonNegative(ejectionTimeJitter, "ejectionTimeJitter"); return this; } /** * Set the maximum percentage of hosts that can be ejected due to outlier detection. + *

* Defaults to {@value DEFAULT_MAX_EJECTION_PERCENTAGE} percent but at least one host will be allowed to be * ejected regardless of value. + * * @param maxEjectionPercentage the maximum percentage of hosts that can be ejected due to outlier detection. * @return {@code this}. */ @@ -672,7 +730,9 @@ public Builder maxEjectionPercentage(final int maxEjectionPercentage) { /** * Set the probability in percentage that a host will be marked as unhealthy when a host reaches the * {@link #consecutive5xx()} threshold. + *

* Defaults to {@value DEFAULT_ENFORCING_CONSECUTIVE_5XX} percent. + * * @param enforcingConsecutive5xx the probability the host will be marked as unhealthy. * @return {@code this}. */ @@ -685,7 +745,9 @@ public Builder enforcingConsecutive5xx(final int enforcingConsecutive5xx) { /** * Set the probability in percentage that a host will be marked as unhealthy when a host exceeds the success * rate outlier detectors threshold. + *

* Defaults to {@value DEFAULT_ENFORCING_SUCCESS_RATE} percent. + * * @param enforcingSuccessRate the probability the host will be marked as unhealthy. * @return {@code this}. */ @@ -698,6 +760,7 @@ public Builder enforcingSuccessRate(final int enforcingSuccessRate) { /** * Set the minimum number of hosts required to perform the success rate outlier detector analysis. * Defaults to {@value DEFAULT_SUCCESS_RATE_MINIMUM_HOSTS}. + * * @param successRateMinimumHosts the minimum number of hosts required to perform the success rate outlier * detector analysis. * @return {@code this}. @@ -711,7 +774,9 @@ public Builder successRateMinimumHosts(final int successRateMinimumHosts) { /** * Set the minimum number of requests in an outlier detector interval required to include it in the success rate * outlier detector analysis. + *

* Defaults to {@value DEFAULT_SUCCESS_RATE_REQUEST_VOLUME}. + * * @param successRateRequestVolume the minimum number of requests in an outlier detector interval required to * include it in the success rate outlier detector analysis. * @return {@code this}. @@ -725,7 +790,9 @@ public Builder successRateRequestVolume(final int successRateRequestVolume) { /** * Set the value divided by 1000 and then multiplied against the success rate standard deviation which sets the * threshold for ejection in the success rate outlier detector. + *

* Defaults to {@value DEFAULT_SUCCESS_RATE_STDEV_FACTOR}. + * * @param successRateStdevFactor the value divided by 1000 and then multiplied against the success rate standard * deviation which sets the threshold for ejection in the success rate outlier * detector. @@ -739,7 +806,9 @@ public Builder successRateStdevFactor(final int successRateStdevFactor) { /** * Set the failure threshold in percentage for ejecting a host. + *

* Defaults to {@value DEFAULT_FAILURE_PERCENTAGE_THRESHOLD} percent. + * * @param failurePercentageThreshold the failure threshold in percentage for ejecting a host. * @return {@code this}. */ @@ -752,7 +821,9 @@ public Builder failurePercentageThreshold(final int failurePercentageThreshold) /** * Set the probability in percentage that a host will be marked as unhealthy when a host exceeds the failure * percentage outlier detectors threshold. + *

* Defaults to {@value DEFAULT_ENFORCING_FAILURE_PERCENTAGE} percent. + * * @param enforcingFailurePercentage the probability in percentage that a host will be marked as unhealthy when * percentage outlier detectors threshold. * @return {@code this}. @@ -765,7 +836,9 @@ public Builder enforcingFailurePercentage(final int enforcingFailurePercentage) /** * Set the minimum number of hosts required to perform the failure percentage outlier detector analysis. + *

* Defaults to {@value DEFAULT_FAILURE_PERCENTAGE_MINIMUM_HOSTS}. + * * @param failurePercentageMinimumHosts the minimum number of hosts required to perform the failure percentage * outlier detector analysis. * @return {@code this}. @@ -779,7 +852,9 @@ public Builder failurePercentageMinimumHosts(final int failurePercentageMinimumH /** * Set the minimum number of requests in an outlier detector interval required to include it in the failure * percentage outlier detector analysis. + *

* Defaults to {@value DEFAULT_FAILURE_PERCENTAGE_REQUEST_VOLUME}. + * * @param failurePercentageRequestVolume the minimum number of requests in an outlier detector interval required * to include it in the failure percentage outlier detector analysis. * @return {@code this}. @@ -792,14 +867,15 @@ public Builder failurePercentageRequestVolume(final int failurePercentageRequest /** * Set the maximum amount of time a host can be ejected regardless of the number of consecutive ejections. + *

* Defaults to a max ejection time of 300 seconds and 0 seconds jitter. + * * @param maxEjectionTime the maximum amount of time a host can be ejected regardless of the number of * consecutive ejections. * @return {@code this}. */ public Builder maxEjectionTime(final Duration maxEjectionTime) { - ensureNonNegative(requireNonNull(maxEjectionTime, "maxEjectionTime").toNanos(), "maxEjectionTime"); - this.maxEjectionTime = maxEjectionTime; + this.maxEjectionTime = DurationUtils.ensureNonNegative(maxEjectionTime, "maxEjectionTime"); return this; } } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CConnectionSelector.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CConnectionSelector.java index b88901534f..30b69a804a 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CConnectionSelector.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CConnectionSelector.java @@ -39,6 +39,7 @@ * - If we fail to select the best connection, try the other connection. * - If both connections fail, repeat the pick-two operation for up to maxEffort attempts, begin linear iteration * through the remaining connections searching for an acceptable connection. + * * @param the type of the load balanced connection. */ final class P2CConnectionSelector implements ConnectionSelector { diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CLoadBalancingPolicy.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CLoadBalancingPolicy.java index 088cb0922d..e8662e77c1 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CLoadBalancingPolicy.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CLoadBalancingPolicy.java @@ -47,7 +47,7 @@ final class P2CLoadBalancingPolicy buildSelector( - List> hosts, String lbDescription) { + HostSelector buildSelector(final List> hosts, + final String lbDescription) { return new P2CSelector<>(hosts, lbDescription, ignoreWeights, maxEffort, failOpen, random); } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CLoadBalancingPolicyBuilder.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CLoadBalancingPolicyBuilder.java index f931a5172b..c0ab47ae51 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CLoadBalancingPolicyBuilder.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CLoadBalancingPolicyBuilder.java @@ -23,7 +23,8 @@ import static io.servicetalk.utils.internal.NumberUtils.ensurePositive; /** - * A builder for immutable {@link P2CLoadBalancingPolicy} instances. + * A builder for {@link P2CLoadBalancingPolicy} instances. + * * @see LoadBalancingPolicies#p2c() */ public final class P2CLoadBalancingPolicyBuilder { @@ -39,12 +40,12 @@ public final class P2CLoadBalancingPolicyBuilder { private Random random; P2CLoadBalancingPolicyBuilder() { - // package private + // package private } /** - * Set the maximum number of attempts that P2C will attempt to select a pair with at least one - * healthy host. + * Set the maximum number of attempts that P2C will attempt to select a pair with at least one healthy host. + *

* Defaults to {@value DEFAULT_MAX_EFFORT}. * * @param maxEffort the maximum number of attempts. @@ -57,9 +58,10 @@ public P2CLoadBalancingPolicyBuilder maxEffort(final int maxEffort) { /** * Set whether the selector should fail-open in the event no healthy hosts are found. + *

* When a load balancing policy is configured to fail-open and is unable to find a healthy host, it will attempt - * to select or establish a connection from an arbitrary host even if it is unlikely to return a healthy - * session. + * to select or establish a connection from an arbitrary host even if it is unlikely to return a healthy session. + *

* Defaults to {@value DEFAULT_FAIL_OPEN_POLICY}. * * @param failOpen if true, will attempt to select or establish a connection from an arbitrary host even if it @@ -73,9 +75,11 @@ public P2CLoadBalancingPolicyBuilder failOpen(final boolean failOpen) { /** * Set whether the host selector should ignore {@link Host}s weight. + *

* Host weight influences the probability it will be selected to serve a request. The host weight can come * from many sources including known host capacity, priority groups, and others, so ignoring weight * information can lead to other features not working properly and should be used with care. + *

* Defaults to {@value DEFAULT_IGNORE_WEIGHTS}. * * @param ignoreWeights whether the host selector should ignore host weight information. @@ -87,13 +91,13 @@ public P2CLoadBalancingPolicyBuilder ignoreWeights(final boolean ignoreWeights) } // For testing purposes only. - P2CLoadBalancingPolicyBuilder random(Random random) { + P2CLoadBalancingPolicyBuilder random(final Random random) { this.random = random; return this; } /** - * Construct an immutable {@link P2CLoadBalancingPolicy}. + * Construct the {@link P2CLoadBalancingPolicy}. * * @param the type of the resolved address. * @param the refined type of the {@link LoadBalancedConnection}. diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CSelector.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CSelector.java index be5edbcdd2..b414215342 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CSelector.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/P2CSelector.java @@ -49,9 +49,9 @@ final class P2CSelector private final int maxEffort; private final boolean failOpen; - P2CSelector(List> hosts, final String lbDescription, - final boolean ignoreWeights, final int maxEffort, final boolean failOpen, - @Nullable final Random random) { + P2CSelector(final List> hosts, final String lbDescription, + final boolean ignoreWeights, final int maxEffort, final boolean failOpen, + @Nullable final Random random) { super(hosts, lbDescription); this.ignoreWeights = ignoreWeights; this.entrySelector = ignoreWeights ? new EqualWeightEntrySelector(hosts.size()) : buildAliasTable(hosts); @@ -66,8 +66,8 @@ public HostSelector rebuildWithHosts(List selectConnection0(Predicate selector, @Nullable ContextMap context, - boolean forceNewConnectionAndReserve) { + Single selectConnection0(final Predicate selector, @Nullable final ContextMap context, + final boolean forceNewConnectionAndReserve) { final int size = hostSetSize(); switch (size) { case 0: diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java index b2f9c8db7d..b9ccee09b0 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java @@ -21,10 +21,13 @@ * A tracker of latency of an action over time. *

* The usage of the RequestTracker is intended to follow the simple workflow: - * - At initiation of an action for which a request is must call {@link RequestTracker#beforeRequestStart()} and save - * the timestamp much like would be done when using a stamped lock. - * - Once the request event is complete only one of the {@link RequestTracker#onRequestSuccess(long)} or - * {@link RequestTracker#onRequestError(long, ErrorClass)} methods must be called and called exactly once. + *

    + *
  • At initiation of an action that will be tracked the caller must call + * {@link RequestTracker#beforeRequestStart()} and save the timestamp much like would be done when using a stamped + * lock.
  • + *
  • Once the request event is complete only one of the {@link RequestTracker#onRequestSuccess(long)} or + * {@link RequestTracker#onRequestError(long, ErrorClass)} methods must be called and called exactly once
  • + *
* In other words, every call to {@link RequestTracker#beforeRequestStart()} must be followed by exactly one call to * either of the completion methods {@link RequestTracker#onRequestSuccess(long)} or * {@link RequestTracker#onRequestError(long, ErrorClass)}. Failure to do so can cause state corruption in the @@ -82,6 +85,7 @@ enum ErrorClass { CANCELLED(true); private final boolean isLocal; + ErrorClass(boolean isLocal) { this.isLocal = isLocal; } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RichServiceDiscovererEvent.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RichServiceDiscovererEvent.java index 15a9712bcf..17cc08672f 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RichServiceDiscovererEvent.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RichServiceDiscovererEvent.java @@ -57,7 +57,7 @@ public Status status() { * The relative weight this endpoint should be given for load balancing decisions. * @return the relative weight this endpoint should be given for load balancing decisions. */ - public double loadBalancingWeight() { + double loadBalancingWeight() { return weight; } @@ -65,7 +65,7 @@ public double loadBalancingWeight() { * Priority group this endpoint belongs to. * @return the priority group this endpoint belongs to. */ - public int priority() { + int priority() { return priority; } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancingPolicy.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancingPolicy.java index 5370a0a08a..f299145d96 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancingPolicy.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancingPolicy.java @@ -21,10 +21,10 @@ /** * A round-robin load balancing policy. - * - * This load balancing algorithm is the well known policy of selecting hosts sequentially - * from an ordered set. If a host is considered unhealthy it is skipped the next host - * is selected until a healthy host is found or the entire host set has been exhausted. + *

+ * This load balancing algorithm is the well known policy of selecting hosts sequentially from an ordered set. + * If a host is considered unhealthy it is skipped the next host is selected until a healthy host is found or the entire + * host set has been exhausted. * * @param the type of the resolved address * @param the type of the load balanced connection @@ -41,8 +41,8 @@ final class RoundRobinLoadBalancingPolicy - buildSelector(final List> hosts, final String lbDescription) { + HostSelector buildSelector(final List> hosts, + final String lbDescription) { return new RoundRobinSelector<>(hosts, lbDescription, failOpen, ignoreWeights); } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancingPolicyBuilder.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancingPolicyBuilder.java index 42154a2ddb..0777219683 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancingPolicyBuilder.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancingPolicyBuilder.java @@ -18,7 +18,8 @@ import io.servicetalk.client.api.LoadBalancedConnection; /** - * A builder for immutable {@link RoundRobinLoadBalancingPolicy} instances. + * A builder for {@link RoundRobinLoadBalancingPolicy} instances. + * * @see LoadBalancingPolicies#roundRobin() */ public final class RoundRobinLoadBalancingPolicyBuilder { @@ -35,9 +36,10 @@ public final class RoundRobinLoadBalancingPolicyBuilder { /** * Set whether the selector should fail-open in the event no healthy hosts are found. + *

* When a load balancing policy is configured to fail-open and is unable to find a healthy host, it will attempt - * to select or establish a connection from an arbitrary host even if it is unlikely to return a healthy - * session. + * to select or establish a connection from an arbitrary host even if it is unlikely to return a healthy session. + *

* Defaults to {@value DEFAULT_FAIL_OPEN_POLICY}. * * @param failOpen if true, will attempt to select or establish a connection from an arbitrary host even if it @@ -51,9 +53,11 @@ public RoundRobinLoadBalancingPolicyBuilder failOpen(final boolean failOpen) { /** * Set whether the host selector should ignore {@link Host}s weight. + *

* Host weight influences the probability it will be selected to serve a request. The host weight can come * from many sources including known host capacity, priority groups, and others, so ignoring weight * information can lead to other features not working properly and should be used with care. + *

* Defaults to {@value DEFAULT_IGNORE_WEIGHTS}. * * @param ignoreWeights whether the host selector should ignore host weight information. @@ -65,14 +69,13 @@ public RoundRobinLoadBalancingPolicyBuilder ignoreWeights(final boolean ignoreWe } /** - * Construct the immutable {@link RoundRobinLoadBalancingPolicy}. + * Construct the {@link RoundRobinLoadBalancingPolicy}. * * @param the type of the resolved address. * @param the refined type of the {@link LoadBalancedConnection}. * @return the concrete {@link RoundRobinLoadBalancingPolicy}. */ - public LoadBalancingPolicy - build() { + public LoadBalancingPolicy build() { return new RoundRobinLoadBalancingPolicy<>(failOpen, ignoreWeights); } } diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinSelector.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinSelector.java index 4cc7f20f58..e0daeccd0e 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinSelector.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/RoundRobinSelector.java @@ -71,9 +71,8 @@ final class RoundRobinSelector selectConnection0( - final Predicate selector, @Nullable final ContextMap context, - final boolean forceNewConnectionAndReserve) { + Single selectConnection0(final Predicate selector, @Nullable final ContextMap context, + final boolean forceNewConnectionAndReserve) { // try one loop over hosts and if all are expired, give up final int cursor = scheduler.nextHost(); Host failOpenHost = null; @@ -141,7 +140,7 @@ private static Scheduler buildScheduler(AtomicInteger index, List