// Selects a dir with enough space (including space evictable) from all tiers private StorageDirView selectEvictableDirFromAnyTier( BlockMetadataManagerView view, long availableBytes) { for (StorageTierView tierView : view.getTierViews()) { for (StorageDirView dirView : tierView.getDirViews()) { if (canEvictBlocksFromDir(dirView, availableBytes)) { return dirView; } } } return null; }
private StorageDirView selectAvailableDir( BlockMeta block, List<StorageTierView> candidateTiers, Map<StorageDirView, Long> pendingBytesInDir) { for (StorageTierView candidateTier : candidateTiers) { for (StorageDirView candidateDir : candidateTier.getDirViews()) { long pendingBytes = 0; if (pendingBytesInDir.containsKey(candidateDir)) { pendingBytes = pendingBytesInDir.get(candidateDir); } if (candidateDir.getAvailableBytes() - pendingBytes >= block.getBlockSize()) { return candidateDir; } } } return null; }
@Override public EvictionPlan freeSpaceWithView( long availableBytes, BlockStoreLocation location, BlockMetadataManagerView view) { Preconditions.checkNotNull(location); Preconditions.checkNotNull(view); // 1. Select a StorageDirView that has enough capacity for required bytes. StorageDirView selectedDirView = null; if (location.equals(BlockStoreLocation.anyTier())) { selectedDirView = selectEvictableDirFromAnyTier(view, availableBytes); } else { String tierAlias = location.tierAlias(); StorageTierView tierView = view.getTierView(tierAlias); if (location.equals(BlockStoreLocation.anyDirInTier(tierAlias))) { selectedDirView = selectEvictableDirFromTier(tierView, availableBytes); } else { int dirIndex = location.dir(); StorageDirView dir = tierView.getDirView(dirIndex); if (canEvictBlocksFromDir(dir, availableBytes)) { selectedDirView = dir; } } } if (selectedDirView == null) { LOG.error( "Failed to freeSpace: No StorageDirView has enough capacity of {} bytes", availableBytes); return null; } // 2. Check if the selected StorageDirView already has enough space. List<BlockTransferInfo> toTransfer = new ArrayList<>(); List<Pair<Long, BlockStoreLocation>> toEvict = new ArrayList<>(); long bytesAvailableInDir = selectedDirView.getAvailableBytes(); if (bytesAvailableInDir >= availableBytes) { // No need to evict anything, return an eviction plan with empty instructions. return new EvictionPlan(toTransfer, toEvict); } // 3. Collect victim blocks from the selected StorageDirView. They could either be evicted or // moved. List<BlockMeta> victimBlocks = new ArrayList<>(); for (BlockMeta block : selectedDirView.getEvictableBlocks()) { victimBlocks.add(block); bytesAvailableInDir += block.getBlockSize(); if (bytesAvailableInDir >= availableBytes) { break; } } // 4. Make best effort to transfer victim blocks to lower tiers rather than evict them. Map<StorageDirView, Long> pendingBytesInDir = new HashMap<>(); for (BlockMeta block : victimBlocks) { // TODO(qifan): Should avoid calling getParentDir. String fromTierAlias = block.getParentDir().getParentTier().getTierAlias(); List<StorageTierView> candidateTiers = view.getTierViewsBelow(fromTierAlias); StorageDirView dstDir = selectAvailableDir(block, candidateTiers, pendingBytesInDir); if (dstDir == null) { // Not possible to transfer toEvict.add(new Pair<>(block.getBlockId(), block.getBlockLocation())); } else { StorageTierView dstTier = dstDir.getParentTierView(); toTransfer.add( new BlockTransferInfo( block.getBlockId(), block.getBlockLocation(), new BlockStoreLocation(dstTier.getTierViewAlias(), dstDir.getDirViewIndex()))); if (pendingBytesInDir.containsKey(dstDir)) { pendingBytesInDir.put(dstDir, pendingBytesInDir.get(dstDir) + block.getBlockSize()); } else { pendingBytesInDir.put(dstDir, block.getBlockSize()); } } } return new EvictionPlan(toTransfer, toEvict); }