Skip to content

Commit 840aca3

Browse files
loadbalancer-experimental: Thread through the EWMA half life in XdsHealthTracker (#2824)
Motivation: We currently set the ewma lifetime to 1 nanosecond in the XdsHealthTracker and it's not configurable. Modifications: - Add a lifetime field to the OutlierDetectorConfig type - Give it a default of 10 seconds in the Builder
1 parent 05e07c4 commit 840aca3

File tree

5 files changed

+43
-11
lines changed

5 files changed

+43
-11
lines changed

servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultRequestTracker.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
2121
import java.util.function.IntBinaryOperator;
2222

23+
import static io.servicetalk.utils.internal.NumberUtils.ensurePositive;
2324
import static java.lang.Integer.MAX_VALUE;
2425
import static java.lang.Integer.MIN_VALUE;
2526
import static java.lang.Math.ceil;
@@ -66,9 +67,7 @@ abstract class DefaultRequestTracker implements RequestTracker, ScoreSupplier {
6667
}
6768

6869
DefaultRequestTracker(final long halfLifeNanos, final long cancelPenalty, final long errorPenalty) {
69-
if (halfLifeNanos <= 0) {
70-
throw new IllegalArgumentException("halfLifeNanos: " + halfLifeNanos + " (expected >0)");
71-
}
70+
ensurePositive(halfLifeNanos, "halfLifeNanos");
7271
this.invTau = Math.pow((halfLifeNanos / log(2)), -1);
7372
this.cancelPenalty = cancelPenalty;
7473
this.errorPenalty = errorPenalty;

servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/OutlierDetectorConfig.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
*/
3232
final class OutlierDetectorConfig {
3333

34+
private final Duration ewmaHalfLife;
3435
private final int consecutive5xx;
3536
private final Duration interval;
3637
private final Duration baseEjectionTime;
@@ -55,7 +56,8 @@ final class OutlierDetectorConfig {
5556
private final Duration maxEjectionTimeJitter;
5657
private final boolean successfulActiveHealthCheckUnejectHost;
5758

58-
OutlierDetectorConfig(final int consecutive5xx, final Duration interval, final Duration baseEjectionTime,
59+
OutlierDetectorConfig(final Duration ewmaHalfLife,
60+
final int consecutive5xx, final Duration interval, final Duration baseEjectionTime,
5961
final int maxEjectionPercentage, final int enforcingConsecutive5xx,
6062
final int enforcingSuccessRate, final int successRateMinimumHosts,
6163
final int successRateRequestVolume, final int successRateStdevFactor,
@@ -66,6 +68,7 @@ final class OutlierDetectorConfig {
6668
final int enforcingFailurePercentageLocalOrigin, final int failurePercentageMinimumHosts,
6769
final int failurePercentageRequestVolume, final Duration maxEjectionTime,
6870
final Duration maxEjectionTimeJitter, final boolean successfulActiveHealthCheckUnejectHost) {
71+
this.ewmaHalfLife = requireNonNull(ewmaHalfLife, "ewmaHalfLife");
6972
this.consecutive5xx = consecutive5xx;
7073
this.interval = requireNonNull(interval, "interval");
7174
this.baseEjectionTime = requireNonNull(baseEjectionTime, "baseEjectionTime");
@@ -91,6 +94,16 @@ final class OutlierDetectorConfig {
9194
this.successfulActiveHealthCheckUnejectHost = successfulActiveHealthCheckUnejectHost;
9295
}
9396

97+
/**
98+
* The Exponentially Weighted Moving Average (EWMA) half-life.
99+
* In the context of an exponentially weighted moving average, the half-life means the time during which
100+
* historical data has the same weight as a new sample.
101+
* @return the Exponentially Weighted Moving Average (EWMA) half-life.
102+
*/
103+
public Duration ewmaHalfLife() {
104+
return ewmaHalfLife;
105+
}
106+
94107
/**
95108
* The number of consecutive failures before the attempt to suspect the host.
96109
* @return the number of consecutive failures before the attempt to suspect the host.
@@ -299,6 +312,7 @@ public boolean successfulActiveHealthCheckUnejectHost() {
299312
* A builder for {@link OutlierDetectorConfig} instances.
300313
*/
301314
public static class Builder {
315+
private Duration ewmaHalfLife = Duration.ofSeconds(10);
302316
private int consecutive5xx = 5;
303317

304318
private Duration interval = Duration.ofSeconds(10);
@@ -346,7 +360,8 @@ public static class Builder {
346360
private boolean successfulActiveHealthCheckUnejectHost = true;
347361

348362
OutlierDetectorConfig build() {
349-
return new OutlierDetectorConfig(consecutive5xx, interval, baseEjectionTime,
363+
return new OutlierDetectorConfig(ewmaHalfLife, consecutive5xx,
364+
interval, baseEjectionTime,
350365
maxEjectionPercentage, enforcingConsecutive5xx,
351366
enforcingSuccessRate, successRateMinimumHosts,
352367
successRateRequestVolume, successRateStdevFactor,
@@ -360,6 +375,21 @@ OutlierDetectorConfig build() {
360375
successfulActiveHealthCheckUnejectHost);
361376
}
362377

378+
/**
379+
* Set the Exponentially Weighted Moving Average (EWMA) half-life.
380+
* In the context of an exponentially weighted moving average, the half-life means the time during which
381+
* historical data has the same weight as a new sample.
382+
* Defaults to 10 seconds.
383+
* @param ewmaHalfLife the half-life for latency data.
384+
* @return {@code this}
385+
*/
386+
public Builder ewmaHalfLife(final Duration ewmaHalfLife) {
387+
requireNonNull(ewmaHalfLife, "ewmaHalfLife");
388+
ensureNonNegative(ewmaHalfLife.toNanos(), "ewmaHalfLife");
389+
this.ewmaHalfLife = ewmaHalfLife;
390+
return this;
391+
}
392+
363393
/**
364394
* Set the threshold for consecutive failures before a host is ejected.
365395
* Defaults to 5.

servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/XdsHealthChecker.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.slf4j.Logger;
2424
import org.slf4j.LoggerFactory;
2525

26+
import java.time.Duration;
2627
import java.util.ArrayList;
2728
import java.util.Collection;
2829
import java.util.HashSet;
@@ -79,7 +80,7 @@ final class XdsHealthChecker<ResolvedAddress> implements HealthChecker<ResolvedA
7980

8081
@Override
8182
public HealthIndicator newHealthIndicator(ResolvedAddress address, HostObserver hostObserver) {
82-
XdsHealthIndicator result = new XdsHealthIndicatorImpl(address, hostObserver);
83+
XdsHealthIndicator result = new XdsHealthIndicatorImpl(address, kernel.config.ewmaHalfLife(), hostObserver);
8384
sequentialExecutor.execute(() -> indicators.add(result));
8485
indicatorCount.incrementAndGet();
8586
return result;
@@ -100,8 +101,8 @@ public void cancel() {
100101

101102
private final class XdsHealthIndicatorImpl extends XdsHealthIndicator<ResolvedAddress> {
102103

103-
XdsHealthIndicatorImpl(final ResolvedAddress address, HostObserver hostObserver) {
104-
super(sequentialExecutor, executor, address, lbDescription, hostObserver);
104+
XdsHealthIndicatorImpl(final ResolvedAddress address, Duration ewmaHalfLife, HostObserver hostObserver) {
105+
super(sequentialExecutor, executor, ewmaHalfLife, address, lbDescription, hostObserver);
105106
}
106107

107108
@Override

servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/XdsHealthIndicator.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
2323

24+
import java.time.Duration;
2425
import java.util.concurrent.ThreadLocalRandom;
2526
import java.util.concurrent.TimeUnit;
2627
import java.util.concurrent.atomic.AtomicInteger;
@@ -57,8 +58,9 @@ abstract class XdsHealthIndicator<ResolvedAddress> extends DefaultRequestTracker
5758
private volatile Long evictedUntilNanos;
5859

5960
XdsHealthIndicator(final SequentialExecutor sequentialExecutor, final Executor executor,
60-
final ResolvedAddress address, final String lbDescription, final HostObserver hostObserver) {
61-
super(1);
61+
final Duration ewmaHalfLife, final ResolvedAddress address, String lbDescription,
62+
final HostObserver hostObserver) {
63+
super(requireNonNull(ewmaHalfLife, "ewmaHalfLife").toNanos());
6264
this.sequentialExecutor = requireNonNull(sequentialExecutor, "sequentialExecutor");
6365
this.executor = requireNonNull(executor, "executor");
6466
assert executor instanceof NormalizedTimeSourceExecutor;

servicetalk-loadbalancer-experimental/src/test/java/io/servicetalk/loadbalancer/XdsHealthIndicatorTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ private class TestIndicator extends XdsHealthIndicator<String> {
212212
boolean mayEjectHost = true;
213213

214214
TestIndicator(final OutlierDetectorConfig config) {
215-
super(sequentialExecutor, new NormalizedTimeSourceExecutor(testExecutor), "address",
215+
super(sequentialExecutor, new NormalizedTimeSourceExecutor(testExecutor), Duration.ofSeconds(10), "address",
216216
"description", NoopLoadBalancerObserver.<String>instance().hostObserver("address"));
217217
this.config = config;
218218
}

0 commit comments

Comments
 (0)