001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hdfs.server.blockmanagement; 019 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.List; 023 024import org.apache.hadoop.hdfs.protocol.Block; 025import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BlockUCState; 026import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.ReplicaState; 027import org.apache.hadoop.hdfs.server.namenode.NameNode; 028 029/** 030 * Represents a block that is currently being constructed.<br> 031 * This is usually the last block of a file opened for write or append. 032 */ 033public class BlockInfoUnderConstruction extends BlockInfo { 034 /** Block state. See {@link BlockUCState} */ 035 private BlockUCState blockUCState; 036 037 /** 038 * Block replicas as assigned when the block was allocated. 039 * This defines the pipeline order. 040 */ 041 private List<ReplicaUnderConstruction> replicas; 042 043 /** A data-node responsible for block recovery. */ 044 private int primaryNodeIndex = -1; 045 046 /** 047 * The new generation stamp, which this block will have 048 * after the recovery succeeds. Also used as a recovery id to identify 049 * the right recovery if any of the abandoned recoveries re-appear. 050 */ 051 private long blockRecoveryId = 0; 052 053 /** 054 * ReplicaUnderConstruction contains information about replicas while 055 * they are under construction. 056 * The GS, the length and the state of the replica is as reported by 057 * the data-node. 058 * It is not guaranteed, but expected, that data-nodes actually have 059 * corresponding replicas. 060 */ 061 static class ReplicaUnderConstruction extends Block { 062 private DatanodeDescriptor expectedLocation; 063 private ReplicaState state; 064 065 ReplicaUnderConstruction(Block block, 066 DatanodeDescriptor target, 067 ReplicaState state) { 068 super(block); 069 this.expectedLocation = target; 070 this.state = state; 071 } 072 073 /** 074 * Expected block replica location as assigned when the block was allocated. 075 * This defines the pipeline order. 076 * It is not guaranteed, but expected, that the data-node actually has 077 * the replica. 078 */ 079 DatanodeDescriptor getExpectedLocation() { 080 return expectedLocation; 081 } 082 083 /** 084 * Get replica state as reported by the data-node. 085 */ 086 ReplicaState getState() { 087 return state; 088 } 089 090 /** 091 * Set replica state. 092 */ 093 void setState(ReplicaState s) { 094 state = s; 095 } 096 097 /** 098 * Is data-node the replica belongs to alive. 099 */ 100 boolean isAlive() { 101 return expectedLocation.isAlive; 102 } 103 104 @Override // Block 105 public int hashCode() { 106 return super.hashCode(); 107 } 108 109 @Override // Block 110 public boolean equals(Object obj) { 111 // Sufficient to rely on super's implementation 112 return (this == obj) || super.equals(obj); 113 } 114 115 /** {@inheritDoc} */ 116 @Override 117 public String toString() { 118 final StringBuilder b = new StringBuilder(getClass().getSimpleName()); 119 b.append("[") 120 .append(expectedLocation) 121 .append("|") 122 .append(state) 123 .append("]"); 124 return b.toString(); 125 } 126 } 127 128 /** 129 * Create block and set its state to 130 * {@link BlockUCState#UNDER_CONSTRUCTION}. 131 */ 132 public BlockInfoUnderConstruction(Block blk, int replication) { 133 this(blk, replication, BlockUCState.UNDER_CONSTRUCTION, null); 134 } 135 136 /** 137 * Create a block that is currently being constructed. 138 */ 139 public BlockInfoUnderConstruction(Block blk, int replication, 140 BlockUCState state, 141 DatanodeDescriptor[] targets) { 142 super(blk, replication); 143 assert getBlockUCState() != BlockUCState.COMPLETE : 144 "BlockInfoUnderConstruction cannot be in COMPLETE state"; 145 this.blockUCState = state; 146 setExpectedLocations(targets); 147 } 148 149 /** 150 * Convert an under construction block to a complete block. 151 * 152 * @return BlockInfo - a complete block. 153 * @throws IOException if the state of the block 154 * (the generation stamp and the length) has not been committed by 155 * the client or it does not have at least a minimal number of replicas 156 * reported from data-nodes. 157 */ 158 BlockInfo convertToCompleteBlock() throws IOException { 159 assert getBlockUCState() != BlockUCState.COMPLETE : 160 "Trying to convert a COMPLETE block"; 161 return new BlockInfo(this); 162 } 163 164 /** Set expected locations */ 165 public void setExpectedLocations(DatanodeDescriptor[] targets) { 166 int numLocations = targets == null ? 0 : targets.length; 167 this.replicas = new ArrayList<ReplicaUnderConstruction>(numLocations); 168 for(int i = 0; i < numLocations; i++) 169 replicas.add( 170 new ReplicaUnderConstruction(this, targets[i], ReplicaState.RBW)); 171 } 172 173 /** 174 * Create array of expected replica locations 175 * (as has been assigned by chooseTargets()). 176 */ 177 public DatanodeDescriptor[] getExpectedLocations() { 178 int numLocations = replicas == null ? 0 : replicas.size(); 179 DatanodeDescriptor[] locations = new DatanodeDescriptor[numLocations]; 180 for(int i = 0; i < numLocations; i++) 181 locations[i] = replicas.get(i).getExpectedLocation(); 182 return locations; 183 } 184 185 /** Get the number of expected locations */ 186 public int getNumExpectedLocations() { 187 return replicas == null ? 0 : replicas.size(); 188 } 189 190 /** 191 * Return the state of the block under construction. 192 * @see BlockUCState 193 */ 194 @Override // BlockInfo 195 public BlockUCState getBlockUCState() { 196 return blockUCState; 197 } 198 199 void setBlockUCState(BlockUCState s) { 200 blockUCState = s; 201 } 202 203 /** Get block recovery ID */ 204 public long getBlockRecoveryId() { 205 return blockRecoveryId; 206 } 207 208 /** 209 * Process the recorded replicas. When about to commit or finish the 210 * pipeline recovery sort out bad replicas. 211 * @param genStamp The final generation stamp for the block. 212 */ 213 public void setGenerationStampAndVerifyReplicas(long genStamp) { 214 // Set the generation stamp for the block. 215 setGenerationStamp(genStamp); 216 if (replicas == null) 217 return; 218 219 // Remove the replicas with wrong gen stamp. 220 // The replica list is unchanged. 221 for (ReplicaUnderConstruction r : replicas) { 222 if (genStamp != r.getGenerationStamp()) { 223 r.getExpectedLocation().removeBlock(this); 224 NameNode.blockStateChangeLog.info("BLOCK* Removing stale replica " 225 + "from location: " + r.getExpectedLocation()); 226 } 227 } 228 } 229 230 /** 231 * Commit block's length and generation stamp as reported by the client. 232 * Set block state to {@link BlockUCState#COMMITTED}. 233 * @param block - contains client reported block length and generation 234 * @throws IOException if block ids are inconsistent. 235 */ 236 void commitBlock(Block block) throws IOException { 237 if(getBlockId() != block.getBlockId()) 238 throw new IOException("Trying to commit inconsistent block: id = " 239 + block.getBlockId() + ", expected id = " + getBlockId()); 240 blockUCState = BlockUCState.COMMITTED; 241 this.set(getBlockId(), block.getNumBytes(), block.getGenerationStamp()); 242 // Sort out invalid replicas. 243 setGenerationStampAndVerifyReplicas(block.getGenerationStamp()); 244 } 245 246 /** 247 * Initialize lease recovery for this block. 248 * Find the first alive data-node starting from the previous primary and 249 * make it primary. 250 */ 251 public void initializeBlockRecovery(long recoveryId) { 252 setBlockUCState(BlockUCState.UNDER_RECOVERY); 253 blockRecoveryId = recoveryId; 254 if (replicas.size() == 0) { 255 NameNode.blockStateChangeLog.warn("BLOCK*" 256 + " INodeFileUnderConstruction.initLeaseRecovery:" 257 + " No blocks found, lease removed."); 258 } 259 260 int previous = primaryNodeIndex; 261 for(int i = 1; i <= replicas.size(); i++) { 262 int j = (previous + i)%replicas.size(); 263 if (replicas.get(j).isAlive()) { 264 primaryNodeIndex = j; 265 DatanodeDescriptor primary = replicas.get(j).getExpectedLocation(); 266 primary.addBlockToBeRecovered(this); 267 NameNode.blockStateChangeLog.info("BLOCK* " + this 268 + " recovery started, primary=" + primary); 269 return; 270 } 271 } 272 } 273 274 void addReplicaIfNotPresent(DatanodeDescriptor dn, 275 Block block, 276 ReplicaState rState) { 277 for (ReplicaUnderConstruction r : replicas) { 278 if (r.getExpectedLocation() == dn) { 279 // Record the gen stamp from the report 280 r.setGenerationStamp(block.getGenerationStamp()); 281 return; 282 } 283 } 284 replicas.add(new ReplicaUnderConstruction(block, dn, rState)); 285 } 286 287 @Override // BlockInfo 288 // BlockInfoUnderConstruction participates in maps the same way as BlockInfo 289 public int hashCode() { 290 return super.hashCode(); 291 } 292 293 @Override // BlockInfo 294 public boolean equals(Object obj) { 295 // Sufficient to rely on super's implementation 296 return (this == obj) || super.equals(obj); 297 } 298 299 /** {@inheritDoc} */ 300 @Override 301 public String toString() { 302 final StringBuilder b = new StringBuilder(super.toString()); 303 b.append("{blockUCState=").append(blockUCState) 304 .append(", primaryNodeIndex=").append(primaryNodeIndex) 305 .append(", replicas=").append(replicas) 306 .append("}"); 307 return b.toString(); 308 } 309}