/** * 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.namenode; import java.io.IOException; import java.util.List; import org.apache.hadoop.fs.permission.PermissionStatus; import org.apache.hadoop.hdfs.protocol.Block; import org.apache.hadoop.hdfs.server.namenode.BlocksMap.BlockInfo; class INodeFileUnderConstruction extends INodeFile { String clientName; // lease holder private String clientMachine; private final DatanodeDescriptor clientNode; // if client is a cluster node too. private int primaryNodeIndex = -1; //the node working on lease recovery // targets and targetGSs should either both be null or have the same length private DatanodeDescriptor[] targets = null; //locations for last block private long[] targetGSs = null; // generation stamp for each replica private long lastRecoveryTime = 0; private boolean lastBlockReplicated = false; // if scheduled to replicate last block INodeFileUnderConstruction(long id, PermissionStatus permissions, short replication, long preferredBlockSize, long modTime, String clientName, String clientMachine, DatanodeDescriptor clientNode) { this(id, permissions, replication, preferredBlockSize, modTime, modTime, clientName, clientMachine, clientNode); } INodeFileUnderConstruction(long id, PermissionStatus permissions, short replication, long preferredBlockSize, long modTime, long accessTime, String clientName, String clientMachine, DatanodeDescriptor clientNode) { super(id, permissions.applyUMask(UMASK), 0, replication, modTime, accessTime, preferredBlockSize); this.clientName = clientName; this.clientMachine = clientMachine; this.clientNode = clientNode; } public INodeFileUnderConstruction(long id, byte[] name, short blockReplication, long modificationTime, long preferredBlockSize, BlockInfo[] blocks, PermissionStatus perm, String clientName, String clientMachine, DatanodeDescriptor clientNode) { this(id, name, blockReplication, modificationTime, modificationTime,preferredBlockSize, blocks, perm, clientName, clientMachine,clientNode); } public INodeFileUnderConstruction(long id, byte[] name, short blockReplication, long modificationTime, long accessTime, long preferredBlockSize, BlockInfo[] blocks, PermissionStatus perm, String clientName, String clientMachine, DatanodeDescriptor clientNode) { super(id, perm, blocks, blockReplication, modificationTime, accessTime, preferredBlockSize, null); setLocalName(name); this.clientName = clientName; this.clientMachine = clientMachine; this.clientNode = clientNode; } String getClientName() { return clientName; } void setClientMachine(String clientMachine) { this.clientMachine = clientMachine; } public void setClientName(String clientName) { this.clientName = clientName; } String getClientMachine() { return clientMachine; } DatanodeDescriptor getClientNode() { return clientNode; } /** * Is this inode being constructed? */ @Override boolean isUnderConstruction() { return true; } DatanodeDescriptor[] getTargets() { return targets; } /** Return the targets with generation stamp matching that of the last block */ DatanodeDescriptor[] getValidTargets() { if (targetGSs == null) { return null; } int count = 0; long lastBlockGS = this.getLastBlock().getGenerationStamp(); for (long targetGS : targetGSs) { if (lastBlockGS == targetGS) { count++; } } if (count == 0) { return null; } if (count == targets.length) { return targets; } else { DatanodeDescriptor[] validTargets = new DatanodeDescriptor[count]; for (int i=0, numOfValidTargets=0; i<targets.length; i++) { if (lastBlockGS == targetGSs[i]) { validTargets[numOfValidTargets++] = targets[i]; if (numOfValidTargets == count) { return validTargets; } } } return validTargets; } } void clearTargets() { if (targets != null) { for (DatanodeDescriptor node : targets) { node.removeINode(this); } } this.targets = null; this.targetGSs = null; } /** * Set targets for list of replicas all sharing the same generationStamp * * @param locs location of replicas * @param generationStamp shared generation stamp */ void setTargets(DatanodeDescriptor[] locs, long generationStamp) { setTargets(locs); if (locs == null) { targetGSs = null; return; } long[] targetGSs = new long[locs.length]; for (int i=0; i<targetGSs.length; i++) { targetGSs[i] = generationStamp; } this.targetGSs = targetGSs; } private void setTargets(DatanodeDescriptor[] targets) { // remove assoc of this with previous Datanodes removeINodeFromDatanodeDescriptors(this.targets); // add new assoc addINodeToDatanodeDescriptors(targets); this.targets = targets; this.primaryNodeIndex = -1; } /** * add this target if it does not already exists. Returns true if the target * was added. * * @param node * data node having the block * @param generationStamp * the generation of the block on the data node * @return true if the data node is added to the target list, or previous * generation stamp for the datanode is updated. Otherwise, false, * which means the data node is already in the target list with * the same generation stamp. */ boolean addTarget(DatanodeDescriptor node, long generationStamp) { if (this.targets == null) { this.targets = new DatanodeDescriptor[0]; } for (int i=0; i<targets.length; i++) { if (targets[i].equals(node)) { if (generationStamp != targetGSs[i]) { targetGSs[i] = generationStamp; return true; } return false; } } if (node != null) { node.addINode(this); } // allocate new data structure to store additional target DatanodeDescriptor[] newt = new DatanodeDescriptor[targets.length + 1]; long[] newgs = new long[targets.length + 1]; for (int i = 0; i < targets.length; i++) { newt[i] = this.targets[i]; newgs[i] = this.targetGSs[i]; } newt[targets.length] = node; newgs[targets.length] = generationStamp; this.targets = newt; this.targetGSs = newgs; this.primaryNodeIndex = -1; return true; } void removeTarget(DatanodeDescriptor node) { if (targets != null) { int index = -1; for (int j = 0; j < this.targets.length; j++) { if (this.targets[j].equals(node)) { index = j; break; } } if (index == -1) { StringBuilder sb = new StringBuilder(); for (DatanodeDescriptor datanode : this.targets) { sb.append(datanode.getName() + ":" + datanode.getStorageID() + " "); } NameNode.stateChangeLog.error( "Node is not in the targets of INodeFileUnderConstruction: " + " node=" + node.getName() + ":" + node.getStorageID() + " inode=" + this + " targets=" + sb); return; } DatanodeDescriptor[] newt = new DatanodeDescriptor[targets.length - 1]; long[] newgs = new long[targets.length - 1]; for (int i = 0, j = 0; i < targets.length; i++) { if (i != index) { newt[j] = this.targets[i]; newgs[j++] = this.targetGSs[i]; } } setTargets(newt); this.targetGSs = newgs; } } // // converts a INodeFileUnderConstruction into a INodeFile // use the modification time as the access time // INodeFile convertToInodeFile(boolean changeAccessTime) { INodeFile obj = new INodeFile(getId(), getPermissionStatus(), getBlocks(), getReplication(), getModificationTime(), changeAccessTime ? getModificationTime() : getAccessTime(), getPreferredBlockSize(), null); return obj; } INodeFile convertToInodeFile() { return this.convertToInodeFile(true); } /** * remove a block from the block list. This block should be * the last one on the list. */ void removeBlock(Block oldblock) throws IOException { this.storage.removeBlock(oldblock); setTargets(null, -1); // reset targets to be null } /** * This function throws exception if the last block of the file * is not for blockId. * @param blockId * @throws IOException */ synchronized void checkLastBlockId(long blockId) throws IOException { this.storage.checkLastBlockId(blockId); } synchronized void setLastBlock(BlockInfo newblock, DatanodeDescriptor[] newtargets ) throws IOException { this.storage.setLastBlock(newblock); setTargets(newtargets, newblock.getGenerationStamp()); lastRecoveryTime = 0; } /** * Initialize lease recovery for this object */ void assignPrimaryDatanode() { //assign the first alive datanode as the primary datanode if (targets.length == 0) { NameNode.stateChangeLog.warn("BLOCK*" + " INodeFileUnderConstruction.initLeaseRecovery:" + " No blocks found, lease removed."); } int previous = primaryNodeIndex; Block lastBlock = this.getLastBlock(); // find an alive datanode beginning from previous. // This causes us to cycle through the targets on successive retries. for(int i = 1; i <= targets.length; i++) { int j = (previous + i)%targets.length; if (targets[j].isAlive) { DatanodeDescriptor primary = targets[primaryNodeIndex = j]; primary.addBlockToBeRecovered(lastBlock, targets); NameNode.stateChangeLog.info("BLOCK* " + lastBlock + " recovery started, primary=" + primary); return; } } } /** * Update lastRecoveryTime if expired. * @return true if lastRecoveryTimeis updated. */ synchronized boolean setLastRecoveryTime(long now) { boolean expired = now - lastRecoveryTime > NameNode.LEASE_RECOVER_PERIOD; if (expired) { lastRecoveryTime = now; } return expired; } /** Check if the last block has scheduled to be replicated */ synchronized boolean isLastBlockReplicated() { return this.lastBlockReplicated; } /** Mark that last block has been scheduled to be replicated */ synchronized void setLastBlockReplicated() { this.lastBlockReplicated = true; } /** * When deleting an open file, we should remove it from the list * of its targets. */ int collectSubtreeBlocksAndClear(List<BlockInfo> v, int blocksLimit, List<INode> removedINodes) { clearTargets(); return super.collectSubtreeBlocksAndClear(v, blocksLimit, removedINodes); } /** * Set local file name. Since the name, and hence hash value, changes, * we need to reinsert this inode into the list of it's targets. */ @Override void setLocalName(byte[] name) { removeINodeFromDatanodeDescriptors(targets); this.name = name; addINodeToDatanodeDescriptors(targets); } /** * Remove this INodeFileUnderConstruction from the list of datanodes. */ private void removeINodeFromDatanodeDescriptors(DatanodeDescriptor[] targets) { if (targets != null) { for (DatanodeDescriptor node : targets) { node.removeINode(this); } } } /** * Add this INodeFileUnderConstruction to the list of datanodes. */ private void addINodeToDatanodeDescriptors(DatanodeDescriptor[] targets) { if (targets != null) { for (DatanodeDescriptor node : targets) { node.addINode(this); } } } }