/* * 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.worker.block.meta; import alluxio.AlluxioURI; import alluxio.Constants; import alluxio.LocalAlluxioClusterResource; import alluxio.PropertyKey; import alluxio.BaseIntegrationTest; import alluxio.client.WriteType; import alluxio.client.file.FileOutStream; import alluxio.client.file.FileSystem; import alluxio.client.file.URIStatus; import alluxio.client.file.options.CreateFileOptions; import alluxio.client.file.options.DeleteOptions; import alluxio.client.file.policy.LocalFirstPolicy; import alluxio.heartbeat.HeartbeatContext; import alluxio.heartbeat.HeartbeatScheduler; import alluxio.heartbeat.ManuallyScheduleHeartbeat; import alluxio.master.block.BlockMaster; import alluxio.util.CommonUtils; import alluxio.util.WaitForOptions; import alluxio.util.io.BufferUtils; import com.google.common.base.Function; import com.google.common.io.Files; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import java.util.List; import java.util.Map; public class SpecificTierWriteIntegrationTest extends BaseIntegrationTest { private static final int CAPACITY_BYTES = Constants.KB; private static final int FILE_SIZE = CAPACITY_BYTES; private static final String BLOCK_SIZE_BYTES = "1KB"; @Rule public LocalAlluxioClusterResource mLocalAlluxioClusterResource = new LocalAlluxioClusterResource.Builder() .setProperty(PropertyKey.USER_BLOCK_SIZE_BYTES_DEFAULT, BLOCK_SIZE_BYTES) .setProperty(PropertyKey.USER_FILE_BUFFER_BYTES, BLOCK_SIZE_BYTES) .setProperty(PropertyKey.WORKER_MEMORY_SIZE, CAPACITY_BYTES) .setProperty(PropertyKey.WORKER_TIERED_STORE_LEVELS, "3") .setProperty(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_ALIAS.format(1), "SSD") .setProperty(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_ALIAS.format(2), "HDD") .setProperty(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_DIRS_PATH.format(0), Files.createTempDir().getAbsolutePath()) .setProperty(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_DIRS_PATH.format(1), Files.createTempDir().getAbsolutePath()) .setProperty(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_DIRS_QUOTA.format(1), String.valueOf(CAPACITY_BYTES)) .setProperty(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_DIRS_PATH.format(2), Files.createTempDir().getAbsolutePath()) .setProperty(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_DIRS_QUOTA.format(2), String.valueOf(CAPACITY_BYTES)).build(); @ClassRule public static ManuallyScheduleHeartbeat sManuallySchedule = new ManuallyScheduleHeartbeat(HeartbeatContext.WORKER_BLOCK_SYNC); private FileSystem mFileSystem = null; private BlockMaster mBlockMaster; @Before public final void before() throws Exception { mFileSystem = mLocalAlluxioClusterResource.get().getClient(); mBlockMaster = mLocalAlluxioClusterResource.get().getLocalAlluxioMaster().getMasterProcess() .getMaster(BlockMaster.class); } /** * Writes a file into a specified tier, and then verifies the expected bytes on each tier. * * @param writeTier the specific tier to write the file to * @param memBytes the expected number of bytes used in the MEM tier * @param ssdBytes the expected number of bytes used in the SSD tier * @param hddBytes the expected number of bytes used in the HDD tier */ private void writeFileAndCheckUsage(int writeTier, long memBytes, long ssdBytes, long hddBytes) throws Exception { FileOutStream os = mFileSystem.createFile( new AlluxioURI("/tier-" + writeTier + "_" + CommonUtils.randomAlphaNumString(5)), CreateFileOptions.defaults().setWriteTier(writeTier).setWriteType(WriteType.MUST_CACHE) .setLocationPolicy(new LocalFirstPolicy())); os.write(BufferUtils.getIncreasingByteArray(FILE_SIZE)); os.close(); HeartbeatScheduler.execute(HeartbeatContext.WORKER_BLOCK_SYNC); long totalBytes = memBytes + ssdBytes + hddBytes; Assert.assertEquals("Total bytes used", totalBytes, mLocalAlluxioClusterResource.get().getLocalAlluxioMaster().getMasterProcess() .getMaster(BlockMaster.class).getUsedBytes()); Map<String, Long> bytesOnTiers = mLocalAlluxioClusterResource.get().getLocalAlluxioMaster().getMasterProcess() .getMaster(BlockMaster.class).getUsedBytesOnTiers(); Assert.assertEquals("MEM tier usage", memBytes, bytesOnTiers.get("MEM").longValue()); Assert.assertEquals("SSD tier usage", ssdBytes, bytesOnTiers.get("SSD").longValue()); Assert.assertEquals("HDD tier usage", hddBytes, bytesOnTiers.get("HDD").longValue()); } private void deleteAllFiles() throws Exception { List<URIStatus> files = mFileSystem.listStatus(new AlluxioURI("/")); for (URIStatus file : files) { mFileSystem .delete(new AlluxioURI(file.getPath()), DeleteOptions.defaults().setRecursive(true)); } // Trigger a worker heartbeat to delete the blocks. HeartbeatScheduler.execute(HeartbeatContext.WORKER_BLOCK_SYNC); CommonUtils.waitFor("files to be deleted", new Function<Void, Boolean>() { @Override public Boolean apply(Void input) { try { // Trigger a worker heartbeat to report removed blocks. HeartbeatScheduler.execute(HeartbeatContext.WORKER_BLOCK_SYNC); } catch (InterruptedException e) { // ignore the exception } return mBlockMaster.getUsedBytes() == 0; } }, WaitForOptions.defaults().setTimeout(10 * Constants.SECOND_MS)); } @Test public void topTierWrite() throws Exception { writeFileAndCheckUsage(0, FILE_SIZE, 0, 0); deleteAllFiles(); writeFileAndCheckUsage(-3, FILE_SIZE, 0, 0); deleteAllFiles(); writeFileAndCheckUsage(-4, FILE_SIZE, 0, 0); } @Test public void midTierWrite() throws Exception { writeFileAndCheckUsage(1, 0, FILE_SIZE, 0); deleteAllFiles(); writeFileAndCheckUsage(-2, 0, FILE_SIZE, 0); } @Test public void bottomTierWrite() throws Exception { writeFileAndCheckUsage(2, 0, 0, FILE_SIZE); deleteAllFiles(); writeFileAndCheckUsage(3, 0, 0, FILE_SIZE); deleteAllFiles(); writeFileAndCheckUsage(-1, 0, 0, FILE_SIZE); } @Test public void allTierWrite() throws Exception { writeFileAndCheckUsage(0, FILE_SIZE, 0, 0); writeFileAndCheckUsage(1, FILE_SIZE, FILE_SIZE, 0); writeFileAndCheckUsage(2, FILE_SIZE, FILE_SIZE, FILE_SIZE); deleteAllFiles(); writeFileAndCheckUsage(-1, 0, 0, FILE_SIZE); writeFileAndCheckUsage(-2, 0, FILE_SIZE, FILE_SIZE); writeFileAndCheckUsage(-3, FILE_SIZE, FILE_SIZE, FILE_SIZE); } @Test public void topTierWriteWithEviction() throws Exception { writeFileAndCheckUsage(0, FILE_SIZE, 0, 0); writeFileAndCheckUsage(0, FILE_SIZE, FILE_SIZE, 0); writeFileAndCheckUsage(0, FILE_SIZE, FILE_SIZE, FILE_SIZE); writeFileAndCheckUsage(0, FILE_SIZE, FILE_SIZE, FILE_SIZE); } @Test public void midTierWriteWithEviction() throws Exception { writeFileAndCheckUsage(1, 0, FILE_SIZE, 0); writeFileAndCheckUsage(1, 0, FILE_SIZE, FILE_SIZE); writeFileAndCheckUsage(1, 0, FILE_SIZE, FILE_SIZE); } @Test public void bottomTierWriteWithEviction() throws Exception { writeFileAndCheckUsage(2, 0, 0, FILE_SIZE); writeFileAndCheckUsage(2, 0, 0, FILE_SIZE); } @Test public void allTierWriteWithEviction() throws Exception { writeFileAndCheckUsage(0, FILE_SIZE, 0, 0); writeFileAndCheckUsage(1, FILE_SIZE, FILE_SIZE, 0); writeFileAndCheckUsage(2, FILE_SIZE, FILE_SIZE, FILE_SIZE); writeFileAndCheckUsage(-1, FILE_SIZE, FILE_SIZE, FILE_SIZE); writeFileAndCheckUsage(-2, FILE_SIZE, FILE_SIZE, FILE_SIZE); writeFileAndCheckUsage(-3, FILE_SIZE, FILE_SIZE, FILE_SIZE); } }