|
@@ -29,6 +29,7 @@ import org.junit.Test;
|
|
import org.mockito.Mockito;
|
|
import org.mockito.Mockito;
|
|
import org.mockito.stubbing.Stubber;
|
|
import org.mockito.stubbing.Stubber;
|
|
|
|
|
|
|
|
+import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
|
import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
|
|
import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
|
|
|
|
|
|
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
|
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
|
@@ -37,8 +38,11 @@ import static java.net.HttpURLConnection.HTTP_OK;
|
|
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
|
|
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
|
|
import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.EGRESS_OVER_ACCOUNT_LIMIT;
|
|
import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.EGRESS_OVER_ACCOUNT_LIMIT;
|
|
import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.INGRESS_OVER_ACCOUNT_LIMIT;
|
|
import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.INGRESS_OVER_ACCOUNT_LIMIT;
|
|
|
|
+import static org.apache.hadoop.fs.azurebfs.services.RetryPolicyConstants.EXPONENTIAL_RETRY_POLICY_ABBREVIATION;
|
|
|
|
+import static org.apache.hadoop.fs.azurebfs.services.RetryPolicyConstants.STATIC_RETRY_POLICY_ABBREVIATION;
|
|
import static org.apache.hadoop.fs.azurebfs.services.AbfsClientTestUtil.addGeneralMockBehaviourToAbfsClient;
|
|
import static org.apache.hadoop.fs.azurebfs.services.AbfsClientTestUtil.addGeneralMockBehaviourToAbfsClient;
|
|
import static org.apache.hadoop.fs.azurebfs.services.AbfsClientTestUtil.addGeneralMockBehaviourToRestOpAndHttpOp;
|
|
import static org.apache.hadoop.fs.azurebfs.services.AbfsClientTestUtil.addGeneralMockBehaviourToRestOpAndHttpOp;
|
|
|
|
+
|
|
import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.CONNECTION_RESET_ABBREVIATION;
|
|
import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.CONNECTION_RESET_ABBREVIATION;
|
|
import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.CONNECTION_RESET_MESSAGE;
|
|
import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.CONNECTION_RESET_MESSAGE;
|
|
import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.CONNECTION_TIMEOUT_ABBREVIATION;
|
|
import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.CONNECTION_TIMEOUT_ABBREVIATION;
|
|
@@ -54,6 +58,8 @@ import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.SOCKET
|
|
import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.UNKNOWN_HOST_EXCEPTION_ABBREVIATION;
|
|
import static org.apache.hadoop.fs.azurebfs.services.RetryReasonConstants.UNKNOWN_HOST_EXCEPTION_ABBREVIATION;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.nullable;
|
|
import static org.mockito.ArgumentMatchers.nullable;
|
|
|
|
+import static org.mockito.Mockito.times;
|
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
|
|
|
public class TestAbfsRestOperationMockFailures {
|
|
public class TestAbfsRestOperationMockFailures {
|
|
|
|
|
|
@@ -63,7 +69,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
String[] abbreviations = new String[1];
|
|
String[] abbreviations = new String[1];
|
|
exceptions[0] = new SocketTimeoutException(CONNECTION_TIMEOUT_JDK_MESSAGE);
|
|
exceptions[0] = new SocketTimeoutException(CONNECTION_TIMEOUT_JDK_MESSAGE);
|
|
abbreviations[0] = CONNECTION_TIMEOUT_ABBREVIATION;
|
|
abbreviations[0] = CONNECTION_TIMEOUT_ABBREVIATION;
|
|
- testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1);
|
|
|
|
|
|
+ testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1, 1);
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -75,7 +81,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
abbreviations[0] = CONNECTION_TIMEOUT_ABBREVIATION;
|
|
abbreviations[0] = CONNECTION_TIMEOUT_ABBREVIATION;
|
|
exceptions[1] = new SocketTimeoutException(READ_TIMEOUT_JDK_MESSAGE);
|
|
exceptions[1] = new SocketTimeoutException(READ_TIMEOUT_JDK_MESSAGE);
|
|
abbreviations[1] = READ_TIMEOUT_ABBREVIATION;
|
|
abbreviations[1] = READ_TIMEOUT_ABBREVIATION;
|
|
- testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1);
|
|
|
|
|
|
+ testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1, 1);
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -84,7 +90,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
String[] abbreviations = new String[1];
|
|
String[] abbreviations = new String[1];
|
|
exceptions[0] = new SocketTimeoutException(READ_TIMEOUT_JDK_MESSAGE);
|
|
exceptions[0] = new SocketTimeoutException(READ_TIMEOUT_JDK_MESSAGE);
|
|
abbreviations[0] = READ_TIMEOUT_ABBREVIATION;
|
|
abbreviations[0] = READ_TIMEOUT_ABBREVIATION;
|
|
- testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1);
|
|
|
|
|
|
+ testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1, 0);
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -93,7 +99,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
String[] abbreviations = new String[1];
|
|
String[] abbreviations = new String[1];
|
|
exceptions[0] = new UnknownHostException();
|
|
exceptions[0] = new UnknownHostException();
|
|
abbreviations[0] = UNKNOWN_HOST_EXCEPTION_ABBREVIATION;
|
|
abbreviations[0] = UNKNOWN_HOST_EXCEPTION_ABBREVIATION;
|
|
- testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1);
|
|
|
|
|
|
+ testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1, 0);
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -102,7 +108,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
String[] abbreviations = new String[1];
|
|
String[] abbreviations = new String[1];
|
|
exceptions[0] = new SocketTimeoutException(CONNECTION_RESET_MESSAGE + " by peer");
|
|
exceptions[0] = new SocketTimeoutException(CONNECTION_RESET_MESSAGE + " by peer");
|
|
abbreviations[0] = CONNECTION_RESET_ABBREVIATION;
|
|
abbreviations[0] = CONNECTION_RESET_ABBREVIATION;
|
|
- testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1);
|
|
|
|
|
|
+ testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1, 0);
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -111,7 +117,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
String[] abbreviations = new String[1];
|
|
String[] abbreviations = new String[1];
|
|
exceptions[0] = new SocketException("unknown");
|
|
exceptions[0] = new SocketException("unknown");
|
|
abbreviations[0] = SOCKET_EXCEPTION_ABBREVIATION;
|
|
abbreviations[0] = SOCKET_EXCEPTION_ABBREVIATION;
|
|
- testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1);
|
|
|
|
|
|
+ testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1, 0);
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -120,7 +126,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
String[] abbreviations = new String[1];
|
|
String[] abbreviations = new String[1];
|
|
exceptions[0] = new InterruptedIOException();
|
|
exceptions[0] = new InterruptedIOException();
|
|
abbreviations[0] = IO_EXCEPTION_ABBREVIATION;
|
|
abbreviations[0] = IO_EXCEPTION_ABBREVIATION;
|
|
- testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1);
|
|
|
|
|
|
+ testClientRequestIdForTimeoutRetry(exceptions, abbreviations, 1, 0);
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -158,16 +164,115 @@ public class TestAbfsRestOperationMockFailures {
|
|
testClientRequestIdForStatusRetry(HTTP_UNAVAILABLE, "Other.", "503");
|
|
testClientRequestIdForStatusRetry(HTTP_UNAVAILABLE, "Other.", "503");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Test for mocking the failure scenario with retry policy assertions.
|
|
|
|
+ * Here we will try to create a request with following life cycle:
|
|
|
|
+ * 1. Primary Request made fails with Connection Timeout and fall into retry loop
|
|
|
|
+ * 2. Retried request fails with 503 and again go for retry
|
|
|
|
+ * 3. Retried request fails with 503 and do not go for retry.
|
|
|
|
+ *
|
|
|
|
+ * We will try to assert that:
|
|
|
|
+ * 1. Correct retry policy is used to get the retry interval for each failed request
|
|
|
|
+ * 2. Tracing header construction takes place with proper arguments based on the failure reason and retry policy used
|
|
|
|
+ * @throws Exception
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testRetryPolicyWithDifferentFailureReasons() throws Exception {
|
|
|
|
+
|
|
|
|
+ AbfsClient abfsClient = Mockito.mock(AbfsClient.class);
|
|
|
|
+ ExponentialRetryPolicy exponentialRetryPolicy = Mockito.mock(
|
|
|
|
+ ExponentialRetryPolicy.class);
|
|
|
|
+ StaticRetryPolicy staticRetryPolicy = Mockito.mock(StaticRetryPolicy.class);
|
|
|
|
+ AbfsThrottlingIntercept intercept = Mockito.mock(
|
|
|
|
+ AbfsThrottlingIntercept.class);
|
|
|
|
+ addGeneralMockBehaviourToAbfsClient(abfsClient, exponentialRetryPolicy, staticRetryPolicy, intercept);
|
|
|
|
+
|
|
|
|
+ AbfsRestOperation abfsRestOperation = Mockito.spy(new AbfsRestOperation(
|
|
|
|
+ AbfsRestOperationType.ReadFile,
|
|
|
|
+ abfsClient,
|
|
|
|
+ "PUT",
|
|
|
|
+ null,
|
|
|
|
+ new ArrayList<>()
|
|
|
|
+ ));
|
|
|
|
+
|
|
|
|
+ AbfsHttpOperation httpOperation = Mockito.mock(AbfsHttpOperation.class);
|
|
|
|
+ addGeneralMockBehaviourToRestOpAndHttpOp(abfsRestOperation, httpOperation);
|
|
|
|
+
|
|
|
|
+ Stubber stubber = Mockito.doThrow(new SocketTimeoutException(CONNECTION_TIMEOUT_JDK_MESSAGE));
|
|
|
|
+ stubber.doNothing().when(httpOperation).processResponse(
|
|
|
|
+ nullable(byte[].class), nullable(int.class), nullable(int.class));
|
|
|
|
+
|
|
|
|
+ when(httpOperation.getStatusCode()).thenReturn(-1).thenReturn(HTTP_UNAVAILABLE);
|
|
|
|
+
|
|
|
|
+ TracingContext tracingContext = Mockito.mock(TracingContext.class);
|
|
|
|
+ Mockito.doNothing().when(tracingContext).setRetryCount(nullable(int.class));
|
|
|
|
+ Mockito.doReturn("").when(httpOperation).getStorageErrorMessage();
|
|
|
|
+ Mockito.doReturn("").when(httpOperation).getStorageErrorCode();
|
|
|
|
+ Mockito.doReturn("HEAD").when(httpOperation).getMethod();
|
|
|
|
+ Mockito.doReturn(tracingContext).when(abfsRestOperation).createNewTracingContext(any());
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ // Operation will fail with CT first and then 503 thereafter.
|
|
|
|
+ abfsRestOperation.execute(tracingContext);
|
|
|
|
+ } catch(AbfsRestOperationException ex) {
|
|
|
|
+ Assertions.assertThat(ex.getStatusCode())
|
|
|
|
+ .describedAs("Status Code must be HTTP_UNAVAILABLE(409)")
|
|
|
|
+ .isEqualTo(HTTP_UNAVAILABLE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Assert that httpOperation.processResponse was called 3 times.
|
|
|
|
+ // One for retry count 0
|
|
|
|
+ // One for retry count 1 after failing with CT
|
|
|
|
+ // One for retry count 2 after failing with 50
|
|
|
|
+ Mockito.verify(httpOperation, times(3)).processResponse(
|
|
|
|
+ nullable(byte[].class), nullable(int.class), nullable(int.class));
|
|
|
|
+
|
|
|
|
+ // Assert that Static Retry Policy was used after CT failure.
|
|
|
|
+ // Iteration 1 failed with CT and shouldRetry was called with retry count 0
|
|
|
|
+ // Before iteration 2 sleep will be computed using static retry policy and retry count 1
|
|
|
|
+ Mockito.verify(abfsClient, Mockito.times(1))
|
|
|
|
+ .getRetryPolicy(CONNECTION_TIMEOUT_ABBREVIATION);
|
|
|
|
+ Mockito.verify(staticRetryPolicy, Mockito.times(1))
|
|
|
|
+ .shouldRetry(0, -1);
|
|
|
|
+ Mockito.verify(staticRetryPolicy, Mockito.times(1))
|
|
|
|
+ .getRetryInterval(1);
|
|
|
|
+ Mockito.verify(tracingContext, Mockito.times(1))
|
|
|
|
+ .constructHeader(httpOperation, CONNECTION_TIMEOUT_ABBREVIATION, STATIC_RETRY_POLICY_ABBREVIATION);
|
|
|
|
+
|
|
|
|
+ // Assert that exponential Retry Policy was used during second and third Iteration.
|
|
|
|
+ // Iteration 2 and 3 failed with 503 and should retry was called with retry count 1 and 2
|
|
|
|
+ // Before iteration 3 sleep will be computed using exponential retry policy and retry count 2
|
|
|
|
+ // Should retry with retry count 2 will return false and no further requests will be made.
|
|
|
|
+ Mockito.verify(abfsClient, Mockito.times(2))
|
|
|
|
+ .getRetryPolicy("503");
|
|
|
|
+ Mockito.verify(exponentialRetryPolicy, Mockito.times(1))
|
|
|
|
+ .shouldRetry(1, HTTP_UNAVAILABLE);
|
|
|
|
+ Mockito.verify(exponentialRetryPolicy, Mockito.times(1))
|
|
|
|
+ .shouldRetry(2, HTTP_UNAVAILABLE);
|
|
|
|
+ Mockito.verify(exponentialRetryPolicy, Mockito.times(1))
|
|
|
|
+ .getRetryInterval(2);
|
|
|
|
+ Mockito.verify(tracingContext, Mockito.times(1))
|
|
|
|
+ .constructHeader(httpOperation, "503", EXPONENTIAL_RETRY_POLICY_ABBREVIATION);
|
|
|
|
+
|
|
|
|
+ // Assert that intercept.updateMetrics was called only once during second Iteration
|
|
|
|
+ Mockito.verify(intercept, Mockito.times(2))
|
|
|
|
+ .updateMetrics(nullable(AbfsRestOperationType.class), nullable(AbfsHttpOperation.class));
|
|
|
|
+ }
|
|
|
|
+
|
|
private void testClientRequestIdForStatusRetry(int status,
|
|
private void testClientRequestIdForStatusRetry(int status,
|
|
String serverErrorMessage,
|
|
String serverErrorMessage,
|
|
String keyExpected) throws Exception {
|
|
String keyExpected) throws Exception {
|
|
|
|
|
|
AbfsClient abfsClient = Mockito.mock(AbfsClient.class);
|
|
AbfsClient abfsClient = Mockito.mock(AbfsClient.class);
|
|
- ExponentialRetryPolicy retryPolicy = Mockito.mock(
|
|
|
|
|
|
+ ExponentialRetryPolicy exponentialRetryPolicy = Mockito.mock(
|
|
ExponentialRetryPolicy.class);
|
|
ExponentialRetryPolicy.class);
|
|
- addGeneralMockBehaviourToAbfsClient(abfsClient, retryPolicy);
|
|
|
|
-
|
|
|
|
|
|
+ StaticRetryPolicy staticRetryPolicy = Mockito.mock(StaticRetryPolicy.class);
|
|
|
|
+ AbfsThrottlingIntercept intercept = Mockito.mock(
|
|
|
|
+ AbfsThrottlingIntercept.class);
|
|
|
|
+ addGeneralMockBehaviourToAbfsClient(abfsClient, exponentialRetryPolicy, staticRetryPolicy, intercept);
|
|
|
|
|
|
|
|
+ // Create a readfile operation that will fail
|
|
AbfsRestOperation abfsRestOperation = Mockito.spy(new AbfsRestOperation(
|
|
AbfsRestOperation abfsRestOperation = Mockito.spy(new AbfsRestOperation(
|
|
AbfsRestOperationType.ReadFile,
|
|
AbfsRestOperationType.ReadFile,
|
|
abfsClient,
|
|
abfsClient,
|
|
@@ -201,8 +306,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
|
|
|
|
TracingContext tracingContext = Mockito.mock(TracingContext.class);
|
|
TracingContext tracingContext = Mockito.mock(TracingContext.class);
|
|
Mockito.doNothing().when(tracingContext).setRetryCount(nullable(int.class));
|
|
Mockito.doNothing().when(tracingContext).setRetryCount(nullable(int.class));
|
|
- Mockito.doReturn(tracingContext)
|
|
|
|
- .when(abfsRestOperation).createNewTracingContext(any());
|
|
|
|
|
|
+ Mockito.doReturn(tracingContext).when(abfsRestOperation).createNewTracingContext(any());
|
|
|
|
|
|
int[] count = new int[1];
|
|
int[] count = new int[1];
|
|
count[0] = 0;
|
|
count[0] = 0;
|
|
@@ -213,7 +317,7 @@ public class TestAbfsRestOperationMockFailures {
|
|
}
|
|
}
|
|
count[0]++;
|
|
count[0]++;
|
|
return null;
|
|
return null;
|
|
- }).when(tracingContext).constructHeader(any(), any());
|
|
|
|
|
|
+ }).when(tracingContext).constructHeader(any(), any(), any());
|
|
|
|
|
|
abfsRestOperation.execute(tracingContext);
|
|
abfsRestOperation.execute(tracingContext);
|
|
Assertions.assertThat(count[0]).isEqualTo(2);
|
|
Assertions.assertThat(count[0]).isEqualTo(2);
|
|
@@ -222,12 +326,14 @@ public class TestAbfsRestOperationMockFailures {
|
|
|
|
|
|
private void testClientRequestIdForTimeoutRetry(Exception[] exceptions,
|
|
private void testClientRequestIdForTimeoutRetry(Exception[] exceptions,
|
|
String[] abbreviationsExpected,
|
|
String[] abbreviationsExpected,
|
|
- int len) throws Exception {
|
|
|
|
|
|
+ int len, int numOfCTExceptions) throws Exception {
|
|
AbfsClient abfsClient = Mockito.mock(AbfsClient.class);
|
|
AbfsClient abfsClient = Mockito.mock(AbfsClient.class);
|
|
- ExponentialRetryPolicy retryPolicy = Mockito.mock(
|
|
|
|
|
|
+ ExponentialRetryPolicy exponentialRetryPolicy = Mockito.mock(
|
|
ExponentialRetryPolicy.class);
|
|
ExponentialRetryPolicy.class);
|
|
- addGeneralMockBehaviourToAbfsClient(abfsClient, retryPolicy);
|
|
|
|
-
|
|
|
|
|
|
+ StaticRetryPolicy staticRetryPolicy = Mockito.mock(StaticRetryPolicy.class);
|
|
|
|
+ AbfsThrottlingIntercept intercept = Mockito.mock(
|
|
|
|
+ AbfsThrottlingIntercept.class);
|
|
|
|
+ addGeneralMockBehaviourToAbfsClient(abfsClient, exponentialRetryPolicy, staticRetryPolicy, intercept);
|
|
|
|
|
|
AbfsRestOperation abfsRestOperation = Mockito.spy(new AbfsRestOperation(
|
|
AbfsRestOperation abfsRestOperation = Mockito.spy(new AbfsRestOperation(
|
|
AbfsRestOperationType.ReadFile,
|
|
AbfsRestOperationType.ReadFile,
|
|
@@ -265,9 +371,19 @@ public class TestAbfsRestOperationMockFailures {
|
|
}
|
|
}
|
|
count[0]++;
|
|
count[0]++;
|
|
return null;
|
|
return null;
|
|
- }).when(tracingContext).constructHeader(any(), any());
|
|
|
|
|
|
+ }).when(tracingContext).constructHeader(any(), any(), any());
|
|
|
|
|
|
abfsRestOperation.execute(tracingContext);
|
|
abfsRestOperation.execute(tracingContext);
|
|
Assertions.assertThat(count[0]).isEqualTo(len + 1);
|
|
Assertions.assertThat(count[0]).isEqualTo(len + 1);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Assert that getRetryPolicy was called with
|
|
|
|
+ * failureReason CT only for Connection Timeout Cases.
|
|
|
|
+ * For every failed request getRetryPolicy will be called three times
|
|
|
|
+ * It will be called with failureReason CT for every request failing with CT
|
|
|
|
+ */
|
|
|
|
+ Mockito.verify(abfsClient, Mockito.times(
|
|
|
|
+ numOfCTExceptions))
|
|
|
|
+ .getRetryPolicy(CONNECTION_TIMEOUT_ABBREVIATION);
|
|
}
|
|
}
|
|
}
|
|
}
|