Skip to content

Commit

Permalink
loadbalancer-experimental: adjust load balancer policy docs and helpe…
Browse files Browse the repository at this point in the history
…rs (#2926)

Motivation:

- More detail could be provided for the P2C and RR `failOpen` configuration
  option since it might not be clear what fail-open mean in that context.
- We could use some helpers to make the different policies more discoverable.

Modifications:

- Adjust the docs for `.failOpen` to add more detail.
- Add LoadBalancerPolicies to aid discoverability of load balancer policies.
- Move away from `new *Builder` pattern and add deprecations.
  • Loading branch information
bryce-anderson authored May 14, 2024
1 parent dd37c3b commit f0e7aff
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@
import io.servicetalk.http.api.HttpResponse;
import io.servicetalk.http.api.SingleAddressHttpClientBuilder;
import io.servicetalk.http.netty.HttpClients;
import io.servicetalk.loadbalancer.LoadBalancerPolicies;
import io.servicetalk.loadbalancer.LoadBalancers;
import io.servicetalk.loadbalancer.OutlierDetectorConfig;
import io.servicetalk.loadbalancer.P2CLoadBalancingPolicy;
import io.servicetalk.loadbalancer.P2CLoadBalancingPolicyBuilder;
import io.servicetalk.transport.api.HostAndPort;

import java.net.InetSocketAddress;
Expand Down Expand Up @@ -60,7 +61,7 @@ private static LoadBalancerFactory<InetSocketAddress, FilterableStreamingHttpLoa
// request count to score hosts. The net result is typically a traffic distribution that will
// show a preference toward faster hosts while also rapidly adjust to changes in backend
// performance.
new P2CLoadBalancingPolicy.Builder()
LoadBalancerPolicies.p2c()
// Set the max effort (default: 5). This is the number of times P2C will pick a random
// pair of hosts in search of a healthy host before giving up. When it gives up it will
// either attempt to use one of the hosts regardless of status if `failOpen == true` or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
package io.servicetalk.loadbalancer.experimental;

import io.servicetalk.client.api.LoadBalancedConnection;
import io.servicetalk.loadbalancer.LoadBalancerPolicies;
import io.servicetalk.loadbalancer.LoadBalancingPolicy;
import io.servicetalk.loadbalancer.OutlierDetectorConfig;
import io.servicetalk.loadbalancer.P2CLoadBalancingPolicy;
import io.servicetalk.loadbalancer.RoundRobinLoadBalancingPolicy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -122,11 +121,8 @@ private LBPolicy getLBPolicy() {
}

<U, C extends LoadBalancedConnection> LoadBalancingPolicy<U, C> getLoadBalancingPolicy() {
if (lbPolicy == LBPolicy.P2C) {
return new P2CLoadBalancingPolicy.Builder().build();
} else {
return new RoundRobinLoadBalancingPolicy.Builder().build();
}
return lbPolicy == LBPolicy.P2C ?
LoadBalancerPolicies.p2c().build() : LoadBalancerPolicies.roundRobin().build();
}

boolean enabledForServiceName(String serviceName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ private static <C extends LoadBalancedConnection> ConnectionPoolStrategyFactory<

private static <ResolvedAddress, C extends LoadBalancedConnection>
LoadBalancingPolicy<ResolvedAddress, C> defaultLoadBalancingPolicy() {
return new RoundRobinLoadBalancingPolicy.Builder().build();
return LoadBalancerPolicies.roundRobin().build();
}

private static <C extends LoadBalancedConnection> ConnectionPoolStrategyFactory<C>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright © 2023 Apple Inc. and the ServiceTalk project 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.servicetalk.loadbalancer;

/**
* A collections of factories for constructing a {@link LoadBalancingPolicy}.
*/
public final class LoadBalancerPolicies {

private LoadBalancerPolicies() {
// no instances.
}

/**
* 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() {
return new RoundRobinLoadBalancingPolicyBuilder();
}

/**
* 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() {
return new P2CLoadBalancingPolicyBuilder();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
* @param <ResolvedAddress> the type of the resolved address.
* @param <C> the type of the load balanced connection.
* @see <a href="https://www.eecs.harvard.edu/~michaelm/postscripts/tpds2001.pdf">Mitzenmacher (2001) The Power of Two
* * Choices in Randomized Load Balancing</a>
* Choices in Randomized Load Balancing</a>
* @deprecated Use {@link P2CLoadBalancingPolicyBuilder}.
*/
@Deprecated // FIXME: 0.42.45 - make package private
public final class P2CLoadBalancingPolicy<ResolvedAddress, C extends LoadBalancedConnection>
extends LoadBalancingPolicy<ResolvedAddress, C> {

Expand All @@ -48,7 +50,7 @@ public final class P2CLoadBalancingPolicy<ResolvedAddress, C extends LoadBalance
@Nullable
private final Random random;

private P2CLoadBalancingPolicy(final boolean ignoreWeights, final int maxEffort,
P2CLoadBalancingPolicy(final boolean ignoreWeights, final int maxEffort,
final boolean failOpen, @Nullable final Random random) {
this.ignoreWeights = ignoreWeights;
this.maxEffort = maxEffort;
Expand All @@ -74,7 +76,9 @@ public String toString() {

/**
* A builder for immutable {@link P2CLoadBalancingPolicy} instances.
* @deprecated Use {@link P2CLoadBalancingPolicyBuilder}.
*/
@Deprecated // FIXME: 0.42.45 - remove builder.
public static final class Builder {

private static final boolean DEFAULT_IGNORE_WEIGHTS = false;
Expand All @@ -99,9 +103,13 @@ public Builder maxEffort(final int maxEffort) {
}

/**
* Set whether the host selector should attempt to use an unhealthy {@link Host} as a last resort.
* 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.
* Defaults to {@value DEFAULT_FAIL_OPEN_POLICY}.
* @param failOpen whether the host selector should attempt to use an unhealthy {@link Host} as a last resort.
* @param failOpen if true, will attempt to select or establish a connection from an arbitrary host even if it
* is unlikely to return a healthy session.
* @return {@code this}
*/
public Builder failOpen(final boolean failOpen) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright © 2024 Apple Inc. and the ServiceTalk project 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.servicetalk.loadbalancer;

import io.servicetalk.client.api.LoadBalancedConnection;

import java.util.Random;
import javax.annotation.Nullable;

import static io.servicetalk.utils.internal.NumberUtils.ensurePositive;

/**
* A builder for immutable {@link P2CLoadBalancingPolicy} instances.
* @see LoadBalancerPolicies#p2c()
*/
public final class P2CLoadBalancingPolicyBuilder {

private static final boolean DEFAULT_IGNORE_WEIGHTS = false;
private static final int DEFAULT_MAX_EFFORT = 5;
private static final boolean DEFAULT_FAIL_OPEN_POLICY = LoadBalancingPolicy.DEFAULT_FAIL_OPEN_POLICY;

private boolean ignoreWeights = DEFAULT_IGNORE_WEIGHTS;
private int maxEffort = DEFAULT_MAX_EFFORT;
private boolean failOpen = DEFAULT_FAIL_OPEN_POLICY;
@Nullable
private Random random;

P2CLoadBalancingPolicyBuilder() {
// package private
}

/**
* 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.
* @return {@code this}
*/
public P2CLoadBalancingPolicyBuilder maxEffort(final int maxEffort) {
this.maxEffort = ensurePositive(maxEffort, "maxEffort");
return this;
}

/**
* 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.
* 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
* is unlikely to return a healthy session.
* @return {@code this}
*/
public P2CLoadBalancingPolicyBuilder failOpen(final boolean failOpen) {
this.failOpen = failOpen;
return this;
}

/**
* 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.
* @return {@code this}
*/
public P2CLoadBalancingPolicyBuilder ignoreWeights(final boolean ignoreWeights) {
this.ignoreWeights = ignoreWeights;
return this;
}

// For testing purposes only.
P2CLoadBalancingPolicyBuilder random(Random random) {
this.random = random;
return this;
}

/**
* Construct an immutable {@link P2CLoadBalancingPolicy}.
*
* @param <ResolvedAddress> the type of the resolved address.
* @param <C> the refined type of the {@link LoadBalancedConnection}.
* @return the concrete {@link P2CLoadBalancingPolicy}.
*/
public <ResolvedAddress, C extends LoadBalancedConnection> LoadBalancingPolicy<ResolvedAddress, C> build() {
return new P2CLoadBalancingPolicy<>(ignoreWeights, maxEffort, failOpen, random);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@
* 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.
*
* Note: this algorithm doesn't currently support weighted hosts and all weight information will be ignored.
*
* @param <ResolvedAddress> the type of the resolved address
* @param <C> the type of the load balanced connection
* @deprecated Use {@link RoundRobinLoadBalancingPolicyBuilder}.
*/
@Deprecated // FIXME: 0.42.45 - make package private
public final class RoundRobinLoadBalancingPolicy<ResolvedAddress, C extends LoadBalancedConnection>
extends LoadBalancingPolicy<ResolvedAddress, C> {

private final boolean failOpen;
private final boolean ignoreWeights;

private RoundRobinLoadBalancingPolicy(final boolean failOpen, final boolean ignoreWeights) {
RoundRobinLoadBalancingPolicy(final boolean failOpen, final boolean ignoreWeights) {
this.failOpen = failOpen;
this.ignoreWeights = ignoreWeights;
}
Expand All @@ -60,7 +60,9 @@ public String toString() {

/**
* A builder for immutable {@link RoundRobinLoadBalancingPolicy} instances.
* @deprecated Use {@link RoundRobinLoadBalancingPolicyBuilder}.
*/
@Deprecated // FIXME: 0.42.45 - remove builder.
public static final class Builder {

private static final boolean DEFAULT_IGNORE_WEIGHTS = false;
Expand All @@ -69,9 +71,14 @@ public static final class Builder {
private boolean ignoreWeights = DEFAULT_IGNORE_WEIGHTS;

/**
* Set whether the host selector should attempt to use an unhealthy {@link Host} as a last resort.
* @param failOpen whether the host selector should attempt to use an unhealthy {@link Host} as a last resort.
* @return this {@link P2CLoadBalancingPolicy.Builder}.
* 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.
* 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
* is unlikely to return a healthy session.
* @return {@code this}
*/
public Builder failOpen(final boolean failOpen) {
this.failOpen = failOpen;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright © 2024 Apple Inc. and the ServiceTalk project 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.servicetalk.loadbalancer;

import io.servicetalk.client.api.LoadBalancedConnection;

/**
* A builder for immutable {@link RoundRobinLoadBalancingPolicy} instances.
* @see LoadBalancerPolicies#roundRobin()
*/
public final class RoundRobinLoadBalancingPolicyBuilder {

private static final boolean DEFAULT_IGNORE_WEIGHTS = false;
private static final boolean DEFAULT_FAIL_OPEN_POLICY = LoadBalancingPolicy.DEFAULT_FAIL_OPEN_POLICY;

private boolean failOpen = DEFAULT_FAIL_OPEN_POLICY;
private boolean ignoreWeights = DEFAULT_IGNORE_WEIGHTS;

RoundRobinLoadBalancingPolicyBuilder() {
// package private constructor
}

/**
* 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.
* 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
* is unlikely to return a healthy session.
* @return {@code this}
*/
public RoundRobinLoadBalancingPolicyBuilder failOpen(final boolean failOpen) {
this.failOpen = failOpen;
return this;
}

/**
* 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.
* @return {@code this}
*/
public RoundRobinLoadBalancingPolicyBuilder ignoreWeights(final boolean ignoreWeights) {
this.ignoreWeights = ignoreWeights;
return this;
}

/**
* Construct the immutable {@link RoundRobinLoadBalancingPolicy}.
*
* @param <ResolvedAddress> the type of the resolved address.
* @param <C> the refined type of the {@link LoadBalancedConnection}.
* @return the concrete {@link RoundRobinLoadBalancingPolicy}.
*/
public <ResolvedAddress, C extends LoadBalancedConnection> LoadBalancingPolicy<ResolvedAddress, C>
build() {
return new RoundRobinLoadBalancingPolicy<>(failOpen, ignoreWeights);
}
}
Loading

0 comments on commit f0e7aff

Please sign in to comment.