Skip to content

Commit 10cb4a3

Browse files
authoredMar 22, 2024··
util: Status desc for outlier detection ejection (#11036)
Including a Status description makes it easier to debug subchannel closure issues if it's clear that a subchannel became unavailable because of an outlier detection ejection.
1 parent bdb6230 commit 10cb4a3

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed
 

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,9 @@ void clearEndpointTracker() {
377377

378378
void eject() {
379379
ejected = true;
380-
subchannelStateListener.onSubchannelState(
381-
ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE));
380+
subchannelStateListener.onSubchannelState(ConnectivityStateInfo.forTransientFailure(
381+
Status.UNAVAILABLE.withDescription(
382+
"The subchannel has been ejected by outlier detection")));
382383
logger.log(ChannelLogLevel.INFO, "Subchannel ejected: {0}", this);
383384
}
384385

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

+32-7
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import static com.google.common.truth.Truth.assertThat;
2020
import static com.google.common.truth.Truth.assertWithMessage;
2121
import static io.grpc.ConnectivityState.READY;
22+
import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
2223
import static org.mockito.ArgumentMatchers.any;
23-
import static org.mockito.ArgumentMatchers.eq;
2424
import static org.mockito.Mockito.doAnswer;
2525
import static org.mockito.Mockito.mock;
2626
import static org.mockito.Mockito.times;
@@ -50,6 +50,7 @@
5050
import io.grpc.LoadBalancerProvider;
5151
import io.grpc.Metadata;
5252
import io.grpc.Status;
53+
import io.grpc.Status.Code;
5354
import io.grpc.SynchronizationContext;
5455
import io.grpc.internal.FakeClock;
5556
import io.grpc.internal.FakeClock.ScheduledTask;
@@ -1203,9 +1204,21 @@ public void successRateAndFailurePercentage_successRateOutlier_() { // with heal
12031204
// The one subchannel that was returning errors should be ejected.
12041205
assertEjectedSubchannels(ImmutableSet.of(ImmutableSet.copyOf(servers.get(0).getAddresses())));
12051206
if (hasHealthConsumer) {
1206-
verify(healthListeners.get(servers.get(0))).onSubchannelState(eq(
1207-
ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE)
1208-
));
1207+
ArgumentCaptor<ConnectivityStateInfo> csiCaptor = ArgumentCaptor.forClass(
1208+
ConnectivityStateInfo.class);
1209+
verify(healthListeners.get(servers.get(0)), times(2)).onSubchannelState(csiCaptor.capture());
1210+
List<ConnectivityStateInfo> connectivityStateInfos = csiCaptor.getAllValues();
1211+
1212+
// The subchannel went through two state transitions...
1213+
assertThat(connectivityStateInfos).hasSize(2);
1214+
// ...it first went to the READY state...
1215+
assertThat(connectivityStateInfos.get(0).getState()).isEqualTo(READY);
1216+
1217+
// ...and then to TRANSIENT_FAILURE as outlier detection ejected it.
1218+
assertThat(connectivityStateInfos.get(1).getState()).isEqualTo(TRANSIENT_FAILURE);
1219+
assertThat(connectivityStateInfos.get(1).getStatus().getCode()).isEqualTo(Code.UNAVAILABLE);
1220+
assertThat(connectivityStateInfos.get(1).getStatus().getDescription()).isEqualTo(
1221+
"The subchannel has been ejected by outlier detection");
12091222
}
12101223
}
12111224

@@ -1264,9 +1277,21 @@ public void successRateAndFailurePercentage_errorPercentageOutlier_() { // with
12641277
// The one subchannel that was returning errors should be ejected.
12651278
assertEjectedSubchannels(ImmutableSet.of(ImmutableSet.copyOf(servers.get(0).getAddresses())));
12661279
if (hasHealthConsumer) {
1267-
verify(healthListeners.get(servers.get(0))).onSubchannelState(eq(
1268-
ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE)
1269-
));
1280+
ArgumentCaptor<ConnectivityStateInfo> csiCaptor = ArgumentCaptor.forClass(
1281+
ConnectivityStateInfo.class);
1282+
verify(healthListeners.get(servers.get(0)), times(2)).onSubchannelState(csiCaptor.capture());
1283+
List<ConnectivityStateInfo> connectivityStateInfos = csiCaptor.getAllValues();
1284+
1285+
// The subchannel went through two state transitions...
1286+
assertThat(connectivityStateInfos).hasSize(2);
1287+
// ...it first went to the READY state...
1288+
assertThat(connectivityStateInfos.get(0).getState()).isEqualTo(READY);
1289+
1290+
// ...and then to TRANSIENT_FAILURE as outlier detection ejected it.
1291+
assertThat(connectivityStateInfos.get(1).getState()).isEqualTo(TRANSIENT_FAILURE);
1292+
assertThat(connectivityStateInfos.get(1).getStatus().getCode()).isEqualTo(Code.UNAVAILABLE);
1293+
assertThat(connectivityStateInfos.get(1).getStatus().getDescription()).isEqualTo(
1294+
"The subchannel has been ejected by outlier detection");
12701295
}
12711296
}
12721297

0 commit comments

Comments
 (0)
Please sign in to comment.