Visitar URL original
Outlier detection: unset `failure_percent_ejection` results in 100% instead of 0% · Issue #13165 · grpc/grpc-java · GitHub
Skip to content

Outlier detection: unset failure_percent_ejection results in 100% instead of 0% #13165

Open

Description

@dfawley

success_rate_ejection.enforcement_percentage defaults to 100% per Envoy and our spec:

https://github.com/grpc/proposal/blob/4125fbd155353d35386d8fbd6b23454f6ab0e345/A50-xds-outlier-detection.md?plain=1#L72-L75

This is implemented (correctly) here:

SuccessRateEjection successRateEjection;
// If success rate enforcement has been turned completely off, don't configure this ejection.
if (envoyOutlierDetection.hasEnforcingSuccessRate()
&& envoyOutlierDetection.getEnforcingSuccessRate().getValue() == 0) {
successRateEjection = null;
} else {
Integer stdevFactor = envoyOutlierDetection.hasSuccessRateStdevFactor()
? envoyOutlierDetection.getSuccessRateStdevFactor().getValue() : null;
Integer enforcementPercentage = envoyOutlierDetection.hasEnforcingSuccessRate()
? envoyOutlierDetection.getEnforcingSuccessRate().getValue() : null;
Integer minimumHosts = envoyOutlierDetection.hasSuccessRateMinimumHosts()
? envoyOutlierDetection.getSuccessRateMinimumHosts().getValue() : null;
Integer requestVolume = envoyOutlierDetection.hasSuccessRateRequestVolume()
? envoyOutlierDetection.getSuccessRateRequestVolume().getValue() : null;
successRateEjection = SuccessRateEjection.create(stdevFactor, enforcementPercentage,
minimumHosts, requestVolume);
}

But failure_percent_ejection should default to zero instead (i.e. disable checking when unset):

https://github.com/grpc/proposal/blob/4125fbd155353d35386d8fbd6b23454f6ab0e345/A50-xds-outlier-detection.md?plain=1#L148

This is implemented (incorrectly) here:

FailurePercentageEjection failurePercentageEjection;
if (envoyOutlierDetection.hasEnforcingFailurePercentage()
&& envoyOutlierDetection.getEnforcingFailurePercentage().getValue() == 0) {
failurePercentageEjection = null;
} else {
Integer threshold = envoyOutlierDetection.hasFailurePercentageThreshold()
? envoyOutlierDetection.getFailurePercentageThreshold().getValue() : null;
Integer enforcementPercentage = envoyOutlierDetection.hasEnforcingFailurePercentage()
? envoyOutlierDetection.getEnforcingFailurePercentage().getValue() : null;
Integer minimumHosts = envoyOutlierDetection.hasFailurePercentageMinimumHosts()
? envoyOutlierDetection.getFailurePercentageMinimumHosts().getValue() : null;
Integer requestVolume = envoyOutlierDetection.hasFailurePercentageRequestVolume()
? envoyOutlierDetection.getFailurePercentageRequestVolume().getValue() : null;
failurePercentageEjection = FailurePercentageEjection.create(threshold,
enforcementPercentage, minimumHosts, requestVolume);
}

Note that it's a copy/paste of the enforcement_percentage above it, meaning it is only disabled if it is explicitly set to zero.

When we go to actually build the enforcer, failurePercentage is non-null:

FailurePercentageEjection failurePercentage = outlierDetection.failurePercentageEjection();
if (failurePercentage != null) {
OutlierDetectionLoadBalancerConfig.FailurePercentageEjection.Builder
failurePercentageConfigBuilder = new OutlierDetectionLoadBalancerConfig
.FailurePercentageEjection.Builder();
if (failurePercentage.threshold() != null) {
failurePercentageConfigBuilder.setThreshold(failurePercentage.threshold());
}
if (failurePercentage.enforcementPercentage() != null) {
failurePercentageConfigBuilder.setEnforcementPercentage(
failurePercentage.enforcementPercentage());
}
if (failurePercentage.minimumHosts() != null) {
failurePercentageConfigBuilder.setMinimumHosts(failurePercentage.minimumHosts());
}
if (failurePercentage.requestVolume() != null) {
failurePercentageConfigBuilder.setRequestVolume(failurePercentage.requestVolume());
}
configBuilder.setFailurePercentageEjection(failurePercentageConfigBuilder.build());

But enforcement percentage is null, so the default percentage of 100 is applied:

public static class Builder {
int threshold = 85;
int enforcementPercentage = 100;
int minimumHosts = 5;
int requestVolume = 50;

The fix appears to be trivial. In EnvoyServerProtoData.java, change the check to:

if (!envoyOutlierDetection.hasEnforcingFailurePercentage()
    || envoyOutlierDetection.getEnforcingFailurePercentage().getValue() == 0) {
  failurePercentageEjection = null;
} else {
  // ...

Activity

  1. added this to the Next milestone on Nov 5, 2026
  2. ahmadmaaz commented on Nov 6, 2026

    @ahmadmaaz

    Hi!
    I would like to work on this if no one on the team has picked it up yet.
    I will go with the fix suggested above, and add tests for the unset, explicit-zero, and non-zero cases.
    Happy to have a PR up within a few days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions