// Tests a report is correctly generated after moving blocks
  @Test
  public void generateReportMoveTest() {
    Long block1 = 1L;
    Long block2 = 2L;
    Long block3 = 3L;
    moveBlock(block1, MEM_LOC);
    moveBlock(block2, SSD_LOC);
    moveBlock(block3, HDD_LOC);
    BlockHeartbeatReport report = mReporter.generateReport();
    Map<Long, List<Long>> addedBlocks = report.getAddedBlocks();

    // Block1 moved to memory
    List<Long> addedBlocksMem = addedBlocks.get(MEM_LOC.getStorageDirId());
    Assert.assertEquals(1, addedBlocksMem.size());
    Assert.assertEquals(block1, addedBlocksMem.get(0));

    // Block2 moved to ssd
    List<Long> addedBlocksSsd = addedBlocks.get(SSD_LOC.getStorageDirId());
    Assert.assertEquals(1, addedBlocksSsd.size());
    Assert.assertEquals(block2, addedBlocksSsd.get(0));

    // Block3 moved to hdd
    List<Long> addedBlocksHdd = addedBlocks.get(HDD_LOC.getStorageDirId());
    Assert.assertEquals(1, addedBlocksHdd.size());
    Assert.assertEquals(block3, addedBlocksHdd.get(0));
  }
Ejemplo n.º 2
0
  @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));
  }
Ejemplo n.º 3
0
 /**
  * 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());
 }
Ejemplo n.º 4
0
  /**
   * Commits a block to Tachyon managed space. The block must be temporary. The block is persisted
   * after {@link BlockStore#commitBlock(long, long)}. The block will not be accessible until {@link
   * WorkerBlockMasterClient#commitBlock} succeeds.
   *
   * @param sessionId The id of the client
   * @param blockId The id of the block to commit
   * @throws BlockAlreadyExistsException if blockId already exists in committed blocks
   * @throws BlockDoesNotExistException if the temporary block cannot be found
   * @throws InvalidWorkerStateException if blockId does not belong to sessionId
   * @throws IOException if the block cannot be moved from temporary path to committed path
   * @throws WorkerOutOfSpaceException if there is no more space left to hold the block
   */
  public void commitBlock(long sessionId, long blockId)
      throws BlockAlreadyExistsException, BlockDoesNotExistException, InvalidWorkerStateException,
          IOException, WorkerOutOfSpaceException {
    mBlockStore.commitBlock(sessionId, blockId);

    // TODO(calvin): Reconsider how to do this without heavy locking.
    // Block successfully committed, update master with new block metadata
    Long lockId = mBlockStore.lockBlock(sessionId, blockId);
    try {
      BlockMeta meta = mBlockStore.getBlockMeta(sessionId, blockId, lockId);
      BlockStoreLocation loc = meta.getBlockLocation();
      Long length = meta.getBlockSize();
      BlockStoreMeta storeMeta = mBlockStore.getBlockStoreMeta();
      Long bytesUsedOnTier = storeMeta.getUsedBytesOnTiers().get(loc.tierAlias());
      mBlockMasterClient.commitBlock(
          WorkerIdRegistry.getWorkerId(), bytesUsedOnTier, loc.tierAlias(), blockId, length);
    } catch (IOException ioe) {
      throw new IOException("Failed to commit block to master.", ioe);
    } finally {
      mBlockStore.unlockBlock(lockId);
    }
  }
  // Tests a report is correctly generated after moving and then removing a block
  @Test
  public void generateReportMoveThenRemoveTest() {
    Long block1 = 1L;
    moveBlock(block1, MEM_LOC);
    removeBlock(block1);

    // The block should not be in the added blocks list
    BlockHeartbeatReport report = mReporter.generateReport();
    Assert.assertEquals(null, report.getAddedBlocks().get(MEM_LOC.getStorageDirId()));

    // The block should be in the removed blocks list
    List<Long> removedBlocks = report.getRemovedBlocks();
    Assert.assertEquals(1, removedBlocks.size());
    Assert.assertTrue(removedBlocks.contains(block1));
  }
