/* * 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; import alluxio.Configuration; import alluxio.PropertyKey; import alluxio.util.io.BufferUtils; import alluxio.util.io.FileUtils; import alluxio.util.io.PathUtils; import alluxio.worker.block.evictor.Evictor; import alluxio.worker.block.io.BlockWriter; import alluxio.worker.block.io.LocalFileBlockWriter; import alluxio.worker.block.meta.StorageDir; import alluxio.worker.block.meta.TempBlockMeta; import com.google.common.base.Preconditions; import com.google.common.primitives.Ints; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import java.util.Collections; /** * Utility methods for setting and testing {@link TieredBlockStore}. */ public final class TieredBlockStoreTestUtils { /** * Default configurations of a TieredBlockStore for use in {@link #defaultMetadataManager}. They * represent a block store with a MEM tier and a SSD tier, there are two directories with capacity * 2000 bytes and 3000 bytes separately in the MEM tier and three directories with capacity 10000, * 20000, 30000 bytes separately in the SSD tier. */ public static final int[] TIER_ORDINAL = {0, 1}; public static final String[] TIER_ALIAS = {"MEM", "SSD"}; public static final String[][] TIER_PATH = {{"/mem/0", "/mem/1"}, {"/ssd/0", "/ssd/1", "/ssd/2"}}; public static final long[][] TIER_CAPACITY_BYTES = {{2000, 3000}, {10000, 20000, 30000}}; public static final String WORKER_DATA_FOLDER = "/alluxioworker/"; /** * Sets up a {@link Configuration} for a {@link TieredBlockStore} with several tiers configured by * the parameters. For simplicity, you can use {@link #setupDefaultConf(String)} which * calls this method with default values. * * @param baseDir the directory path as prefix for all the paths of directories in the tiered * storage; when specified, the directory needs to exist before calling this method * @param tierOrdinal like {@link #TIER_ORDINAL}, length must be > 0 * @param tierAlias like {@link #TIER_ALIAS}, each corresponds to an element in tierLevel * @param tierPath like {@link #TIER_PATH}, each list represents directories of the tier with the * same list index in tierAlias * @param tierCapacity like {@link #TIER_CAPACITY_BYTES}, should be in the same dimension with * tierPath, each element is the capacity of the corresponding dir in tierPath * @param workerDataFolder when specified it sets up the alluxio.worker.data.folder property */ public static void setupConfWithMultiTier(String baseDir, int[] tierOrdinal, String[] tierAlias, String[][] tierPath, long[][] tierCapacity, String workerDataFolder) throws Exception { // make sure dimensions are legal Preconditions.checkNotNull(tierOrdinal); Preconditions.checkNotNull(tierAlias); Preconditions.checkNotNull(tierPath); Preconditions.checkNotNull(tierCapacity); Preconditions.checkArgument(tierOrdinal.length > 0, "length of tierLevel should be > 0"); Preconditions.checkArgument(tierOrdinal.length == tierAlias.length, "tierAlias and tierLevel should have the same length"); Preconditions.checkArgument(tierOrdinal.length == tierPath.length, "tierPath and tierLevel should have the same length"); Preconditions.checkArgument(tierOrdinal.length == tierCapacity.length, "tierCapacity and tierLevel should have the same length"); int nTier = tierOrdinal.length; tierPath = createDirHierarchy(baseDir, tierPath); if (workerDataFolder != null) { Configuration.set(PropertyKey.WORKER_DATA_FOLDER, workerDataFolder); } Configuration.set(PropertyKey.WORKER_TIERED_STORE_LEVELS, String.valueOf(nTier)); // sets up each tier in turn for (int i = 0; i < nTier; i++) { setupConfTier(tierOrdinal[i], tierAlias[i], tierPath[i], tierCapacity[i]); } } /** * Sets up a {@link Configuration} for a {@link TieredBlockStore} with only *one tier* configured * by the parameters. For simplicity, you can use {@link #setupDefaultConf(String)} which * sets up the tierBlockStore with default values. * * This method modifies the configuration, so be sure to reset it when done. * * @param baseDir the directory path as prefix for all the paths of directories in the tiered * storage; when specified, the directory needs to exist before calling this method * @param tierOrdinal ordinal of this tier * @param tierAlias alias of this tier * @param tierPath path of this tier; when `baseDir` is specified, the actual test tierPath turns * into `baseDir/tierPath` * @param tierCapacity capacity of this tier * @param workerDataFolder when specified it sets up the alluxio.worker.data.folder property */ public static void setupConfWithSingleTier(String baseDir, int tierOrdinal, String tierAlias, String[] tierPath, long[] tierCapacity, String workerDataFolder) throws Exception { if (baseDir != null) { tierPath = createDirHierarchy(baseDir, tierPath); } if (workerDataFolder != null) { Configuration.set(PropertyKey.WORKER_DATA_FOLDER, workerDataFolder); } Configuration.set(PropertyKey.WORKER_TIERED_STORE_LEVELS, String.valueOf(1)); setupConfTier(tierOrdinal, tierAlias, tierPath, tierCapacity); } /** * Sets up a specific tier's {@link Configuration} for a {@link TieredBlockStore}. * * @param tierAlias alias of the tier * @param tierPath absolute path of the tier * @param tierCapacity capacity of the tier */ private static void setupConfTier(int ordinal, String tierAlias, String[] tierPath, long[] tierCapacity) { Preconditions.checkNotNull(tierPath); Preconditions.checkNotNull(tierCapacity); Preconditions.checkArgument(tierPath.length == tierCapacity.length, "tierPath and tierCapacity should have the same length"); Configuration .set(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_ALIAS.format(ordinal), tierAlias); String tierPathString = StringUtils.join(tierPath, ","); Configuration .set(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_DIRS_PATH.format(ordinal), tierPathString); String tierCapacityString = StringUtils.join(ArrayUtils.toObject(tierCapacity), ","); Configuration .set(PropertyKey.Template.WORKER_TIERED_STORE_LEVEL_DIRS_QUOTA.format(ordinal), tierCapacityString); } /** * Joins baseDir with all the paths listed in the array and then create the new generated path. * * @param baseDir the directory path as prefix for all the paths in the array 'dirs' * @param dirs 2-D array of directory paths * @return new joined and created paths array */ private static String[][] createDirHierarchy(String baseDir, final String[][] dirs) throws Exception { if (baseDir == null) { return dirs; } String[][] newDirs = new String[dirs.length][]; for (int i = 0; i < dirs.length; i++) { newDirs[i] = createDirHierarchy(baseDir, dirs[i]); } return newDirs; } /** * Joins baseDir with all the paths listed in the array and then create the new generated path. * * @param baseDir the directory path as prefix for all the paths in the array 'dirs' * @param dirs 1-D array of directory paths * @return new joined and created paths array */ private static String[] createDirHierarchy(String baseDir, final String[] dirs) throws Exception { if (baseDir == null) { return dirs; } String[] newDirs = new String[dirs.length]; for (int i = 0; i < dirs.length; i++) { newDirs[i] = PathUtils.concatPath(baseDir, dirs[i]); FileUtils.createDir(newDirs[i]); } return newDirs; } /** * Creates a BlockMetadataManager with {@link #setupDefaultConf(String)}. * * @param baseDir the directory path as prefix for paths of directories in the tiered storage; the * directory needs to exist before calling this method * @return the created metadata manager */ public static BlockMetadataManager defaultMetadataManager(String baseDir) throws Exception { setupDefaultConf(baseDir); return BlockMetadataManager.createBlockMetadataManager(); } /** * Creates a {@link BlockMetadataManagerView} with {@link #setupDefaultConf(String)}. * * @param baseDir the directory path as prefix for paths of directories in the tiered storage; the * directory needs to exist before calling this method * @return the created metadata manager view */ public static BlockMetadataManagerView defaultMetadataManagerView(String baseDir) throws Exception { BlockMetadataManager metaManager = TieredBlockStoreTestUtils.defaultMetadataManager(baseDir); return new BlockMetadataManagerView(metaManager, Collections.<Long>emptySet(), Collections.<Long>emptySet()); } /** * Sets up a {@link Configuration} with default values of {@link #TIER_ORDINAL}, * {@link #TIER_ALIAS}, {@link #TIER_PATH} with the baseDir as path prefix, * {@link #TIER_CAPACITY_BYTES}. * * @param baseDir the directory path as prefix for paths of directories in the tiered storage; the * directory needs to exist before calling this method */ public static void setupDefaultConf(String baseDir) throws Exception { setupConfWithMultiTier(baseDir, TIER_ORDINAL, TIER_ALIAS, TIER_PATH, TIER_CAPACITY_BYTES, WORKER_DATA_FOLDER); } /** * Caches bytes into {@link StorageDir}. * * @param sessionId session who caches the data * @param blockId id of the cached block * @param bytes size of the block in bytes * @param dir the {@link StorageDir} the block resides in * @param meta the metadata manager to update meta of the block * @param evictor the evictor to be informed of the new block */ public static void cache(long sessionId, long blockId, long bytes, StorageDir dir, BlockMetadataManager meta, Evictor evictor) throws Exception { TempBlockMeta tempBlockMeta = createTempBlock(sessionId, blockId, bytes, dir); // commit block FileUtils.move(tempBlockMeta.getPath(), tempBlockMeta.getCommitPath()); meta.commitTempBlockMeta(tempBlockMeta); // update evictor if (evictor instanceof BlockStoreEventListener) { ((BlockStoreEventListener) evictor) .onCommitBlock(sessionId, blockId, dir.toBlockStoreLocation()); } } /** * Caches bytes into {@link BlockStore} at specific location. * * @param sessionId session who caches the data * @param blockId id of the cached block * @param bytes size of the block in bytes * @param blockStore block store that the block is written into * @param location the location where the block resides */ public static void cache(long sessionId, long blockId, long bytes, BlockStore blockStore, BlockStoreLocation location) throws Exception { TempBlockMeta tempBlockMeta = blockStore.createBlock(sessionId, blockId, location, bytes); // write data FileUtils.createFile(tempBlockMeta.getPath()); BlockWriter writer = new LocalFileBlockWriter(tempBlockMeta.getPath()); writer.append(BufferUtils.getIncreasingByteBuffer(Ints.checkedCast(bytes))); writer.close(); // commit block blockStore.commitBlock(sessionId, blockId); } /** * Caches bytes into {@link StorageDir}. * * @param sessionId session who caches the data * @param blockId id of the cached block * @param bytes size of the block in bytes * @param tierLevel tier level of the {@link StorageDir} the block resides in * @param dirIndex index of directory in the tierLevel the block resides in * @param meta the metadata manager to update meta of the block * @param evictor the evictor to be informed of the new block */ public static void cache(long sessionId, long blockId, long bytes, int tierLevel, int dirIndex, BlockMetadataManager meta, Evictor evictor) throws Exception { StorageDir dir = meta.getTiers().get(tierLevel).getDir(dirIndex); cache(sessionId, blockId, bytes, dir, meta, evictor); } /** * Makes a temp block of a given size in {@link StorageDir}. * * @param sessionId session who caches the data * @param blockId id of the cached block * @param bytes size of the block in bytes * @param dir the {@link StorageDir} the block resides in * @return the temp block meta */ public static TempBlockMeta createTempBlock(long sessionId, long blockId, long bytes, StorageDir dir) throws Exception { // prepare temp block TempBlockMeta tempBlockMeta = new TempBlockMeta(sessionId, blockId, bytes, dir); dir.addTempBlockMeta(tempBlockMeta); // write data FileUtils.createFile(tempBlockMeta.getPath()); BlockWriter writer = new LocalFileBlockWriter(tempBlockMeta.getPath()); writer.append(BufferUtils.getIncreasingByteBuffer(Ints.checkedCast(bytes))); writer.close(); return tempBlockMeta; } /** * Gets the total capacity of all tiers in bytes. * * @return total capacity of all tiers in bytes */ public static long getDefaultTotalCapacityBytes() { long totalCapacity = 0; for (long[] tierCapacityBytes : TIER_CAPACITY_BYTES) { for (long tierCapacityByte : tierCapacityBytes) { totalCapacity += tierCapacityByte; } } return totalCapacity; } /** * Gets the number of testing directories of all tiers. * * @return number of testing directories of all tiers */ public static long getDefaultDirNum() { int dirNum = 0; for (String[] tierPath : TIER_PATH) { dirNum += tierPath.length; } return dirNum; } }