/** * Commits a temp block. * * @param sessionId the id of session * @param blockId the id of block * @return destination location to move the block * @throws BlockDoesNotExistException if block id can not be found in temporary blocks * @throws BlockAlreadyExistsException if block id already exists in committed blocks * @throws InvalidWorkerStateException if block id is not owned by session id * @throws IOException if I/O errors occur when deleting the block file */ private BlockStoreLocation commitBlockInternal(long sessionId, long blockId) throws BlockAlreadyExistsException, InvalidWorkerStateException, BlockDoesNotExistException, IOException { long lockId = mLockManager.lockBlock(sessionId, blockId, BlockLockType.WRITE); try { // When committing TempBlockMeta, the final BlockMeta calculates the block size according to // the actual file size of this TempBlockMeta. Therefore, commitTempBlockMeta must happen // after moving actual block file to its committed path. BlockStoreLocation loc; String srcPath; String dstPath; TempBlockMeta tempBlockMeta; mMetadataReadLock.lock(); try { checkTempBlockOwnedBySession(sessionId, blockId); tempBlockMeta = mMetaManager.getTempBlockMeta(blockId); srcPath = tempBlockMeta.getPath(); dstPath = tempBlockMeta.getCommitPath(); loc = tempBlockMeta.getBlockLocation(); } finally { mMetadataReadLock.unlock(); } // Heavy IO is guarded by block lock but not metadata lock. This may throw IOException. FileUtils.move(srcPath, dstPath); mMetadataWriteLock.lock(); try { mMetaManager.commitTempBlockMeta(tempBlockMeta); } 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) { throw Throwables.propagate(e); // we shall never reach here } finally { mMetadataWriteLock.unlock(); } return loc; } finally { mLockManager.unlockBlock(lockId); } }
/** * 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); } }