/*
* 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.master.file;
import alluxio.AlluxioURI;
import alluxio.AuthenticatedUserRule;
import alluxio.BaseIntegrationTest;
import alluxio.Configuration;
import alluxio.ConfigurationTestUtils;
import alluxio.Constants;
import alluxio.LocalAlluxioClusterResource;
import alluxio.PropertyKey;
import alluxio.client.file.FileSystem;
import alluxio.client.file.options.GetStatusOptions;
import alluxio.exception.FileDoesNotExistException;
import alluxio.master.file.meta.UfsAbsentPathCache;
import alluxio.underfs.UnderFileSystemFactoryRegistry;
import alluxio.underfs.sleepfs.SleepingUnderFileSystemFactory;
import alluxio.underfs.sleepfs.SleepingUnderFileSystemOptions;
import alluxio.util.CommonUtils;
import alluxio.util.WaitForOptions;
import alluxio.wire.LoadMetadataType;
import com.google.common.base.Function;
import com.google.common.io.Files;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.powermock.reflect.Whitebox;
import java.io.File;
import java.io.FileWriter;
/**
* Tests the loading of metadata and the available options.
*/
public class LoadMetadataTest extends BaseIntegrationTest {
private static final long SLEEP_MS = Constants.SECOND_MS / 2;
private static SleepingUnderFileSystemFactory sSleepingUfsFactory;
private FileSystem mFileSystem;
private String mLocalUfsPath = Files.createTempDir().getAbsolutePath();
@Rule
public AuthenticatedUserRule mAuthenticatedUser = new AuthenticatedUserRule("test");
@Rule
public LocalAlluxioClusterResource mLocalAlluxioClusterResource =
new LocalAlluxioClusterResource.Builder().build();
@BeforeClass
public static void beforeClass() throws Exception {
SleepingUnderFileSystemOptions options = new SleepingUnderFileSystemOptions();
options.setExistsMs(SLEEP_MS);
sSleepingUfsFactory = new SleepingUnderFileSystemFactory(options);
UnderFileSystemFactoryRegistry.register(sSleepingUfsFactory);
}
@AfterClass
public static void afterClass() throws Exception {
UnderFileSystemFactoryRegistry.unregister(sSleepingUfsFactory);
}
@Before
public void before() throws Exception {
mFileSystem = FileSystem.Factory.get();
mFileSystem.mount(new AlluxioURI("/mnt/"), new AlluxioURI("sleep://" + mLocalUfsPath));
new File(mLocalUfsPath + "/dir1/dirA/").mkdirs();
FileWriter fileWriter = new FileWriter(mLocalUfsPath + "/dir1/dirA/file");
fileWriter.write("test");
fileWriter.close();
}
@After
public void after() throws Exception {
ConfigurationTestUtils.resetConfiguration();
}
@Test
public void loadMetadataAlways() throws Exception {
GetStatusOptions options =
GetStatusOptions.defaults().setLoadMetadataType(LoadMetadataType.Always);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, true);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, true);
checkGetStatus("/mnt/dir1/dirA/fileDNE2", options, false, true);
checkGetStatus("/mnt/dir1/dirA/file", options, true, true);
checkGetStatus("/mnt/dir1/dirA/dirDNE/", options, false, true);
}
@Test
public void loadMetadataNever() throws Exception {
GetStatusOptions options =
GetStatusOptions.defaults().setLoadMetadataType(LoadMetadataType.Never);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, false);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, false);
checkGetStatus("/mnt/dir1/dirA/fileDNE2", options, false, false);
checkGetStatus("/mnt/dir1/dirA/file", options, false, false);
checkGetStatus("/mnt/dir1/dirA/dirDNE/", options, false, false);
checkGetStatus("/mnt/dir1/dirA/dirDNE/fileDNE3", options, false, false);
}
@Test
public void loadMetadataOnce() throws Exception {
GetStatusOptions options =
GetStatusOptions.defaults().setLoadMetadataType(LoadMetadataType.Once);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, true);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, false);
checkGetStatus("/mnt/dir1/dirA/fileDNE2", options, false, true);
checkGetStatus("/mnt/dir1/dirA/file", options, true, true);
checkGetStatus("/mnt/dir1/dirA/dirDNE/", options, false, true);
checkGetStatus("/mnt/dir1/dirA/dirDNE/dir1", options, false, false);
checkGetStatus("/mnt/dir1/dirA/dirDNE/dir1/file1", options, false, false);
checkGetStatus("/mnt/dir1/dirA/dirDNE/dir2", options, false, false);
}
@Test
public void loadMetadataOnceAfterUfsCreate() throws Exception {
GetStatusOptions options =
GetStatusOptions.defaults().setLoadMetadataType(LoadMetadataType.Once);
// dirB does not exist yet
checkGetStatus("/mnt/dir1/dirA/dirB/file", options, false, true);
// create dirB in UFS
Assert.assertTrue(new File(mLocalUfsPath + "/dir1/dirA/dirB").mkdirs());
// 'ONCE' still should not load the metadata
checkGetStatus("/mnt/dir1/dirA/dirB/file", options, false, false);
// load metadata for dirB with 'ALWAYS'
checkGetStatus("/mnt/dir1/dirA/dirB",
GetStatusOptions.defaults().setLoadMetadataType(LoadMetadataType.Always), true, true);
// 'ONCE' should now load the metadata
checkGetStatus("/mnt/dir1/dirA/dirB/file", options, false, true);
}
@Test
public void loadMetadataOnceAfterUfsDelete() throws Exception {
GetStatusOptions options =
GetStatusOptions.defaults().setLoadMetadataType(LoadMetadataType.Once);
// create dirB in UFS
Assert.assertTrue(new File(mLocalUfsPath + "/dir1/dirA/dirB").mkdirs());
checkGetStatus("/mnt/dir1/dirA/dirB/file", options, false, true);
checkGetStatus("/mnt/dir1/dirA/dirB/file", options, false, false);
// delete dirB in UFS
Assert.assertTrue(new File(mLocalUfsPath + "/dir1/dirA/dirB").delete());
// 'ONCE' should not be affected if UFS is changed
checkGetStatus("/mnt/dir1/dirA/dirB/file", options, false, false);
// force load metadata with 'ALWAYS'
checkGetStatus("/mnt/dir1/dirA/dirB",
GetStatusOptions.defaults().setLoadMetadataType(LoadMetadataType.Always), false, true);
// 'ONCE' should still not load metadata, since the ancestor is absent
checkGetStatus("/mnt/dir1/dirA/dirB/file", options, false, false);
}
@Test
public void loadAlwaysConfiguration() throws Exception {
Configuration.set(PropertyKey.USER_FILE_METADATA_LOAD_TYPE, LoadMetadataType.Always.toString());
GetStatusOptions options = GetStatusOptions.defaults();
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, true);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, true);
}
@Test
public void loadOnceConfiguration() throws Exception {
Configuration.set(PropertyKey.USER_FILE_METADATA_LOAD_TYPE, LoadMetadataType.Once.toString());
GetStatusOptions options = GetStatusOptions.defaults();
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, true);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, false);
}
@Test
public void loadNeverConfiguration() throws Exception {
Configuration.set(PropertyKey.USER_FILE_METADATA_LOAD_TYPE, LoadMetadataType.Never.toString());
GetStatusOptions options = GetStatusOptions.defaults();
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, false);
checkGetStatus("/mnt/dir1/dirA/fileDNE1", options, false, false);
}
/**
* Checks the get status call with the specified parameters and expectations.
*
* @param path the path to get the status for
* @param options the options for the get status call
* @param expectExists if true, the path should exist
* @param expectLoadFromUfs if true, the get status call will load from ufs
*/
private void checkGetStatus(final String path, GetStatusOptions options, boolean expectExists,
boolean expectLoadFromUfs)
throws Exception {
long startMs = CommonUtils.getCurrentMs();
try {
mFileSystem.getStatus(new AlluxioURI(path), options);
if (!expectExists) {
Assert.fail("Path is not expected to exist: " + path);
}
} catch (FileDoesNotExistException e) {
if (expectExists) {
throw e;
}
}
long durationMs = CommonUtils.getCurrentMs() - startMs;
if (expectLoadFromUfs) {
Assert.assertTrue("Expected to be slow (ufs load). actual duration (ms): " + durationMs,
durationMs >= SLEEP_MS);
} else {
Assert.assertTrue("Expected to be fast (no ufs load). actual duration (ms): " + durationMs,
durationMs < SLEEP_MS / 2);
}
if (!expectExists && expectLoadFromUfs) {
// The metadata is loaded from Ufs, but the path does not exist, so it will be added to the
// absent cache. Wait until the path shows up in the absent cache.
final UfsAbsentPathCache cache = Whitebox.getInternalState(
mLocalAlluxioClusterResource.get().getLocalAlluxioMaster().getMasterProcess()
.getMaster(FileSystemMaster.class), "mUfsAbsentPathCache");
CommonUtils.waitFor("path (" + path + ") to be added to absent cache",
new Function<Void, Boolean>() {
@Override
public Boolean apply(Void input) {
if (cache.isAbsent(new AlluxioURI(path))) {
return true;
}
return false;
}
}, WaitForOptions.defaults().setTimeout(60000));
}
if (expectExists && expectLoadFromUfs) {
// The metadata is loaded from Ufs, and the path exists, so it will be removed from the
// absent cache. Wait until the path is removed.
final UfsAbsentPathCache cache = Whitebox.getInternalState(
mLocalAlluxioClusterResource.get().getLocalAlluxioMaster().getMasterProcess()
.getMaster(FileSystemMaster.class), "mUfsAbsentPathCache");
CommonUtils.waitFor("path (" + path + ") to be removed from absent cache",
new Function<Void, Boolean>() {
@Override
public Boolean apply(Void input) {
if (cache.isAbsent(new AlluxioURI(path))) {
return false;
}
return true;
}
}, WaitForOptions.defaults().setTimeout(60000));
}
}
}