Skip to content

Commit

Permalink
Fix flaky retry tests (grpc#10887)
Browse files Browse the repository at this point in the history
* Reorder tracing and actually closing listener to eliminate test flakiness
* Use real value rather than mock for flaky test
  • Loading branch information
larry-safran committed Feb 5, 2024
1 parent ace0dcd commit 340e493
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -455,10 +455,10 @@ private void closeListener(
if (!listenerClosed) {
listenerClosed = true;
statsTraceCtx.streamClosed(status);
listener().closed(status, rpcProgress, trailers);
if (getTransportTracer() != null) {
getTransportTracer().reportStreamClosed(status.isOk());
}
listener().closed(status, rpcProgress, trailers);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

import static com.google.common.truth.Truth.assertThat;
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.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -78,8 +81,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

Expand All @@ -103,8 +104,11 @@ public class RetryTest {
@Rule
public final GrpcCleanupRule cleanupRule = new GrpcCleanupRule();
private final FakeClock fakeClock = new FakeClock();
@Mock
private ClientCall.Listener<Integer> mockCallListener;
private TestListener testCallListener = new TestListener();
@SuppressWarnings("unchecked")
private ClientCall.Listener<Integer> mockCallListener =
mock(ClientCall.Listener.class, delegatesTo(testCallListener));

private CountDownLatch backoffLatch = new CountDownLatch(1);
private final EventLoopGroup group = new DefaultEventLoopGroup() {
@SuppressWarnings("FutureReturnValueIgnored")
Expand Down Expand Up @@ -244,8 +248,10 @@ private void assertInboundWireSizeRecorded(long length) throws Exception {

private void assertRpcStatusRecorded(
Status.Code code, long roundtripLatencyMs, long outboundMessages) throws Exception {
MetricsRecord record = clientStatsRecorder.pollRecord(5, SECONDS);
MetricsRecord record = clientStatsRecorder.pollRecord(7, SECONDS);
assertNotNull(record);
TagValue statusTag = record.tags.get(RpcMeasureConstants.GRPC_CLIENT_STATUS);
assertNotNull(statusTag);
assertThat(statusTag.asString()).isEqualTo(code.toString());
assertThat(record.getMetricAsLongOrFail(DeprecatedCensusConstants.RPC_CLIENT_FINISHED_COUNT))
.isEqualTo(1);
Expand Down Expand Up @@ -295,14 +301,14 @@ public void retryUntilBufferLimitExceeded() throws Exception {
verify(mockCallListener, never()).onClose(any(Status.class), any(Metadata.class));
// send one more message, should exceed buffer limit
call.sendMessage(message);

// let attempt fail
testCallListener.clear();
serverCall.close(
Status.UNAVAILABLE.withDescription("2nd attempt failed"),
new Metadata());
// no more retry
ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class);
verify(mockCallListener, timeout(5000)).onClose(statusCaptor.capture(), any(Metadata.class));
assertThat(statusCaptor.getValue().getDescription()).contains("2nd attempt failed");
testCallListener.verifyDescription("2nd attempt failed", 5000);
}

@Test
Expand Down Expand Up @@ -534,4 +540,26 @@ public ClientStreamTracer newClientStreamTracer(StreamInfo info, Metadata header
assertRpcStatusRecorded(Code.INVALID_ARGUMENT, 0, 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);
}
}
}

0 comments on commit 340e493

Please sign in to comment.