|
@@ -18,6 +18,7 @@
|
|
|
|
|
|
package org.apache.hadoop.fs.azurebfs;
|
|
|
|
|
|
+import java.time.Instant;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
import java.util.concurrent.Callable;
|
|
@@ -25,12 +26,26 @@ import java.util.concurrent.ExecutorService;
|
|
|
import java.util.concurrent.Executors;
|
|
|
import java.util.concurrent.Future;
|
|
|
|
|
|
-import org.junit.Assert;
|
|
|
+import org.assertj.core.api.Assertions;
|
|
|
import org.junit.Test;
|
|
|
+import org.junit.Assert;
|
|
|
|
|
|
+import org.apache.hadoop.fs.azurebfs.services.AbfsClient;
|
|
|
+import org.apache.hadoop.fs.azurebfs.services.AbfsHttpOperation;
|
|
|
+import org.apache.hadoop.fs.azurebfs.services.AbfsRestOperation;
|
|
|
+import org.apache.hadoop.fs.azurebfs.services.TestAbfsClient;
|
|
|
import org.apache.hadoop.fs.FileStatus;
|
|
|
import org.apache.hadoop.fs.Path;
|
|
|
|
|
|
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
|
|
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
|
|
|
+import static java.net.HttpURLConnection.HTTP_OK;
|
|
|
+import static java.util.UUID.randomUUID;
|
|
|
+
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+
|
|
|
+import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.DEFAULT_CLOCK_SKEW_WITH_SERVER_IN_MS;
|
|
|
import static org.apache.hadoop.fs.contract.ContractTestUtils.assertMkdirs;
|
|
|
import static org.apache.hadoop.fs.contract.ContractTestUtils.assertPathDoesNotExist;
|
|
|
import static org.apache.hadoop.fs.contract.ContractTestUtils.assertRenameOutcome;
|
|
@@ -42,6 +57,9 @@ import static org.apache.hadoop.fs.contract.ContractTestUtils.assertIsFile;
|
|
|
public class ITestAzureBlobFileSystemRename extends
|
|
|
AbstractAbfsIntegrationTest {
|
|
|
|
|
|
+ private static final int REDUCED_RETRY_COUNT = 1;
|
|
|
+ private static final int REDUCED_MAX_BACKOFF_INTERVALS_MS = 5000;
|
|
|
+
|
|
|
public ITestAzureBlobFileSystemRename() throws Exception {
|
|
|
super();
|
|
|
}
|
|
@@ -149,4 +167,97 @@ public class ITestAzureBlobFileSystemRename extends
|
|
|
assertTrue(fs.exists(new Path("testDir2/test4/test3")));
|
|
|
assertFalse(fs.exists(new Path("testDir2/test1/test2/test3")));
|
|
|
}
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testRenameRetryFailureAsHTTP400() throws Exception {
|
|
|
+ // Rename failed as Bad Request
|
|
|
+ // RenameIdempotencyCheck should throw back the rename failure Op
|
|
|
+ testRenameTimeout(HTTP_BAD_REQUEST, HTTP_BAD_REQUEST, false,
|
|
|
+ "renameIdempotencyCheckOp should return rename BadRequest "
|
|
|
+ + "response itself.");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testRenameRetryFailureAsHTTP404() throws Exception {
|
|
|
+ // Rename failed as FileNotFound and the destination LMT is
|
|
|
+ // within TimespanForIdentifyingRecentOperationThroughLMT
|
|
|
+ testRenameTimeout(HTTP_NOT_FOUND, HTTP_OK, false,
|
|
|
+ "Rename should return success response because the destination "
|
|
|
+ + "path is present and its LMT is within "
|
|
|
+ + "TimespanForIdentifyingRecentOperationThroughLMT.");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testRenameRetryFailureWithDestOldLMT() throws Exception {
|
|
|
+ // Rename failed as FileNotFound and the destination LMT is
|
|
|
+ // older than TimespanForIdentifyingRecentOperationThroughLMT
|
|
|
+ testRenameTimeout(HTTP_NOT_FOUND, HTTP_NOT_FOUND, true,
|
|
|
+ "Rename should return original rename failure response "
|
|
|
+ + "because the destination path LMT is older than "
|
|
|
+ + "TimespanForIdentifyingRecentOperationThroughLMT.");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void testRenameTimeout(
|
|
|
+ int renameRequestStatus,
|
|
|
+ int renameIdempotencyCheckStatus,
|
|
|
+ boolean isOldOp,
|
|
|
+ String assertMessage) throws Exception {
|
|
|
+ // Config to reduce the retry and maxBackoff time for test run
|
|
|
+ AbfsConfiguration abfsConfig
|
|
|
+ = TestAbfsConfigurationFieldsValidation.updateRetryConfigs(
|
|
|
+ getConfiguration(),
|
|
|
+ REDUCED_RETRY_COUNT, REDUCED_MAX_BACKOFF_INTERVALS_MS);
|
|
|
+
|
|
|
+ final AzureBlobFileSystem fs = getFileSystem();
|
|
|
+ AbfsClient abfsClient = fs.getAbfsStore().getClient();
|
|
|
+ AbfsClient testClient = TestAbfsClient.createTestClientFromCurrentContext(
|
|
|
+ abfsClient,
|
|
|
+ abfsConfig);
|
|
|
+
|
|
|
+ // Mock instance of AbfsRestOperation
|
|
|
+ AbfsRestOperation op = mock(AbfsRestOperation.class);
|
|
|
+ // Set retryCount to non-zero
|
|
|
+ when(op.isARetriedRequest()).thenReturn(true);
|
|
|
+
|
|
|
+ // Mock instance of Http Operation response. This will return HTTP:Bad Request
|
|
|
+ AbfsHttpOperation http400Op = mock(AbfsHttpOperation.class);
|
|
|
+ when(http400Op.getStatusCode()).thenReturn(HTTP_BAD_REQUEST);
|
|
|
+
|
|
|
+ // Mock instance of Http Operation response. This will return HTTP:Not Found
|
|
|
+ AbfsHttpOperation http404Op = mock(AbfsHttpOperation.class);
|
|
|
+ when(http404Op.getStatusCode()).thenReturn(HTTP_NOT_FOUND);
|
|
|
+
|
|
|
+ Path destinationPath = fs.makeQualified(
|
|
|
+ new Path("destination" + randomUUID().toString()));
|
|
|
+
|
|
|
+ Instant renameRequestStartTime = Instant.now();
|
|
|
+
|
|
|
+ if (renameRequestStatus == HTTP_BAD_REQUEST) {
|
|
|
+ when(op.getResult()).thenReturn(http400Op);
|
|
|
+ } else if (renameRequestStatus == HTTP_NOT_FOUND) {
|
|
|
+ // Create the file new.
|
|
|
+ fs.create(destinationPath);
|
|
|
+ when(op.getResult()).thenReturn(http404Op);
|
|
|
+
|
|
|
+ if (isOldOp) {
|
|
|
+ // instead of sleeping for DEFAULT_CLOCK_SKEW_WITH_SERVER_IN_MS
|
|
|
+ // which will affect test run time
|
|
|
+ // will modify renameRequestStartTime to a future time so that
|
|
|
+ // lmt will qualify for old op
|
|
|
+ renameRequestStartTime = renameRequestStartTime.plusSeconds(
|
|
|
+ DEFAULT_CLOCK_SKEW_WITH_SERVER_IN_MS);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ Assertions.assertThat(testClient.renameIdempotencyCheckOp(
|
|
|
+ renameRequestStartTime,
|
|
|
+ op,
|
|
|
+ destinationPath.toUri().getPath())
|
|
|
+ .getResult()
|
|
|
+ .getStatusCode())
|
|
|
+ .describedAs(assertMessage)
|
|
|
+ .isEqualTo(renameIdempotencyCheckStatus);
|
|
|
+ }
|
|
|
+
|
|
|
}
|