/*
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
* (the "License"). You may not use this work except in compliance with the License, which is
* available at www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied, as more fully set forth in the License.
*
* See the NOTICE file distributed with this work for information regarding copyright ownership.
*/
package alluxio.worker.block;
import alluxio.exception.BlockDoesNotExistException;
import alluxio.exception.ExceptionMessage;
import alluxio.master.block.BlockId;
import alluxio.worker.block.meta.BlockMeta;
import alluxio.worker.block.meta.StorageDirView;
import alluxio.worker.block.meta.StorageTier;
import alluxio.worker.block.meta.StorageTierView;
import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
/**
* This class exposes a narrower view of {@link BlockMetadataManager} to Evictors and Allocators,
* filtering out un-evictable blocks and un-allocatable space internally, so that evictors and
* allocators can be developed with much simpler logic, without worrying about various constraints,
* e.g. pinned files, locked blocks, etc.
*
* TODO(cc): Filter un-allocatable space.
*/
@NotThreadSafe
public class BlockMetadataManagerView {
/** The {@link BlockMetadataManager} this view is derived from. */
private final BlockMetadataManager mMetadataManager;
/**
* A list of {@link StorageTierView}, derived from {@link StorageTier}s from the
* {@link BlockMetadataManager}.
*/
private List<StorageTierView> mTierViews = new ArrayList<>();
/** A list of pinned inodes. */
private final Set<Long> mPinnedInodes = new HashSet<>();
/** Indices of locks that are being used. */
private final Set<Long> mInUseBlocks = new HashSet<>();
/** A map from tier alias to {@link StorageTierView}. */
private Map<String, StorageTierView> mAliasToTierViews = new HashMap<>();
/**
* Creates a new instance of {@link BlockMetadataManagerView}. Now we always create a new view
* before freespace.
*
* @param manager which the view should be constructed from
* @param pinnedInodes a set of pinned inodes
* @param lockedBlocks a set of locked blocks
*/
// TODO(qifan): Incrementally update the view.
public BlockMetadataManagerView(BlockMetadataManager manager, Set<Long> pinnedInodes,
Set<Long> lockedBlocks) {
mMetadataManager = Preconditions.checkNotNull(manager);
mPinnedInodes.addAll(Preconditions.checkNotNull(pinnedInodes));
Preconditions.checkNotNull(lockedBlocks);
mInUseBlocks.addAll(lockedBlocks);
// iteratively create all StorageTierViews and StorageDirViews
for (StorageTier tier : manager.getTiers()) {
StorageTierView tierView = new StorageTierView(tier, this);
mTierViews.add(tierView);
mAliasToTierViews.put(tier.getTierAlias(), tierView);
}
}
/**
* Clears all marks of blocks to move in/out in all dir views.
*/
public void clearBlockMarks() {
for (StorageTierView tierView : mTierViews) {
for (StorageDirView dirView : tierView.getDirViews()) {
dirView.clearBlockMarks();
}
}
}
/**
* Tests if the block is pinned.
*
* @param blockId to be tested
* @return boolean, true if block is pinned
*/
public boolean isBlockPinned(long blockId) {
return mPinnedInodes.contains(
BlockId.createBlockId(BlockId.getContainerId(blockId), BlockId.getMaxSequenceNumber()));
}
/**
* Tests if the block is locked.
*
* @param blockId to be tested
* @return boolean, true if block is locked
*/
public boolean isBlockLocked(long blockId) {
return mInUseBlocks.contains(blockId);
}
/**
* Tests if the block is evictable.
*
* @param blockId to be tested
* @return boolean, true if the block can be evicted
*/
public boolean isBlockEvictable(long blockId) {
return (!isBlockPinned(blockId) && !isBlockLocked(blockId) && !isBlockMarked(blockId));
}
/**
* Tests if the block is marked to move out of its current dir in this view.
*
* @param blockId the id of the block
* @return boolean, true if the block is marked to move out
*/
public boolean isBlockMarked(long blockId) {
for (StorageTierView tierView : mTierViews) {
for (StorageDirView dirView : tierView.getDirViews()) {
if (dirView.isMarkedToMoveOut(blockId)) {
return true;
}
}
}
return false;
}
/**
* Provides {@link StorageTierView} given tierAlias. Throws an {@link IllegalArgumentException} if
* the tierAlias is not found.
*
* @param tierAlias the alias of this tierView
* @return the {@link StorageTierView} object associated with the alias
*/
public StorageTierView getTierView(String tierAlias) {
StorageTierView tierView = mAliasToTierViews.get(tierAlias);
if (tierView == null) {
throw new IllegalArgumentException(
ExceptionMessage.TIER_VIEW_ALIAS_NOT_FOUND.getMessage(tierAlias));
} else {
return tierView;
}
}
/**
* Gets all tierViews under this managerView.
*
* @return the list of {@link StorageTierView}s
*/
public List<StorageTierView> getTierViews() {
return Collections.unmodifiableList(mTierViews);
}
/**
* Gets all tierViews before certain tierView. Throws an {@link IllegalArgumentException} if the
* tierAlias is not found.
*
* @param tierAlias the alias of a tierView
* @return the list of {@link StorageTierView}
*/
public List<StorageTierView> getTierViewsBelow(String tierAlias) {
int ordinal = getTierView(tierAlias).getTierViewOrdinal();
return mTierViews.subList(ordinal + 1, mTierViews.size());
}
/**
* Gets the next storage tier view.
*
* @param tierView the storage tier view
* @return the next storage tier view, null if this is the last tier view
*/
public StorageTierView getNextTier(StorageTierView tierView) {
int nextOrdinal = tierView.getTierViewOrdinal() + 1;
if (nextOrdinal < mTierViews.size()) {
return mTierViews.get(nextOrdinal);
}
return null;
}
/**
* Gets available bytes given certain location
* {@link BlockMetadataManager#getAvailableBytes(BlockStoreLocation)}. Throws an
* {@link IllegalArgumentException} if the location does not belong to tiered storage.
*
* @param location location the check available bytes
* @return available bytes
*/
public long getAvailableBytes(BlockStoreLocation location) {
return mMetadataManager.getAvailableBytes(location);
}
/**
* Returns null if block is pinned or currently being locked, otherwise returns
* {@link BlockMetadataManager#getBlockMeta(long)}.
*
* @param blockId the block id
* @return metadata of the block or null
* @throws BlockDoesNotExistException if no {@link BlockMeta} for this block id is found
*/
public BlockMeta getBlockMeta(long blockId) throws BlockDoesNotExistException {
if (isBlockEvictable(blockId)) {
return mMetadataManager.getBlockMeta(blockId);
} else {
return null;
}
}
}