Ejemplo n.º 6
0
  /**
   * 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;
  }
Ejemplo n.º 7
0
  /**
   * 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();
  }
Ejemplo n.º 8
0
 /**
  * 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);
 }
Ejemplo n.º 9
0
 /**
  * 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);
 }
Ejemplo n.º 10
0
 /**
  * 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());
 }
Ejemplo n.º 11
0
  /**
   * Moves a block to new location only if allocator finds available space in newLocation. This
   * method will not trigger any eviction. Returns {@link MoveBlockResult}.
   *
   * @param sessionId session Id
   * @param blockId block Id
   * @param oldLocation the source location of the block
   * @param newLocation new location to move this block
   * @return the resulting information about the move operation
   * @throws BlockDoesNotExistException if block is not found
   * @throws BlockAlreadyExistsException if a block with same Id already exists in new location
   * @throws InvalidWorkerStateException if the block to move is a temp block
   * @throws IOException if I/O errors occur when moving block file
   */
  private MoveBlockResult moveBlockInternal(
      long sessionId, long blockId, BlockStoreLocation oldLocation, BlockStoreLocation newLocation)
      throws BlockDoesNotExistException, BlockAlreadyExistsException, InvalidWorkerStateException,
          IOException {
    long lockId = mLockManager.lockBlock(sessionId, blockId, BlockLockType.WRITE);
    try {
      long blockSize;
      String srcFilePath;
      String dstFilePath;
      BlockMeta srcBlockMeta;
      BlockStoreLocation srcLocation;
      BlockStoreLocation dstLocation;

      mMetadataReadLock.lock();
      try {
        if (mMetaManager.hasTempBlockMeta(blockId)) {
          throw new InvalidWorkerStateException(ExceptionMessage.MOVE_UNCOMMITTED_BLOCK, blockId);
        }
        srcBlockMeta = mMetaManager.getBlockMeta(blockId);
        srcLocation = srcBlockMeta.getBlockLocation();
        srcFilePath = srcBlockMeta.getPath();
        blockSize = srcBlockMeta.getBlockSize();
      } finally {
        mMetadataReadLock.unlock();
      }

      if (!srcLocation.belongsTo(oldLocation)) {
        throw new BlockDoesNotExistException(
            ExceptionMessage.BLOCK_NOT_FOUND_AT_LOCATION, blockId, oldLocation);
      }
      TempBlockMeta dstTempBlock =
          createBlockMetaInternal(sessionId, blockId, newLocation, blockSize, false);
      if (dstTempBlock == null) {
        return new MoveBlockResult(false, blockSize, null, null);
      }

      // When `newLocation` is some specific location, the `newLocation` and the `dstLocation` are
      // just the same; while for `newLocation` with a wildcard significance, the `dstLocation`
      // is a specific one with specific tier and dir which belongs to newLocation.
      dstLocation = dstTempBlock.getBlockLocation();

      // When the dstLocation belongs to srcLocation, simply abort the tempBlockMeta just created
      // internally from the newLocation and return success with specific block location.
      if (dstLocation.belongsTo(srcLocation)) {
        mMetaManager.abortTempBlockMeta(dstTempBlock);
        return new MoveBlockResult(true, blockSize, srcLocation, dstLocation);
      }
      dstFilePath = dstTempBlock.getCommitPath();

      // Heavy IO is guarded by block lock but not metadata lock. This may throw IOException.
      FileUtils.move(srcFilePath, dstFilePath);

      mMetadataWriteLock.lock();
      try {
        // If this metadata update fails, we panic for now.
        // TODO(bin): Implement rollback scheme to recover from IO failures.
        mMetaManager.moveBlockMeta(srcBlockMeta, dstTempBlock);
      } catch (BlockAlreadyExistsException e) {
        throw Throwables.propagate(e); // we shall never reach here
      } catch (BlockDoesNotExistException e) {
        throw Throwables.propagate(e); // we shall never reach here
      } catch (WorkerOutOfSpaceException e) {
        // Only possible if session id gets cleaned between createBlockMetaInternal and
        // moveBlockMeta.
        throw Throwables.propagate(e);
      } finally {
        mMetadataWriteLock.unlock();
      }

      return new MoveBlockResult(true, blockSize, srcLocation, dstLocation);
    } finally {
      mLockManager.unlockBlock(lockId);
    }
  }
Ejemplo n.º 12
0
 @Override
 public void removeBlock(long sessionId, long blockId)
     throws InvalidWorkerStateException, BlockDoesNotExistException, IOException {
   removeBlock(sessionId, blockId, BlockStoreLocation.anyTier());
 }
Ejemplo n.º 13
0
 @Override
 public void moveBlock(long sessionId, long blockId, BlockStoreLocation newLocation)
     throws BlockDoesNotExistException, BlockAlreadyExistsException, InvalidWorkerStateException,
         WorkerOutOfSpaceException, IOException {
   moveBlock(sessionId, blockId, BlockStoreLocation.anyTier(), newLocation);
 }