|
@@ -18,19 +18,28 @@
|
|
|
|
|
|
package org.apache.hadoop.fs.azurebfs.services;
|
|
|
|
|
|
+import java.io.IOException;
|
|
|
+import java.net.URI;
|
|
|
+import java.net.URISyntaxException;
|
|
|
+
|
|
|
import org.assertj.core.api.Assertions;
|
|
|
import org.junit.Test;
|
|
|
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
import org.apache.hadoop.fs.FSDataOutputStream;
|
|
|
+import org.apache.hadoop.fs.Path;
|
|
|
+import org.apache.hadoop.fs.PathIOException;
|
|
|
import org.apache.hadoop.fs.azurebfs.AbstractAbfsIntegrationTest;
|
|
|
import org.apache.hadoop.fs.azurebfs.AzureBlobFileSystem;
|
|
|
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
|
|
+import org.apache.hadoop.test.LambdaTestUtils;
|
|
|
|
|
|
/**
|
|
|
* Test create operation.
|
|
|
*/
|
|
|
public class ITestAbfsOutputStream extends AbstractAbfsIntegrationTest {
|
|
|
+
|
|
|
+ private static final int TEST_EXECUTION_TIMEOUT = 2 * 60 * 1000;
|
|
|
private static final String TEST_FILE_PATH = "testfile";
|
|
|
|
|
|
public ITestAbfsOutputStream() throws Exception {
|
|
@@ -84,4 +93,73 @@ public class ITestAbfsOutputStream extends AbstractAbfsIntegrationTest {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Verify the passing of AzureBlobFileSystem reference to AbfsOutputStream
|
|
|
+ * to make sure that the FS instance is not eligible for GC while writing.
|
|
|
+ */
|
|
|
+ @Test(timeout = TEST_EXECUTION_TIMEOUT)
|
|
|
+ public void testAzureBlobFileSystemBackReferenceInOutputStream()
|
|
|
+ throws Exception {
|
|
|
+ byte[] testBytes = new byte[5 * 1024];
|
|
|
+ // Creating an output stream using a FS in a separate method to make the
|
|
|
+ // FS instance used eligible for GC. Since when a method is popped from
|
|
|
+ // the stack frame, it's variables become anonymous, this creates higher
|
|
|
+ // chance of getting Garbage collected.
|
|
|
+ try (AbfsOutputStream out = getStream()) {
|
|
|
+ // Every 5KB block written is flushed and a GC is hinted, if the
|
|
|
+ // executor service is shut down in between, the test should fail
|
|
|
+ // indicating premature shutdown while writing.
|
|
|
+ for (int i = 0; i < 5; i++) {
|
|
|
+ out.write(testBytes);
|
|
|
+ out.flush();
|
|
|
+ System.gc();
|
|
|
+ Assertions.assertThat(
|
|
|
+ out.getExecutorService().isShutdown() || out.getExecutorService()
|
|
|
+ .isTerminated())
|
|
|
+ .describedAs("Executor Service should not be closed before "
|
|
|
+ + "OutputStream while writing")
|
|
|
+ .isFalse();
|
|
|
+ Assertions.assertThat(out.getFsBackRef().isNull())
|
|
|
+ .describedAs("BackReference in output stream should not be null")
|
|
|
+ .isFalse();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Verify AbfsOutputStream close() behaviour of throwing a PathIOE when the
|
|
|
+ * FS instance is closed before the stream.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testAbfsOutputStreamClosingFsBeforeStream()
|
|
|
+ throws Exception {
|
|
|
+ AzureBlobFileSystem fs = new AzureBlobFileSystem();
|
|
|
+ fs.initialize(new URI(getTestUrl()), new Configuration());
|
|
|
+ Path pathFs = path(getMethodName());
|
|
|
+ byte[] inputBytes = new byte[5 * 1024];
|
|
|
+ try (AbfsOutputStream out = createAbfsOutputStreamWithFlushEnabled(fs,
|
|
|
+ pathFs)) {
|
|
|
+ out.write(inputBytes);
|
|
|
+ fs.close();
|
|
|
+ // verify that output stream close after fs.close() would raise a
|
|
|
+ // pathIOE containing the path being written to.
|
|
|
+ LambdaTestUtils
|
|
|
+ .intercept(PathIOException.class, getMethodName(), out::close);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Separate method to create an outputStream using a local FS instance so
|
|
|
+ * that once this method has returned, the FS instance can be eligible for GC.
|
|
|
+ *
|
|
|
+ * @return AbfsOutputStream used for writing.
|
|
|
+ */
|
|
|
+ private AbfsOutputStream getStream() throws URISyntaxException, IOException {
|
|
|
+ AzureBlobFileSystem fs1 = new AzureBlobFileSystem();
|
|
|
+ fs1.initialize(new URI(getTestUrl()), new Configuration());
|
|
|
+ Path pathFs1 = path(getMethodName() + "1");
|
|
|
+
|
|
|
+ return createAbfsOutputStreamWithFlushEnabled(fs1, pathFs1);
|
|
|
+ }
|
|
|
+
|
|
|
}
|