Skip to content

Commit d093a45

Browse files
committed
outlier-detection: Eject always failing nodes on success rate ejection
1 parent 753ed1b commit d093a45

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

util/src/main/java/io/grpc/util/OutlierDetectionLoadBalancer.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -569,8 +569,12 @@ long inactiveVolume() {
569569
return inactiveCallCounter.successCount.get() + inactiveCallCounter.failureCount.get();
570570
}
571571

572+
long inactiveSuccessCount() {
573+
return inactiveCallCounter.successCount.get();
574+
}
575+
572576
double successRate() {
573-
return ((double) inactiveCallCounter.successCount.get()) / inactiveVolume();
577+
return ((double) inactiveSuccessCount()) / inactiveVolume();
574578
}
575579

576580
double failureRate() {
@@ -817,6 +821,7 @@ public void ejectOutliers(EndpointTrackerMap trackerMap, long ejectionTimeNanos)
817821
double mean = mean(successRates);
818822
double stdev = standardDeviation(successRates, mean);
819823

824+
// Might be non-positive.
820825
double requiredSuccessRate =
821826
mean - stdev * (config.successRateEjection.stdevFactor / 1000f);
822827

@@ -829,8 +834,8 @@ public void ejectOutliers(EndpointTrackerMap trackerMap, long ejectionTimeNanos)
829834
return;
830835
}
831836

832-
// If success rate is below the threshold, eject the address.
833-
if (tracker.successRate() < requiredSuccessRate) {
837+
// If success rate is zero or below the threshold, eject the address.
838+
if (tracker.inactiveSuccessCount() == 0L || tracker.successRate() < requiredSuccessRate) {
834839
logger.log(ChannelLogLevel.DEBUG,
835840
"SuccessRate algorithm detected outlier: {0}. "
836841
+ "Parameters: successRate={1}, mean={2}, stdev={3}, "

util/src/test/java/io/grpc/util/OutlierDetectionLoadBalancerTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,35 @@ public void successRateTwoOutliers() {
724724
ImmutableSet.of(servers.get(1).getAddresses().get(0))));
725725
}
726726

727+
/**
728+
* Two outliers get ejected with default stdevFactor.
729+
*/
730+
@Test
731+
public void successRateTwoOutliersWithDefaultStdevFactor() {
732+
OutlierDetectionLoadBalancerConfig config = new OutlierDetectionLoadBalancerConfig.Builder()
733+
.setMaxEjectionPercent(50)
734+
.setSuccessRateEjection(
735+
// Default stdevFactor results in negative requiredSuccessRate,
736+
// but always failing endpoints still get ejected.
737+
new SuccessRateEjection.Builder()
738+
.setMinimumHosts(3)
739+
.setRequestVolume(10).build())
740+
.setChildConfig(newChildConfig(roundRobinLbProvider, null)).build();
741+
742+
loadBalancer.acceptResolvedAddresses(buildResolvedAddress(config, servers));
743+
744+
generateLoad(ImmutableMap.of(
745+
subchannel1, Status.DEADLINE_EXCEEDED,
746+
subchannel2, Status.DEADLINE_EXCEEDED), 7);
747+
748+
// Move forward in time to a point where the detection timer has fired.
749+
forwardTime(config);
750+
751+
// The one subchannel that was returning errors should be ejected.
752+
assertEjectedSubchannels(ImmutableSet.of(ImmutableSet.of(servers.get(0).getAddresses().get(0)),
753+
ImmutableSet.of(servers.get(1).getAddresses().get(0))));
754+
}
755+
727756
/**
728757
* Three outliers, second one ejected even if ejecting it goes above the max ejection percentage,
729758
* as this matches Envoy behavior. The third one should not get ejected.

0 commit comments

Comments
 (0)