@Test public void reserveTest() throws Exception { // Reserve on top tier long blockId = 100; BlockStoreLocation tier0 = BlockStoreLocation.anyDirInTier(StorageLevelAlias.MEM.getValue()); for (int i = 0; i < 3; i++) { TieredBlockStoreTestUtils.cache(SESSION_ID, blockId++, BLOCK_SIZE, mBlockStore, tier0); } CommonUtils.sleepMs( WorkerContext.getConf().getLong(Constants.WORKER_SPACE_RESERVER_INTERVAL_MS)); BlockStoreMeta storeMeta = mBlockStore.getBlockStoreMeta(); Assert.assertEquals(3 * BLOCK_SIZE, storeMeta.getUsedBytes()); List<Long> usedBytesOnTiers = storeMeta.getUsedBytesOnTiers(); Assert.assertEquals( 2 * BLOCK_SIZE, (long) usedBytesOnTiers.get(StorageLevelAlias.MEM.getValue() - 1)); Assert.assertEquals( BLOCK_SIZE, (long) usedBytesOnTiers.get(StorageLevelAlias.HDD.getValue() - 1)); // Reserve on under tier for (int i = 0; i < 7; i++) { TieredBlockStoreTestUtils.cache(SESSION_ID, blockId++, BLOCK_SIZE, mBlockStore, tier0); } CommonUtils.sleepMs( WorkerContext.getConf().getLong(Constants.WORKER_SPACE_RESERVER_INTERVAL_MS)); storeMeta = mBlockStore.getBlockStoreMeta(); Assert.assertEquals(9 * BLOCK_SIZE, storeMeta.getUsedBytes()); usedBytesOnTiers = storeMeta.getUsedBytesOnTiers(); Assert.assertEquals( 2 * BLOCK_SIZE, (long) usedBytesOnTiers.get(StorageLevelAlias.MEM.getValue() - 1)); Assert.assertEquals( 7 * BLOCK_SIZE, (long) usedBytesOnTiers.get(StorageLevelAlias.HDD.getValue() - 1)); }
/** * Gets the StorageDir given its location in the store. * * @param location Location of the dir * @return the StorageDir object * @throws IllegalArgumentException if location is not a specific dir or the location is invalid */ public synchronized StorageDir getDir(BlockStoreLocation location) { if (location.equals(BlockStoreLocation.anyTier()) || location.equals(BlockStoreLocation.anyDirInTier(location.tierAlias()))) { throw new IllegalArgumentException( "Failed to get block path: " + location + " is not a specific dir."); } return getTier(location.tierAlias()).getDir(location.dir()); }
/** * Moves the metadata of an existing block to another location or throws IOExceptions. * * @param blockMeta the meta data of the block to move * @param newLocation new location of the block * @return the new block metadata if success, absent otherwise * @throws IllegalArgumentException when the newLocation is not in the tiered storage * @throws NotFoundException when the block to move is not found * @throws AlreadyExistsException when the block to move already exists in the destination * @throws OutOfSpaceException when destination have no extra space to hold the block to move */ public synchronized BlockMeta moveBlockMeta(BlockMeta blockMeta, BlockStoreLocation newLocation) throws NotFoundException, AlreadyExistsException, OutOfSpaceException { // If existing location belongs to the target location, simply return the current block meta. BlockStoreLocation oldLocation = blockMeta.getBlockLocation(); if (oldLocation.belongTo(newLocation)) { LOG.info("moveBlockMeta: moving {} to {} is a noop", oldLocation, newLocation); return blockMeta; } long blockSize = blockMeta.getBlockSize(); int newTierAlias = newLocation.tierAlias(); StorageTier newTier = getTier(newTierAlias); StorageDir newDir = null; if (newLocation.equals(BlockStoreLocation.anyDirInTier(newTierAlias))) { for (StorageDir dir : newTier.getStorageDirs()) { if (dir.getAvailableBytes() >= blockSize) { newDir = dir; break; } } } else { StorageDir dir = newTier.getDir(newLocation.dir()); if (dir.getAvailableBytes() >= blockSize) { newDir = dir; } } if (newDir == null) { throw new OutOfSpaceException( "Failed to move BlockMeta: newLocation " + newLocation + " does not have enough space for " + blockSize + " bytes"); } StorageDir oldDir = blockMeta.getParentDir(); oldDir.removeBlockMeta(blockMeta); BlockMeta newBlockMeta = new BlockMeta(blockMeta.getBlockId(), blockSize, newDir); newDir.addBlockMeta(newBlockMeta); return newBlockMeta; }
/** * Gets the amount of available space of given location in bytes. Master queries the total number * of bytes available on each tier of the worker, and Evictor/Allocator often cares about the * bytes at a {@link StorageDir}. * * @param location location the check available bytes * @return available bytes * @throws IllegalArgumentException when location does not belong to tiered storage */ public synchronized long getAvailableBytes(BlockStoreLocation location) { long spaceAvailable = 0; if (location.equals(BlockStoreLocation.anyTier())) { for (StorageTier tier : mTiers) { spaceAvailable += tier.getAvailableBytes(); } return spaceAvailable; } int tierAlias = location.tierAlias(); StorageTier tier = getTier(tierAlias); // TODO: This should probably be max of the capacity bytes in the dirs? if (location.equals(BlockStoreLocation.anyDirInTier(tierAlias))) { return tier.getAvailableBytes(); } int dirIndex = location.dir(); StorageDir dir = tier.getDir(dirIndex); return dir.getAvailableBytes(); }
/** * Moves a block from its current location to a target location, currently only tier level moves * are supported * * @param sessionId The id of the client * @param blockId The id of the block to move * @param tierAlias The alias of the tier to move the block to * @throws IllegalArgumentException if tierAlias is out of range of tiered storage * @throws BlockDoesNotExistException if blockId cannot be found * @throws BlockAlreadyExistsException if blockId already exists in committed blocks of the * newLocation * @throws InvalidWorkerStateException if blockId has not been committed * @throws WorkerOutOfSpaceException if newLocation does not have enough extra space to hold the * block * @throws IOException if block cannot be moved from current location to newLocation */ public void moveBlock(long sessionId, long blockId, String tierAlias) throws BlockDoesNotExistException, BlockAlreadyExistsException, InvalidWorkerStateException, WorkerOutOfSpaceException, IOException { BlockStoreLocation dst = BlockStoreLocation.anyDirInTier(tierAlias); mBlockStore.moveBlock(sessionId, blockId, dst); }
/** * Frees space to make a specific amount of bytes available in the tier. * * @param sessionId the session ID * @param availableBytes the amount of free space in bytes * @param tierAlias the alias of the tier to free space * @throws WorkerOutOfSpaceException if there is not enough space * @throws BlockDoesNotExistException if blocks can not be found * @throws IOException if blocks fail to be moved or deleted on file system * @throws BlockAlreadyExistsException if blocks to move already exists in destination location * @throws InvalidWorkerStateException if blocks to move/evict is uncommitted */ public void freeSpace(long sessionId, long availableBytes, String tierAlias) throws WorkerOutOfSpaceException, BlockDoesNotExistException, IOException, BlockAlreadyExistsException, InvalidWorkerStateException { BlockStoreLocation location = BlockStoreLocation.anyDirInTier(tierAlias); mBlockStore.freeSpace(sessionId, availableBytes, location); }
/** * Creates a block. This method is only called from a data server. * * <p>Call {@link #getTempBlockWriterRemote(long, long)} to get a writer for writing to the block. * * @param sessionId The id of the client * @param blockId The id of the block to be created * @param tierAlias The alias of the tier to place the new block in * @param initialBytes The initial amount of bytes to be allocated * @throws IllegalArgumentException if location does not belong to tiered storage * @throws BlockAlreadyExistsException if blockId already exists, either temporary or committed, * or block in eviction plan already exists * @throws WorkerOutOfSpaceException if this Store has no more space than the initialBlockSize * @throws IOException if blocks in eviction plan fail to be moved or deleted */ public void createBlockRemote(long sessionId, long blockId, String tierAlias, long initialBytes) throws BlockAlreadyExistsException, WorkerOutOfSpaceException, IOException { BlockStoreLocation loc = BlockStoreLocation.anyDirInTier(tierAlias); TempBlockMeta createdBlock = mBlockStore.createBlockMeta(sessionId, blockId, loc, initialBytes); FileUtils.createBlockPath(createdBlock.getPath()); }