Fix flaky retry tests (#10887)

* Reorder tracing and actually closing listener to eliminate test flakiness
* Use real value rather than mock for flaky test
This commit is contained in:
Larry Safran 2024-02-05 10:54:55 -08:00 committed by GitHub
parent 3202370684
commit 374dbe9461
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 36 additions and 8 deletions

View File

@ -455,10 +455,10 @@ public abstract class AbstractClientStream extends AbstractStream
if (!listenerClosed) { if (!listenerClosed) {
listenerClosed = true; listenerClosed = true;
statsTraceCtx.streamClosed(status); statsTraceCtx.streamClosed(status);
listener().closed(status, rpcProgress, trailers);
if (getTransportTracer() != null) { if (getTransportTracer() != null) {
getTransportTracer().reportStreamClosed(status.isOk()); getTransportTracer().reportStreamClosed(status.isOk());
} }
listener().closed(status, rpcProgress, trailers);
} }
} }
} }

View File

@ -18,7 +18,10 @@ package io.grpc.testing.integration;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertNotNull;
import static org.mockito.AdditionalAnswers.delegatesTo;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -78,8 +81,6 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
@ -103,8 +104,11 @@ public class RetryTest {
@Rule @Rule
public final GrpcCleanupRule cleanupRule = new GrpcCleanupRule(); public final GrpcCleanupRule cleanupRule = new GrpcCleanupRule();
private final FakeClock fakeClock = new FakeClock(); private final FakeClock fakeClock = new FakeClock();
@Mock private TestListener testCallListener = new TestListener();
private ClientCall.Listener<Integer> mockCallListener; @SuppressWarnings("unchecked")
private ClientCall.Listener<Integer> mockCallListener =
mock(ClientCall.Listener.class, delegatesTo(testCallListener));
private CountDownLatch backoffLatch = new CountDownLatch(1); private CountDownLatch backoffLatch = new CountDownLatch(1);
private final EventLoopGroup group = new DefaultEventLoopGroup() { private final EventLoopGroup group = new DefaultEventLoopGroup() {
@SuppressWarnings("FutureReturnValueIgnored") @SuppressWarnings("FutureReturnValueIgnored")
@ -245,7 +249,9 @@ public class RetryTest {
private void assertRpcStatusRecorded( private void assertRpcStatusRecorded(
Status.Code code, long roundtripLatencyMs, long outboundMessages) throws Exception { Status.Code code, long roundtripLatencyMs, long outboundMessages) throws Exception {
MetricsRecord record = clientStatsRecorder.pollRecord(7, SECONDS); MetricsRecord record = clientStatsRecorder.pollRecord(7, SECONDS);
assertNotNull(record);
TagValue statusTag = record.tags.get(RpcMeasureConstants.GRPC_CLIENT_STATUS); TagValue statusTag = record.tags.get(RpcMeasureConstants.GRPC_CLIENT_STATUS);
assertNotNull(statusTag);
assertThat(statusTag.asString()).isEqualTo(code.toString()); assertThat(statusTag.asString()).isEqualTo(code.toString());
assertThat(record.getMetricAsLongOrFail(DeprecatedCensusConstants.RPC_CLIENT_FINISHED_COUNT)) assertThat(record.getMetricAsLongOrFail(DeprecatedCensusConstants.RPC_CLIENT_FINISHED_COUNT))
.isEqualTo(1); .isEqualTo(1);
@ -295,14 +301,14 @@ public class RetryTest {
verify(mockCallListener, never()).onClose(any(Status.class), any(Metadata.class)); verify(mockCallListener, never()).onClose(any(Status.class), any(Metadata.class));
// send one more message, should exceed buffer limit // send one more message, should exceed buffer limit
call.sendMessage(message); call.sendMessage(message);
// let attempt fail // let attempt fail
testCallListener.clear();
serverCall.close( serverCall.close(
Status.UNAVAILABLE.withDescription("2nd attempt failed"), Status.UNAVAILABLE.withDescription("2nd attempt failed"),
new Metadata()); new Metadata());
// no more retry // no more retry
ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class); testCallListener.verifyDescription("2nd attempt failed", 5000);
verify(mockCallListener, timeout(5000)).onClose(statusCaptor.capture(), any(Metadata.class));
assertThat(statusCaptor.getValue().getDescription()).contains("2nd attempt failed");
} }
@Test @Test
@ -534,4 +540,26 @@ public class RetryTest {
assertRpcStatusRecorded(Code.INVALID_ARGUMENT, 0, 0); assertRpcStatusRecorded(Code.INVALID_ARGUMENT, 0, 0);
assertRetryStatsRecorded(0, 1, 0); assertRetryStatsRecorded(0, 1, 0);
} }
private static class TestListener extends ClientCall.Listener<Integer> {
Status status = null;
private CountDownLatch closeLatch = new CountDownLatch(1);
@Override
public void onClose(Status status, Metadata trailers) {
this.status = status;
closeLatch.countDown();
}
void clear() {
status = null;
closeLatch = new CountDownLatch(1);
}
void verifyDescription(String description, long timeoutMs) throws InterruptedException {
closeLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
assertNotNull(status);
assertThat(status.getDescription()).contains(description);
}
}
} }