/*
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
* (the "License"). You may not use this work except in compliance with the License, which is
* available at www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied, as more fully set forth in the License.
*
* See the NOTICE file distributed with this work for information regarding copyright ownership.
*/
package alluxio.client;
import alluxio.AlluxioURI;
import alluxio.Constants;
import alluxio.LocalAlluxioClusterResource;
import alluxio.PropertyKey;
import alluxio.BaseIntegrationTest;
import alluxio.client.file.FileInStream;
import alluxio.client.file.FileOutStream;
import alluxio.client.file.FileSystem;
import alluxio.client.file.FileSystemTestUtils;
import alluxio.client.file.options.CreateFileOptions;
import alluxio.client.file.options.OpenFileOptions;
import alluxio.util.io.BufferUtils;
import alluxio.util.io.PathUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Integration tests for reading data which is only stored in Alluxio's under storage.
*/
public class UnderStorageReadIntegrationTest extends BaseIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(UnderStorageReadIntegrationTest.class);
private static final int MIN_LEN = 0;
private static final int MAX_LEN = 255;
private static final int DELTA = 33;
private FileSystem mFileSystem = null;
private CreateFileOptions mWriteUnderStore;
private OpenFileOptions mReadNoCache;
private OpenFileOptions mReadCache;
@Rule
public LocalAlluxioClusterResource mLocalAlluxioClusterResource =
new LocalAlluxioClusterResource.Builder()
.setProperty(PropertyKey.USER_BLOCK_REMOTE_READ_BUFFER_SIZE_BYTES, "100")
.setProperty(PropertyKey.USER_UFS_BLOCK_READ_CONCURRENCY_MAX, 2).build();
@Rule
public ExpectedException mThrown = ExpectedException.none();
@Before
public final void before() throws Exception {
mFileSystem = mLocalAlluxioClusterResource.get().getClient();
mWriteUnderStore = CreateFileOptions.defaults().setWriteType(WriteType.THROUGH);
mReadCache = OpenFileOptions.defaults().setReadType(ReadType.CACHE_PROMOTE);
mReadNoCache = OpenFileOptions.defaults().setReadType(ReadType.NO_CACHE);
}
/**
* Tests single byte reads from the underfs.
*/
@Test
public void read() throws Exception {
String uniqPath = PathUtils.uniqPath();
for (int k = MIN_LEN; k <= MAX_LEN; k += DELTA) {
AlluxioURI uri = new AlluxioURI(uniqPath + "/file_" + k);
FileSystemTestUtils.createByteFile(mFileSystem, uri, mWriteUnderStore, k);
FileInStream is = mFileSystem.openFile(uri, mReadNoCache);
byte[] ret = new byte[k];
int value = is.read();
int cnt = 0;
while (value != -1) {
Assert.assertTrue(value >= 0);
Assert.assertTrue(value < 256);
ret[cnt++] = (byte) value;
value = is.read();
}
Assert.assertEquals(cnt, k);
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(k, ret));
is.close();
if (k == 0) {
Assert.assertEquals(100, mFileSystem.getStatus(uri).getInMemoryPercentage());
} else {
Assert.assertNotEquals(100, mFileSystem.getStatus(uri).getInMemoryPercentage());
}
is = mFileSystem.openFile(uri, mReadCache);
ret = new byte[k];
value = is.read();
cnt = 0;
while (value != -1) {
Assert.assertTrue(value >= 0);
Assert.assertTrue(value < 256);
ret[cnt++] = (byte) value;
value = is.read();
}
Assert.assertEquals(cnt, k);
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(k, ret));
is.close();
Assert.assertEquals(100, mFileSystem.getStatus(uri).getInMemoryPercentage());
is = mFileSystem.openFile(uri, mReadCache);
ret = new byte[k];
value = is.read();
cnt = 0;
while (value != -1) {
Assert.assertTrue(value >= 0);
Assert.assertTrue(value < 256);
ret[cnt++] = (byte) value;
value = is.read();
}
Assert.assertEquals(cnt, k);
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(k, ret));
is.close();
Assert.assertEquals(100, mFileSystem.getStatus(uri).getInMemoryPercentage());
}
}
/**
* Tests single byte reads from the underfs concurrently.
*/
@Test
public void concurrentUfsRead() throws Exception {
String uniqPath = PathUtils.uniqPath();
final AlluxioURI uri = new AlluxioURI(uniqPath + "/file_" + MAX_LEN);
FileSystemTestUtils.createByteFile(mFileSystem, uri, mWriteUnderStore, MAX_LEN);
ExecutorService executorService = Executors.newFixedThreadPool(100);
final AtomicInteger count = new AtomicInteger(0);
final Random random = new Random();
int expectedCount = 100;
for (int i = 0; i < expectedCount; i++) {
final int index = i;
executorService.submit(new Runnable() {
@Override
public void run() {
try {
// Add some randomness here to avoid opening too many connections too fast.
Thread.sleep(random.nextInt(100));
FileInStream is = mFileSystem.openFile(uri, mReadCache);
// Sleep here so that we can make sure the file is not cached too fast.
Thread.sleep(100);
byte[] ret = new byte[MAX_LEN];
int value = is.read();
int cnt = 0;
while (value != -1) {
Assert.assertTrue(value >= 0);
Assert.assertTrue(value < 256);
ret[cnt++] = (byte) value;
value = is.read();
}
is.close();
Assert.assertEquals(cnt, MAX_LEN);
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(MAX_LEN, ret));
while (mFileSystem.getStatus(uri).getInMemoryPercentage() < 100) {
Thread.sleep(1000);
}
Assert.assertEquals(100, mFileSystem.getStatus(uri).getInMemoryPercentage());
count.incrementAndGet();
} catch (Throwable e) {
LOG.error("Failed to read file {}.", index, e);
}
}
});
}
executorService.shutdown();
executorService.awaitTermination(Constants.MINUTE_MS * 5, TimeUnit.MILLISECONDS);
// There can be some errors due because SASL handsake failure or opening the connections too
// fast. If that happens, consider loosing the condition a bit.
Assert.assertTrue(expectedCount >= count.get() - 1);
}
/**
* Tests seeking through files only in the underfs.
*/
@Test
public void seek() throws Exception {
String uniqPath = PathUtils.uniqPath();
for (int k = MIN_LEN + DELTA; k <= MAX_LEN; k += DELTA) {
AlluxioURI uri = new AlluxioURI(uniqPath + "/file_" + k);
FileSystemTestUtils.createByteFile(mFileSystem, uri, mWriteUnderStore, k);
FileInStream is = mFileSystem.openFile(uri, mReadCache);
Assert.assertEquals(0, is.read());
is.seek(k / 3);
Assert.assertEquals(k / 3, is.read());
is.seek(k / 2);
Assert.assertEquals(k / 2, is.read());
is.seek(k / 4);
Assert.assertEquals(k / 4, is.read());
is.close();
}
}
/**
* Tests skipping through files only in the underfs.
*/
@Test
public void skip() throws Exception {
String uniqPath = PathUtils.uniqPath();
for (int k = MIN_LEN + DELTA; k <= MAX_LEN; k += DELTA) {
AlluxioURI uri = new AlluxioURI(uniqPath + "/file_" + k);
FileSystemTestUtils.createByteFile(mFileSystem, uri, mWriteUnderStore, k);
FileInStream is = mFileSystem.openFile(uri, mReadCache);
Assert.assertEquals(k / 2, is.skip(k / 2));
Assert.assertEquals(k / 2, is.read());
is.close();
Assert.assertEquals(100, mFileSystem.getStatus(uri).getInMemoryPercentage());
if (k >= 3) {
is = mFileSystem.openFile(uri, mReadCache);
int t = k / 3;
Assert.assertEquals(t, is.skip(t));
Assert.assertEquals(t, is.read());
Assert.assertEquals(t, is.skip(t));
Assert.assertEquals(2 * t + 1, is.read());
is.close();
Assert.assertTrue(mFileSystem.getStatus(uri).getInMemoryPercentage() == 100);
}
}
}
/**
* Tests that reading a file consisting of more than one block from the underfs works.
*/
@Test
public void readMultiBlockFile() throws Exception {
String uniqPath = PathUtils.uniqPath();
int blockSizeByte = 10;
int numBlocks = 10;
AlluxioURI uri = new AlluxioURI(uniqPath);
FileOutStream os = mFileSystem.createFile(uri, mWriteUnderStore);
for (int i = 0; i < numBlocks; i++) {
for (int j = 0; j < blockSizeByte; j++) {
os.write((byte) (i * blockSizeByte + j));
}
}
os.close();
FileInStream is = mFileSystem.openFile(uri, mReadCache);
for (int i = 0; i < blockSizeByte * numBlocks; i++) {
Assert.assertEquals((byte) i, is.read());
}
is.close();
Assert.assertTrue(mFileSystem.getStatus(uri).getInMemoryPercentage() == 100);
}
}