/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.apache.hadoop.hdfs.server.blockmanagement; import io.hops.exception.StorageException; import io.hops.exception.TransactionContextException; import io.hops.metadata.common.FinderType; import io.hops.metadata.hdfs.entity.Replica; import io.hops.metadata.hdfs.entity.ReplicaBase; import io.hops.transaction.EntityManager; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.hdfs.protocol.Block; import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BlockUCState; import org.apache.hadoop.hdfs.server.namenode.INode; import org.apache.hadoop.hdfs.server.namenode.INodeFile; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Internal class for block metadata. BlockInfo class maintains for a given * block the {@link BlockCollection} it is part of and datanodes where the * replicas of the block are stored. */ @InterfaceAudience.Private public class BlockInfo extends Block { public static final BlockInfo[] EMPTY_ARRAY = {}; private static final List<Replica> EMPTY_REPLICAS_ARRAY = new ArrayList<Replica>(); public static enum Finder implements FinderType<BlockInfo> { ByBlockIdAndINodeId, ByINodeId, ByINodeIds, ByMaxBlockIndexForINode, ByBlockIdsAndINodeIds; @Override public Class getType() { return BlockInfo.class; } @Override public Annotation getAnnotated() { switch (this) { case ByBlockIdAndINodeId: return Annotation.PrimaryKey; case ByBlockIdsAndINodeIds: return Annotation.Batched; case ByMaxBlockIndexForINode: return Annotation.PrunedIndexScan; case ByINodeId: return Annotation.PrunedIndexScan; case ByINodeIds: return Annotation.BatchedPrunedIndexScan; default: throw new IllegalStateException(); } } } public static enum Order implements Comparator<BlockInfo> { ByBlockIndex() { @Override public int compare(BlockInfo o1, BlockInfo o2) { if (o1.getBlockIndex() == o2.getBlockIndex()) { throw new IllegalStateException( "A file cannot have 2 blocks with the same index. index = " + o1.getBlockIndex() + " blk1_id = " + o1.getBlockId() + " blk2_id = " + o2.getBlockId()); } if (o1.getBlockIndex() < o2.getBlockIndex()) { return -1; } else { return 1; } } }, ByBlockId() { @Override public int compare(BlockInfo o1, BlockInfo o2) { if (o1.getBlockId() == o2.getBlockId()) { return 0; } if (o1.getBlockId() < o2.getBlockId()) { return -1; } else { return 1; } } }, ByGenerationStamp() { @Override public int compare(BlockInfo o1, BlockInfo o2) { if (o1.getGenerationStamp() == o2.getGenerationStamp()) { throw new IllegalStateException( "A file cannot have 2 blocks with the same generation stamp"); } if (o1.getGenerationStamp() < o2.getGenerationStamp()) { return -1; } else { return 1; } } }; @Override public abstract int compare(BlockInfo o1, BlockInfo o2); public Comparator acsending() { return this; } public Comparator descending() { return Collections.reverseOrder(this); } } private BlockCollection bc; private int blockIndex = -1; private long timestamp = 1; protected int inodeId = INode.NON_EXISTING_ID; public BlockInfo(Block blk, int inodeId) { super(blk); this.inodeId = inodeId; if (blk instanceof BlockInfo) { this.bc = ((BlockInfo) blk).bc; this.blockIndex = ((BlockInfo) blk).blockIndex; this.timestamp = ((BlockInfo) blk).timestamp; if (inodeId != ((BlockInfo) blk).inodeId) { throw new IllegalArgumentException("inodeId does not match"); } } } public BlockInfo() { this.bc = null; } /** * Copy construction. This is used to convert BlockInfoUnderConstruction * * @param from * BlockInfo to copy from. */ protected BlockInfo(BlockInfo from) { super(from); this.bc = from.bc; this.blockIndex = from.blockIndex; this.timestamp = from.timestamp; this.inodeId = from.inodeId; } public BlockCollection getBlockCollection() throws StorageException, TransactionContextException { //Every time get block collection is called, get it from DB //Why? some times it happens that the inode is deleted and copy //of the block is lying around is some secondary data structure ( not block_info ) //if we call get block collection op of that copy then it should return null BlockCollection bc = (BlockCollection) EntityManager .find(INodeFile.Finder.ByINodeIdFTIS, inodeId); this.bc = bc; if (bc == null) { this.inodeId = INode.NON_EXISTING_ID; } return bc; } public void setBlockCollection(BlockCollection bc) throws StorageException, TransactionContextException { this.bc = bc; if (bc != null) { setINodeId(bc.getId()); } } /** * Count the number of data-nodes the block belongs to. */ public int numNodes(DatanodeManager datanodeMgr) throws StorageException, TransactionContextException { return getReplicas(datanodeMgr).size(); } public DatanodeDescriptor[] getDatanodes(DatanodeManager datanodeMgr) throws StorageException, TransactionContextException { List<Replica> replicas = getReplicas(datanodeMgr); return getDatanodes(datanodeMgr, replicas); } List<Replica> getReplicasNoCheck() throws StorageException, TransactionContextException { List<Replica> replicas = (List<Replica>) EntityManager .findList(Replica.Finder.ByBlockIdAndINodeId, getBlockId(), getInodeId()); if (replicas == null) { replicas = EMPTY_REPLICAS_ARRAY; } else { Collections.sort(replicas, Replica.Order.ByStorageId); } return replicas; } List<Replica> getReplicas(DatanodeManager datanodeMgr) throws StorageException, TransactionContextException { List<Replica> replicas = getReplicasNoCheck(); getDatanodes(datanodeMgr, replicas); //getReplicasNoCheck return a sorted list of replicas. //There is no need to sort the list again after removing the dead replicas. //Collections.sort(replicas, Replica.Order.ByStorageId); return replicas; } /** * Adds new replica for this block. */ Replica addReplica(DatanodeDescriptor dn, BlockInfo b) throws StorageException, TransactionContextException { Replica replica = new Replica(dn.getSId(), getBlockId(), b.getInodeId()); add(replica); return replica; } public void removeAllReplicas() throws StorageException, TransactionContextException { for (Replica replica : getReplicasNoCheck()) { remove(replica); } } /** * removes a replica of this block related to storageId * * @return */ Replica removeReplica(DatanodeDescriptor dn) throws StorageException, TransactionContextException { List<Replica> replicas = getReplicasNoCheck(); Replica replica = null; for (Replica r : replicas) { if (r.getStorageId() == dn.getSId()) { replica = r; remove(r); break; } } return replica; } int findDatanode(DatanodeDescriptor dn) throws StorageException, TransactionContextException { Replica replica = EntityManager .find(Replica.Finder.ByBlockIdAndStorageId, getBlockId(), dn.getSId()); if (replica == null) { return -1; } return 1; } boolean hasReplicaIn(DatanodeDescriptor dn) throws StorageException, TransactionContextException { return EntityManager .find(Replica.Finder.ByBlockIdAndStorageId, getBlockId(), dn.getSId()) != null; } /** * BlockInfo represents a block that is not being constructed. In order to * start modifying the block, the BlockInfo should be converted to * {@link BlockInfoUnderConstruction}. * * @return {@link BlockUCState#COMPLETE} */ public BlockUCState getBlockUCState() { return BlockUCState.COMPLETE; } /** * Is this block complete? * * @return true if the state of the block is {@link BlockUCState#COMPLETE} */ public boolean isComplete() { return getBlockUCState().equals(BlockUCState.COMPLETE); } /** * Convert a complete block to an under construction block. * * @return BlockInfoUnderConstruction - an under construction block. */ public BlockInfoUnderConstruction convertToBlockUnderConstruction( BlockUCState s, DatanodeDescriptor[] targets) throws StorageException, TransactionContextException { if (isComplete()) { return new BlockInfoUnderConstruction(this, this.getInodeId(), s, targets); } // the block is already under construction BlockInfoUnderConstruction ucBlock = (BlockInfoUnderConstruction) this; ucBlock.setBlockUCState(s); ucBlock.setExpectedLocations(targets); return ucBlock; } public int getInodeId() { return inodeId; } public void setINodeIdNoPersistance(int id) { this.inodeId = id; } public void setINodeId(int id) throws StorageException, TransactionContextException { setINodeIdNoPersistance(id); save(); } public int getBlockIndex() { return this.blockIndex; } public void setBlockIndexNoPersistance(int bindex) { this.blockIndex = bindex; } public void setBlockIndex(int bindex) throws StorageException, TransactionContextException { setBlockIndexNoPersistance(bindex); save(); } public long getTimestamp() { return this.timestamp; } public void setTimestampNoPersistance(long ts) { this.timestamp = ts; } public void setTimestamp(long ts) throws StorageException, TransactionContextException { setTimestampNoPersistance(ts); save(); } protected DatanodeDescriptor[] getDatanodes(DatanodeManager datanodeMgr, List<? extends ReplicaBase> replicas) { int numLocations = replicas.size(); List<DatanodeDescriptor> list = new ArrayList<DatanodeDescriptor>(); for (int i = numLocations - 1; i >= 0; i--) { DatanodeDescriptor desc = datanodeMgr.getDatanode(replicas.get(i).getStorageId()); if (desc != null) { list.add(desc); } else { replicas.remove(i); } } DatanodeDescriptor[] locations = new DatanodeDescriptor[list.size()]; return list.toArray(locations); } protected void add(Replica replica) throws StorageException, TransactionContextException { EntityManager.add(replica); } protected void remove(Replica replica) throws StorageException, TransactionContextException { EntityManager.remove(replica); } protected void save(Replica replica) throws StorageException, TransactionContextException { EntityManager.update(replica); } @Override public int hashCode() { // Super implementation is sufficient return super.hashCode(); } @Override public boolean equals(Object obj) { // Sufficient to rely on super's implementation return (this == obj) || super.equals(obj); } @Override public String toString() { return "bid= " + getBlockId() + " State = " + getBlockUCState(); } public void setBlockId(long bid) throws StorageException, TransactionContextException { setBlockIdNoPersistance(bid); save(); } public void setNumBytes(long len) throws StorageException, TransactionContextException { setNumBytesNoPersistance(len); save(); } public void setGenerationStamp(long stamp) throws StorageException, TransactionContextException { setGenerationStampNoPersistance(stamp); save(); } public void set(long blkid, long len, long genStamp) throws StorageException, TransactionContextException { setNoPersistance(blkid, len, genStamp); save(); } protected void save() throws StorageException, TransactionContextException { save(this); } protected void save(BlockInfo blk) throws StorageException, TransactionContextException { EntityManager.update(blk); } protected void remove() throws StorageException, TransactionContextException { remove(this); } protected void remove(BlockInfo blk) throws StorageException, TransactionContextException { EntityManager.remove(blk); } public static BlockInfo cloneBlock(BlockInfo block) throws StorageException { if (block instanceof BlockInfo) { return new BlockInfo(((BlockInfo) block), ((BlockInfo) block).getInodeId()); } else if (block instanceof BlockInfoUnderConstruction) { return new BlockInfoUnderConstruction((BlockInfoUnderConstruction) block, ((BlockInfoUnderConstruction) block).getInodeId()); } else { throw new StorageException("Unable to create a clone of the Block"); } } }