Skip to content

Commit

Permalink
If we are below a failure threshold and a probe fails, use the destin…
Browse files Browse the repository at this point in the history
…ation's previous active health state as the next health state.

This has the effect of a destination with a health state of Unknown in the unknown state until the threshold is reached, at which time the destination transitions into Unhealthy.
  • Loading branch information
Robbie Knuth committed Feb 27, 2024
1 parent 73a5b85 commit 2d95f7c
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 7 deletions.
7 changes: 4 additions & 3 deletions src/ReverseProxy/Health/ConsecutiveFailuresHealthPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ public void ProbingCompleted(ClusterState cluster, IReadOnlyList<DestinationProb
for (var i = 0; i < probingResults.Count; i++)
{
var destination = probingResults[i].Destination;
var previousState = destination.Health.Active;

var count = _failureCounters.GetOrCreateValue(destination);
var newHealth = EvaluateHealthState(threshold, probingResults[i].Response, count);
var newHealth = EvaluateHealthState(threshold, probingResults[i].Response, count, previousState);
newHealthStates[i] = new NewActiveDestinationHealth(destination, newHealth);
}

Expand All @@ -55,7 +56,7 @@ private double GetFailureThreshold(ClusterState cluster)
return thresholdEntry.GetParsedOrDefault(_options.DefaultThreshold);
}

private static DestinationHealth EvaluateHealthState(double threshold, HttpResponseMessage? response, AtomicCounter count)
private static DestinationHealth EvaluateHealthState(double threshold, HttpResponseMessage? response, AtomicCounter count, DestinationHealth previousState)
{
DestinationHealth newHealth;
if (response is not null && response.IsSuccessStatusCode)
Expand All @@ -68,7 +69,7 @@ private static DestinationHealth EvaluateHealthState(double threshold, HttpRespo
{
// Failure
var currentFailureCount = count.Increment();
newHealth = currentFailureCount < threshold ? DestinationHealth.Healthy : DestinationHealth.Unhealthy;
newHealth = currentFailureCount < threshold ? previousState : DestinationHealth.Unhealthy;
}

return newHealth;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void ProbingCompleted_FailureThresholdExceeded_MarkDestinationUnhealthy()
var options = Options.Create(new ConsecutiveFailuresHealthPolicyOptions { DefaultThreshold = 2 });
var policy = new ConsecutiveFailuresHealthPolicy(options, new DestinationHealthUpdaterStub());
var cluster0 = GetClusterInfo("cluster0", destinationCount: 2);
var cluster1 = GetClusterInfo("cluster0", destinationCount: 2, failureThreshold: 3);
var cluster1 = GetClusterInfo("cluster1", destinationCount: 2, failureThreshold: 3);

var probingResults0 = new[] {
new DestinationProbingResult(cluster0.Destinations.Values.First(), new HttpResponseMessage(HttpStatusCode.InternalServerError), null),
Expand All @@ -40,16 +40,19 @@ public void ProbingCompleted_FailureThresholdExceeded_MarkDestinationUnhealthy()

// First probing attempt
policy.ProbingCompleted(cluster0, probingResults0);
Assert.All(cluster0.Destinations.Values, d => Assert.Equal(DestinationHealth.Healthy, d.Health.Active));
Assert.Equal(DestinationHealth.Unknown, cluster0.Destinations.Values.First().Health.Active);
Assert.Equal(DestinationHealth.Healthy, cluster0.Destinations.Values.Skip(1).First().Health.Active);
policy.ProbingCompleted(cluster1, probingResults1);
Assert.All(cluster1.Destinations.Values, d => Assert.Equal(DestinationHealth.Healthy, d.Health.Active));
Assert.Equal(DestinationHealth.Healthy, cluster1.Destinations.Values.First().Health.Active);
Assert.Equal(DestinationHealth.Unknown, cluster1.Destinations.Values.Skip(1).First().Health.Active);

// Second probing attempt
policy.ProbingCompleted(cluster0, probingResults0);
Assert.Equal(DestinationHealth.Unhealthy, cluster0.Destinations.Values.First().Health.Active);
Assert.Equal(DestinationHealth.Healthy, cluster0.Destinations.Values.Skip(1).First().Health.Active);
policy.ProbingCompleted(cluster1, probingResults1);
Assert.All(cluster1.Destinations.Values, d => Assert.Equal(DestinationHealth.Healthy, d.Health.Active));
Assert.Equal(DestinationHealth.Healthy, cluster1.Destinations.Values.First().Health.Active);
Assert.Equal(DestinationHealth.Unknown, cluster1.Destinations.Values.Skip(1).First().Health.Active);

// Third probing attempt
policy.ProbingCompleted(cluster0, probingResults0);
Expand Down

0 comments on commit 2d95f7c

Please sign in to comment.