|
54 | 54 | import io.grpc.Status; |
55 | 55 | import io.grpc.StatusRuntimeException; |
56 | 56 | import java.io.IOException; |
| 57 | +import java.time.Instant; |
57 | 58 | import java.util.ArrayList; |
58 | 59 | import java.util.Arrays; |
59 | 60 | import java.util.HashMap; |
@@ -86,12 +87,15 @@ public class StreamWriterTest { |
86 | 87 | private static final String EXPLICIT_STREAM = "projects/p/datasets/d1/tables/t1/streams/s1"; |
87 | 88 | private static final String TEST_TRACE_ID = "DATAFLOW:job_id"; |
88 | 89 | private static final int MAX_RETRY_NUM_ATTEMPTS = 3; |
| 90 | + private static final long INITIAL_RETRY_MILLIS = 500; |
| 91 | + private static final double RETRY_MULTIPLIER = 1.3; |
| 92 | + private static final int MAX_RETRY_DELAY_MINUTES = 5; |
89 | 93 | private static final RetrySettings retrySettings = |
90 | 94 | RetrySettings.newBuilder() |
91 | | - .setInitialRetryDelay(Duration.ofMillis(500)) |
92 | | - .setRetryDelayMultiplier(1.1) |
| 95 | + .setInitialRetryDelay(Duration.ofMillis(INITIAL_RETRY_MILLIS)) |
| 96 | + .setRetryDelayMultiplier(RETRY_MULTIPLIER) |
93 | 97 | .setMaxAttempts(MAX_RETRY_NUM_ATTEMPTS) |
94 | | - .setMaxRetryDelay(org.threeten.bp.Duration.ofMinutes(5)) |
| 98 | + .setMaxRetryDelay(org.threeten.bp.Duration.ofMinutes(MAX_RETRY_DELAY_MINUTES)) |
95 | 99 | .build(); |
96 | 100 | private FakeScheduledExecutorService fakeExecutor; |
97 | 101 | private FakeBigQueryWrite testBigQueryWrite; |
@@ -2003,6 +2007,46 @@ public void testExclusiveAppendSuccessAndQuotaErrorRetryMaxRetry() throws Except |
2003 | 2007 | ((StatusRuntimeException) ex.getCause()).getStatus().getCode()); |
2004 | 2008 | } |
2005 | 2009 |
|
| 2010 | + @Test |
| 2011 | + public void testExclusiveAppendQuotaErrorRetryExponentialBackoff() throws Exception { |
| 2012 | + testBigQueryWrite.setReturnErrorDuringExclusiveStreamRetry(true); |
| 2013 | + StreamWriter writer = getTestStreamWriterExclusiveRetryEnabled(); |
| 2014 | + |
| 2015 | + testBigQueryWrite.addResponse( |
| 2016 | + new DummyResponseSupplierWillFailThenSucceed( |
| 2017 | + new FakeBigQueryWriteImpl.Response(createAppendResponse(0)), |
| 2018 | + /* totalFailCount= */ MAX_RETRY_NUM_ATTEMPTS + 1, |
| 2019 | + com.google.rpc.Status.newBuilder().setCode(Code.RESOURCE_EXHAUSTED.ordinal()).build())); |
| 2020 | + |
| 2021 | + ApiFuture<AppendRowsResponse> future = |
| 2022 | + writer.append(createProtoRows(new String[] {String.valueOf(0)}), 0); |
| 2023 | + |
| 2024 | + ExecutionException ex = |
| 2025 | + assertThrows( |
| 2026 | + ExecutionException.class, |
| 2027 | + () -> { |
| 2028 | + future.get(); |
| 2029 | + }); |
| 2030 | + assertEquals( |
| 2031 | + Status.Code.RESOURCE_EXHAUSTED, |
| 2032 | + ((StatusRuntimeException) ex.getCause()).getStatus().getCode()); |
| 2033 | + |
| 2034 | + ArrayList<Instant> instants = testBigQueryWrite.getLatestRequestReceivedInstants(); |
| 2035 | + Instant previousInstant = instants.get(0); |
| 2036 | + // Include initial attempt |
| 2037 | + assertEquals(instants.size(), MAX_RETRY_NUM_ATTEMPTS + 1); |
| 2038 | + double minExpectedDelay = INITIAL_RETRY_MILLIS * 0.95; |
| 2039 | + for (int i = 1; i < instants.size(); i++) { |
| 2040 | + Instant currentInstant = instants.get(i); |
| 2041 | + double differenceInMillis = |
| 2042 | + java.time.Duration.between(previousInstant, currentInstant).toMillis(); |
| 2043 | + assertThat(differenceInMillis).isAtLeast((double) INITIAL_RETRY_MILLIS); |
| 2044 | + assertThat(differenceInMillis).isGreaterThan(minExpectedDelay); |
| 2045 | + minExpectedDelay = minExpectedDelay * RETRY_MULTIPLIER; |
| 2046 | + previousInstant = currentInstant; |
| 2047 | + } |
| 2048 | + } |
| 2049 | + |
2006 | 2050 | @Test |
2007 | 2051 | public void testAppendSuccessAndNonRetryableError() throws Exception { |
2008 | 2052 | StreamWriter writer = getTestStreamWriterRetryEnabled(); |
|
0 commit comments