/** * Aborts a temp block. * * @param sessionId the id of session * @param blockId the id of 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 void abortBlockInternal(long sessionId, long blockId) throws BlockDoesNotExistException, BlockAlreadyExistsException, InvalidWorkerStateException, IOException { long lockId = mLockManager.lockBlock(sessionId, blockId, BlockLockType.WRITE); try { String path; TempBlockMeta tempBlockMeta; mMetadataReadLock.lock(); try { checkTempBlockOwnedBySession(sessionId, blockId); tempBlockMeta = mMetaManager.getTempBlockMeta(blockId); path = tempBlockMeta.getPath(); } finally { mMetadataReadLock.unlock(); } // Heavy IO is guarded by block lock but not metadata lock. This may throw IOException. Files.delete(Paths.get(path)); mMetadataWriteLock.lock(); try { mMetaManager.abortTempBlockMeta(tempBlockMeta); } catch (BlockDoesNotExistException e) { throw Throwables.propagate(e); // We shall never reach here } finally { mMetadataWriteLock.unlock(); } } finally { mLockManager.unlockBlock(lockId); } }
/** * Checks if block id is a temporary block and owned by session id. This method must be enclosed * by {@link #mMetadataLock}. * * @param sessionId the id of session * @param blockId the id of 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 */ private void checkTempBlockOwnedBySession(long sessionId, long blockId) throws BlockDoesNotExistException, BlockAlreadyExistsException, InvalidWorkerStateException { if (mMetaManager.hasBlockMeta(blockId)) { throw new BlockAlreadyExistsException(ExceptionMessage.TEMP_BLOCK_ID_COMMITTED, blockId); } TempBlockMeta tempBlockMeta = mMetaManager.getTempBlockMeta(blockId); long ownerSessionId = tempBlockMeta.getSessionId(); if (ownerSessionId != sessionId) { throw new InvalidWorkerStateException( ExceptionMessage.BLOCK_ID_FOR_DIFFERENT_SESSION, blockId, ownerSessionId, sessionId); } }
@Override public BlockWriter getBlockWriter(long sessionId, long blockId) throws BlockDoesNotExistException, IOException { // NOTE: a temp block is supposed to only be visible by its own writer, unnecessary to acquire // block lock here since no sharing // TODO(bin): Handle the case where multiple writers compete for the same block. mMetadataReadLock.lock(); try { TempBlockMeta tempBlockMeta = mMetaManager.getTempBlockMeta(blockId); return new LocalFileBlockWriter(tempBlockMeta.getPath()); } finally { mMetadataReadLock.unlock(); } }
/** * 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); } }
/** * Increases the temp block size only if this temp block's parent dir has enough available space. * * @param blockId block Id * @param additionalBytes additional bytes to request for this block * @return a pair of boolean and {@link BlockStoreLocation}. The boolean indicates if the * operation succeeds and the {@link BlockStoreLocation} denotes where to free more space if * it fails. * @throws BlockDoesNotExistException if this block is not found */ private Pair<Boolean, BlockStoreLocation> requestSpaceInternal(long blockId, long additionalBytes) throws BlockDoesNotExistException { // NOTE: a temp block is supposed to be visible for its own writer, unnecessary to acquire // block lock here since no sharing mMetadataWriteLock.lock(); try { TempBlockMeta tempBlockMeta = mMetaManager.getTempBlockMeta(blockId); if (tempBlockMeta.getParentDir().getAvailableBytes() < additionalBytes) { return new Pair<Boolean, BlockStoreLocation>(false, tempBlockMeta.getBlockLocation()); } // Increase the size of this temp block try { mMetaManager.resizeTempBlockMeta( tempBlockMeta, tempBlockMeta.getBlockSize() + additionalBytes); } catch (InvalidWorkerStateException e) { throw Throwables.propagate(e); // we shall never reach here } return new Pair<Boolean, BlockStoreLocation>(true, null); } finally { mMetadataWriteLock.unlock(); } }
@Override public void cleanupSession(long sessionId) { // Release all locks the session is holding. mLockManager.cleanupSession(sessionId); // Collect a list of temp blocks the given session owns and abort all of them with best effort List<TempBlockMeta> tempBlocksToRemove; mMetadataReadLock.lock(); try { tempBlocksToRemove = mMetaManager.getSessionTempBlocks(sessionId); } finally { mMetadataReadLock.unlock(); } for (TempBlockMeta tempBlockMeta : tempBlocksToRemove) { try { abortBlockInternal(sessionId, tempBlockMeta.getBlockId()); } catch (Exception e) { LOG.error( "Failed to cleanup tempBlock {} due to {}", tempBlockMeta.getBlockId(), e.getMessage()); } } }
/** * 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); } }