All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tachyon.worker.block.meta.StorageDir Maven / Gradle / Ivy

/*
 * Licensed to the University of California, Berkeley under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package tachyon.worker.block.meta;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;

import tachyon.Constants;
import tachyon.StorageDirId;
import tachyon.StorageLevelAlias;
import tachyon.exception.BlockAlreadyExistsException;
import tachyon.exception.BlockDoesNotExistException;
import tachyon.exception.ExceptionMessage;
import tachyon.exception.InvalidWorkerStateException;
import tachyon.exception.WorkerOutOfSpaceException;
import tachyon.util.io.FileUtils;
import tachyon.worker.block.BlockStoreLocation;

/**
 * Represents a directory in a storage tier. It has a fixed capacity allocated to it on
 * instantiation. It contains the set of blocks currently in the storage directory.
 * 

* This class does not guarantee thread safety. */ public final class StorageDir { private static final Logger LOG = LoggerFactory.getLogger(Constants.LOGGER_TYPE); private final long mCapacityBytes; /** A map from block ID to block meta data */ private Map mBlockIdToBlockMap; /** A map from block ID to temp block meta data */ private Map mBlockIdToTempBlockMap; /** A map from session ID to the set of temp blocks created by this session */ private Map> mSessionIdToTempBlockIdsMap; private AtomicLong mAvailableBytes; private AtomicLong mCommittedBytes; private String mDirPath; private int mDirIndex; private StorageTier mTier; private StorageDir(StorageTier tier, int dirIndex, long capacityBytes, String dirPath) { mTier = Preconditions.checkNotNull(tier); mDirIndex = dirIndex; mCapacityBytes = capacityBytes; mAvailableBytes = new AtomicLong(capacityBytes); mCommittedBytes = new AtomicLong(0); mDirPath = dirPath; mBlockIdToBlockMap = new HashMap(200); mBlockIdToTempBlockMap = new HashMap(200); mSessionIdToTempBlockIdsMap = new HashMap>(200); } /** * Factory method to create {@link StorageDir}. * * It will load meta data of existing committed blocks in the dirPath specified. Only files with * directory depth 1 under dirPath and whose file name can be parsed into {@code long} will be * considered as existing committed blocks, these files will be preserved, others files or * directories will be deleted. * * @param tier the {@link StorageTier} this dir belongs to * @param dirIndex the index of this dir in its tier * @param capacityBytes the initial capacity of this dir, can not be modified later * @param dirPath filesystem path of this dir for actual storage * @return the new created StorageDir * @throws BlockAlreadyExistsException when meta data of existing committed blocks already exists * @throws IOException if the storage directory cannot be created with the appropriate permissions * @throws WorkerOutOfSpaceException when meta data can not be added due to limited left space */ public static StorageDir newStorageDir(StorageTier tier, int dirIndex, long capacityBytes, String dirPath) throws BlockAlreadyExistsException, IOException, WorkerOutOfSpaceException { StorageDir dir = new StorageDir(tier, dirIndex, capacityBytes, dirPath); dir.initializeMeta(); return dir; } /** * Initializes meta data for existing blocks in this StorageDir. * * Only paths satisfying the contract defined in {@link BlockMetaBase#commitPath} are legal, * should be in format like {dir}/{blockId}. other paths will be deleted. * * @throws BlockAlreadyExistsException when meta data of existing committed blocks already exists * @throws IOException if the storage directory cannot be created with the appropriate permissions * @throws WorkerOutOfSpaceException when meta data can not be added due to limited left space */ private void initializeMeta() throws BlockAlreadyExistsException, IOException, WorkerOutOfSpaceException { // Create the storage directory path FileUtils.createStorageDirPath(mDirPath); File dir = new File(mDirPath); File[] paths = dir.listFiles(); if (paths == null) { return; } for (File path : paths) { if (!path.isFile()) { LOG.error("{} in StorageDir is not a file", path.getAbsolutePath()); try { // TODO(calvin): Resolve this conflict in class names. org.apache.commons.io.FileUtils.deleteDirectory(path); } catch (IOException ioe) { LOG.error("can not delete directory {}: {}", path.getAbsolutePath(), ioe); } } else { try { long blockId = Long.valueOf(path.getName()); addBlockMeta(new BlockMeta(blockId, path.length(), this)); } catch (NumberFormatException nfe) { LOG.error("filename of {} in StorageDir can not be parsed into long", path.getAbsolutePath()); if (path.delete()) { LOG.warn("file {} has been deleted", path.getAbsolutePath()); } else { LOG.error("can not delete file {}", path.getAbsolutePath()); } } } } } /** * Gets the total capacity of this StorageDir in bytes, which is a constant once this StorageDir * has been initialized. * * @return the total capacity of this StorageDir in bytes */ public long getCapacityBytes() { return mCapacityBytes; } /** * Gets the total available capacity of this StorageDir in bytes. This value equals the total * capacity of this StorageDir, minus the used bytes by committed blocks and temp blocks. * * @return available capacity in bytes */ public long getAvailableBytes() { return mAvailableBytes.get(); } /** * Gets the total size of committed blocks in this StorageDir in bytes. * * @return number of committed bytes */ public long getCommittedBytes() { return mCommittedBytes.get(); } public String getDirPath() { return mDirPath; } /** * Returns the StorageTier containing this StorageDir. * * @return StorageTier */ public StorageTier getParentTier() { return mTier; } /** * Returns the zero-based index of this dir in its parent StorageTier. * * @return index */ public int getDirIndex() { return mDirIndex; } // TODO(bin): Deprecate this method. public long getStorageDirId() { int level = mTier.getTierLevel(); int storageLevelAliasValue = mTier.getTierAlias(); return StorageDirId.getStorageDirId(level, storageLevelAliasValue, mDirIndex); } /** * Returns the list of block IDs in this dir. * * @return a list of block IDs */ public List getBlockIds() { return new ArrayList(mBlockIdToBlockMap.keySet()); } /** * Returns the list of blocks stored in this dir. * * @return a list of blocks */ public List getBlocks() { return new ArrayList(mBlockIdToBlockMap.values()); } /** * Checks if a block is in this storage dir. * * @param blockId the block ID * @return true if the block is in this storage dir, false otherwise */ public boolean hasBlockMeta(long blockId) { return mBlockIdToBlockMap.containsKey(blockId); } /** * Checks if a temp block is in this storage dir. * * @param blockId the block ID * @return true if the block is in this storage dir, false otherwise */ public boolean hasTempBlockMeta(long blockId) { return mBlockIdToTempBlockMap.containsKey(blockId); } /** * Gets the BlockMeta from this storage dir by its block ID. * * @param blockId the block ID * @return BlockMeta of the given block or null * @throws BlockDoesNotExistException if no block is found */ public BlockMeta getBlockMeta(long blockId) throws BlockDoesNotExistException { BlockMeta blockMeta = mBlockIdToBlockMap.get(blockId); if (blockMeta == null) { throw new BlockDoesNotExistException(ExceptionMessage.BLOCK_META_NOT_FOUND, blockId); } return blockMeta; } /** * Gets the BlockMeta from this storage dir by its block ID. * * @param blockId the block ID * @return TempBlockMeta of the given block or null * @throws BlockDoesNotExistException if no temp block is found */ public TempBlockMeta getTempBlockMeta(long blockId) throws BlockDoesNotExistException { TempBlockMeta tempBlockMeta = mBlockIdToTempBlockMap.get(blockId); if (tempBlockMeta == null) { throw new BlockDoesNotExistException(ExceptionMessage.TEMP_BLOCK_META_NOT_FOUND, blockId); } return tempBlockMeta; } /** * Adds the metadata of a new block into this storage dir. * * @param blockMeta the meta data of the block * @throws BlockAlreadyExistsException if blockId already exists * @throws WorkerOutOfSpaceException when not enough space to hold block */ public void addBlockMeta(BlockMeta blockMeta) throws WorkerOutOfSpaceException, BlockAlreadyExistsException { Preconditions.checkNotNull(blockMeta); long blockId = blockMeta.getBlockId(); long blockSize = blockMeta.getBlockSize(); if (getAvailableBytes() < blockSize) { StorageLevelAlias alias = StorageLevelAlias.getAlias(blockMeta.getBlockLocation().tierAlias()); throw new WorkerOutOfSpaceException(ExceptionMessage.NO_SPACE_FOR_BLOCK_META, blockId, blockSize, getAvailableBytes(), alias); } if (hasBlockMeta(blockId)) { StorageLevelAlias alias = StorageLevelAlias.getAlias(blockMeta.getBlockLocation().tierAlias()); throw new BlockAlreadyExistsException(ExceptionMessage.ADD_EXISTING_BLOCK, blockId, alias); } mBlockIdToBlockMap.put(blockId, blockMeta); reserveSpace(blockSize, true); } /** * Adds the metadata of a new block into this storage dir. * * @param tempBlockMeta the meta data of a temp block to add * @throws BlockAlreadyExistsException if blockId already exists * @throws WorkerOutOfSpaceException when not enough space to hold block */ public void addTempBlockMeta(TempBlockMeta tempBlockMeta) throws WorkerOutOfSpaceException, BlockAlreadyExistsException { Preconditions.checkNotNull(tempBlockMeta); long sessionId = tempBlockMeta.getSessionId(); long blockId = tempBlockMeta.getBlockId(); long blockSize = tempBlockMeta.getBlockSize(); if (getAvailableBytes() < blockSize) { StorageLevelAlias alias = StorageLevelAlias.getAlias(tempBlockMeta.getBlockLocation().tierAlias()); throw new WorkerOutOfSpaceException(ExceptionMessage.NO_SPACE_FOR_BLOCK_META, blockId, blockSize, getAvailableBytes(), alias); } if (hasTempBlockMeta(blockId)) { StorageLevelAlias alias = StorageLevelAlias.getAlias(tempBlockMeta.getBlockLocation().tierAlias()); throw new BlockAlreadyExistsException(ExceptionMessage.ADD_EXISTING_BLOCK, blockId, alias); } mBlockIdToTempBlockMap.put(blockId, tempBlockMeta); Set sessionTempBlocks = mSessionIdToTempBlockIdsMap.get(sessionId); if (sessionTempBlocks == null) { mSessionIdToTempBlockIdsMap.put(sessionId, Sets.newHashSet(blockId)); } else { sessionTempBlocks.add(blockId); } reserveSpace(blockSize, false); } /** * Removes a block from this storage dir. * * @param blockMeta the meta data of the block * @throws BlockDoesNotExistException if no block is found */ public void removeBlockMeta(BlockMeta blockMeta) throws BlockDoesNotExistException { Preconditions.checkNotNull(blockMeta); long blockId = blockMeta.getBlockId(); BlockMeta deletedBlockMeta = mBlockIdToBlockMap.remove(blockId); if (deletedBlockMeta == null) { throw new BlockDoesNotExistException(ExceptionMessage.BLOCK_META_NOT_FOUND, blockId); } reclaimSpace(blockMeta.getBlockSize(), true); } /** * Removes a temp block from this storage dir. * * @param tempBlockMeta the meta data of the temp block to remove * @throws BlockDoesNotExistException if no temp block is found */ public void removeTempBlockMeta(TempBlockMeta tempBlockMeta) throws BlockDoesNotExistException { Preconditions.checkNotNull(tempBlockMeta); final long blockId = tempBlockMeta.getBlockId(); final long sessionId = tempBlockMeta.getSessionId(); TempBlockMeta deletedTempBlockMeta = mBlockIdToTempBlockMap.remove(blockId); if (deletedTempBlockMeta == null) { throw new BlockDoesNotExistException(ExceptionMessage.BLOCK_META_NOT_FOUND, blockId); } Set sessionBlocks = mSessionIdToTempBlockIdsMap.get(sessionId); if (sessionBlocks == null || !sessionBlocks.contains(blockId)) { StorageLevelAlias alias = StorageLevelAlias.getAlias(this.getDirIndex()); throw new BlockDoesNotExistException(ExceptionMessage.BLOCK_NOT_FOUND_FOR_SESSION, blockId, alias, sessionId); } Preconditions.checkState(sessionBlocks.remove(blockId)); if (sessionBlocks.isEmpty()) { mSessionIdToTempBlockIdsMap.remove(sessionId); } reclaimSpace(tempBlockMeta.getBlockSize(), false); } /** * Changes the size of a temp block. * * @param tempBlockMeta the meta data of the temp block to resize * @param newSize the new size after change in bytes * @throws InvalidWorkerStateException when newSize is smaller than oldSize */ public void resizeTempBlockMeta(TempBlockMeta tempBlockMeta, long newSize) throws InvalidWorkerStateException { long oldSize = tempBlockMeta.getBlockSize(); if (newSize > oldSize) { reserveSpace(newSize - oldSize, false); tempBlockMeta.setBlockSize(newSize); } else if (newSize < oldSize) { throw new InvalidWorkerStateException("Shrinking block, not supported!"); } } /** * Cleans up the temp block meta data for each block id passed in. * * @param sessionId the ID of the client associated with the temporary blocks * @param tempBlockIds the list of temporary blocks to clean up, non temporary blocks or * nonexistent blocks will be ignored */ public void cleanupSessionTempBlocks(long sessionId, List tempBlockIds) { Set sessionTempBlocks = mSessionIdToTempBlockIdsMap.get(sessionId); // The session's temporary blocks have already been removed. if (sessionTempBlocks == null) { return; } for (Long tempBlockId : tempBlockIds) { if (!mBlockIdToTempBlockMap.containsKey(tempBlockId)) { // This temp block does not exist in this dir, this is expected for some blocks since the // input list is across all dirs continue; } sessionTempBlocks.remove(tempBlockId); TempBlockMeta tempBlockMeta = mBlockIdToTempBlockMap.remove(tempBlockId); if (tempBlockMeta != null) { reclaimSpace(tempBlockMeta.getBlockSize(), false); } else { LOG.error("Cannot find blockId {} when cleanup sessionId {}", tempBlockId, sessionId); } } if (sessionTempBlocks.isEmpty()) { mSessionIdToTempBlockIdsMap.remove(sessionId); } else { // This may happen if the client comes back during clean up and creates more blocks or some // temporary blocks failed to be deleted LOG.warn("Blocks still owned by session " + sessionId + " after cleanup."); } } /** * Gets the temporary blocks associated with a session in this StorageDir, an empty list is * returned if the session has no temporary blocks in this StorageDir. * * @param sessionId the ID of the session * @return A list of temporary blocks the session is associated with in this StorageDir */ public List getSessionTempBlocks(long sessionId) { Set sessionTempBlockIds = mSessionIdToTempBlockIdsMap.get(sessionId); if (sessionTempBlockIds == null || sessionTempBlockIds.isEmpty()) { return Collections.emptyList(); } List sessionTempBlocks = new ArrayList(); for (long blockId : sessionTempBlockIds) { sessionTempBlocks.add(mBlockIdToTempBlockMap.get(blockId)); } return sessionTempBlocks; } /** * @return the block store location of this directory */ public BlockStoreLocation toBlockStoreLocation() { return new BlockStoreLocation(mTier.getTierAlias(), mTier.getTierLevel(), mDirIndex); } private void reclaimSpace(long size, boolean committed) { Preconditions.checkState(mCapacityBytes >= mAvailableBytes.get() + size, "Available bytes should always be less than total capacity bytes"); mAvailableBytes.addAndGet(size); if (committed) { mCommittedBytes.addAndGet(-size); } } private void reserveSpace(long size, boolean committed) { Preconditions.checkState(size <= mAvailableBytes.get(), "Available bytes should always be non-negative"); mAvailableBytes.addAndGet(-size); if (committed) { mCommittedBytes.addAndGet(size); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy