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 */
018 package org.apache.hadoop.hdfs.server.blockmanagement;
019
020 import java.io.IOException;
021 import java.util.ArrayList;
022 import java.util.Collections;
023 import java.util.Iterator;
024 import java.util.List;
025
026 import org.apache.hadoop.hdfs.protocol.Block;
027 import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BlockUCState;
028 import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.ReplicaState;
029 import org.apache.hadoop.hdfs.server.namenode.NameNode;
030
031 /**
032 * Represents a block that is currently being constructed.<br>
033 * This is usually the last block of a file opened for write or append.
034 */
035 public class BlockInfoUnderConstruction extends BlockInfo {
036 /** Block state. See {@link BlockUCState} */
037 private BlockUCState blockUCState;
038
039 /**
040 * Block replicas as assigned when the block was allocated.
041 * This defines the pipeline order.
042 */
043 private List<ReplicaUnderConstruction> replicas;
044
045 /**
046 * Index of the primary data node doing the recovery. Useful for log
047 * messages.
048 */
049 private int primaryNodeIndex = -1;
050
051 /**
052 * The new generation stamp, which this block will have
053 * after the recovery succeeds. Also used as a recovery id to identify
054 * the right recovery if any of the abandoned recoveries re-appear.
055 */
056 private long blockRecoveryId = 0;
057
058 /**
059 * ReplicaUnderConstruction contains information about replicas while
060 * they are under construction.
061 * The GS, the length and the state of the replica is as reported by
062 * the data-node.
063 * It is not guaranteed, but expected, that data-nodes actually have
064 * corresponding replicas.
065 */
066 static class ReplicaUnderConstruction extends Block {
067 private DatanodeDescriptor expectedLocation;
068 private ReplicaState state;
069 private boolean chosenAsPrimary;
070
071 ReplicaUnderConstruction(Block block,
072 DatanodeDescriptor target,
073 ReplicaState state) {
074 super(block);
075 this.expectedLocation = target;
076 this.state = state;
077 this.chosenAsPrimary = false;
078 }
079
080 /**
081 * Expected block replica location as assigned when the block was allocated.
082 * This defines the pipeline order.
083 * It is not guaranteed, but expected, that the data-node actually has
084 * the replica.
085 */
086 DatanodeDescriptor getExpectedLocation() {
087 return expectedLocation;
088 }
089
090 /**
091 * Get replica state as reported by the data-node.
092 */
093 ReplicaState getState() {
094 return state;
095 }
096
097 /**
098 * Whether the replica was chosen for recovery.
099 */
100 boolean getChosenAsPrimary() {
101 return chosenAsPrimary;
102 }
103
104 /**
105 * Set replica state.
106 */
107 void setState(ReplicaState s) {
108 state = s;
109 }
110
111 /**
112 * Set whether this replica was chosen for recovery.
113 */
114 void setChosenAsPrimary(boolean chosenAsPrimary) {
115 this.chosenAsPrimary = chosenAsPrimary;
116 }
117
118 /**
119 * Is data-node the replica belongs to alive.
120 */
121 boolean isAlive() {
122 return expectedLocation.isAlive;
123 }
124
125 @Override // Block
126 public int hashCode() {
127 return super.hashCode();
128 }
129
130 @Override // Block
131 public boolean equals(Object obj) {
132 // Sufficient to rely on super's implementation
133 return (this == obj) || super.equals(obj);
134 }
135
136 @Override
137 public String toString() {
138 final StringBuilder b = new StringBuilder(50);
139 appendStringTo(b);
140 return b.toString();
141 }
142
143 @Override
144 public void appendStringTo(StringBuilder sb) {
145 sb.append("ReplicaUnderConstruction[")
146 .append(expectedLocation)
147 .append("|")
148 .append(state)
149 .append("]");
150 }
151 }
152
153 /**
154 * Create block and set its state to
155 * {@link BlockUCState#UNDER_CONSTRUCTION}.
156 */
157 public BlockInfoUnderConstruction(Block blk, int replication) {
158 this(blk, replication, BlockUCState.UNDER_CONSTRUCTION, null);
159 }
160
161 /**
162 * Create a block that is currently being constructed.
163 */
164 public BlockInfoUnderConstruction(Block blk, int replication,
165 BlockUCState state,
166 DatanodeDescriptor[] targets) {
167 super(blk, replication);
168 assert getBlockUCState() != BlockUCState.COMPLETE :
169 "BlockInfoUnderConstruction cannot be in COMPLETE state";
170 this.blockUCState = state;
171 setExpectedLocations(targets);
172 }
173
174 /**
175 * Convert an under construction block to a complete block.
176 *
177 * @return BlockInfo - a complete block.
178 * @throws IOException if the state of the block
179 * (the generation stamp and the length) has not been committed by
180 * the client or it does not have at least a minimal number of replicas
181 * reported from data-nodes.
182 */
183 BlockInfo convertToCompleteBlock() throws IOException {
184 assert getBlockUCState() != BlockUCState.COMPLETE :
185 "Trying to convert a COMPLETE block";
186 return new BlockInfo(this);
187 }
188
189 /** Set expected locations */
190 public void setExpectedLocations(DatanodeDescriptor[] targets) {
191 int numLocations = targets == null ? 0 : targets.length;
192 this.replicas = new ArrayList<ReplicaUnderConstruction>(numLocations);
193 for(int i = 0; i < numLocations; i++)
194 replicas.add(
195 new ReplicaUnderConstruction(this, targets[i], ReplicaState.RBW));
196 }
197
198 /**
199 * Create array of expected replica locations
200 * (as has been assigned by chooseTargets()).
201 */
202 public DatanodeDescriptor[] getExpectedLocations() {
203 int numLocations = replicas == null ? 0 : replicas.size();
204 DatanodeDescriptor[] locations = new DatanodeDescriptor[numLocations];
205 for(int i = 0; i < numLocations; i++)
206 locations[i] = replicas.get(i).getExpectedLocation();
207 return locations;
208 }
209
210 /** Get the number of expected locations */
211 public int getNumExpectedLocations() {
212 return replicas == null ? 0 : replicas.size();
213 }
214
215 /**
216 * Return the state of the block under construction.
217 * @see BlockUCState
218 */
219 @Override // BlockInfo
220 public BlockUCState getBlockUCState() {
221 return blockUCState;
222 }
223
224 void setBlockUCState(BlockUCState s) {
225 blockUCState = s;
226 }
227
228 /** Get block recovery ID */
229 public long getBlockRecoveryId() {
230 return blockRecoveryId;
231 }
232
233 /**
234 * Commit block's length and generation stamp as reported by the client.
235 * Set block state to {@link BlockUCState#COMMITTED}.
236 * @param block - contains client reported block length and generation
237 * @throws IOException if block ids are inconsistent.
238 */
239 void commitBlock(Block block) throws IOException {
240 if(getBlockId() != block.getBlockId())
241 throw new IOException("Trying to commit inconsistent block: id = "
242 + block.getBlockId() + ", expected id = " + getBlockId());
243 blockUCState = BlockUCState.COMMITTED;
244 this.set(getBlockId(), block.getNumBytes(), block.getGenerationStamp());
245 }
246
247 /**
248 * Initialize lease recovery for this block.
249 * Find the first alive data-node starting from the previous primary and
250 * make it primary.
251 */
252 public void initializeBlockRecovery(long recoveryId) {
253 setBlockUCState(BlockUCState.UNDER_RECOVERY);
254 blockRecoveryId = recoveryId;
255 if (replicas.size() == 0) {
256 NameNode.blockStateChangeLog.warn("BLOCK*"
257 + " BlockInfoUnderConstruction.initLeaseRecovery:"
258 + " No blocks found, lease removed.");
259 }
260 boolean allLiveReplicasTriedAsPrimary = true;
261 for (int i = 0; i < replicas.size(); i++) {
262 // Check if all replicas have been tried or not.
263 if (replicas.get(i).isAlive()) {
264 allLiveReplicasTriedAsPrimary =
265 (allLiveReplicasTriedAsPrimary && replicas.get(i).getChosenAsPrimary());
266 }
267 }
268 if (allLiveReplicasTriedAsPrimary) {
269 // Just set all the replicas to be chosen whether they are alive or not.
270 for (int i = 0; i < replicas.size(); i++) {
271 replicas.get(i).setChosenAsPrimary(false);
272 }
273 }
274 long mostRecentLastUpdate = 0;
275 ReplicaUnderConstruction primary = null;
276 primaryNodeIndex = -1;
277 for(int i = 0; i < replicas.size(); i++) {
278 // Skip alive replicas which have been chosen for recovery.
279 if (!(replicas.get(i).isAlive() && !replicas.get(i).getChosenAsPrimary())) {
280 continue;
281 }
282 if (replicas.get(i).getExpectedLocation().getLastUpdate() > mostRecentLastUpdate) {
283 primary = replicas.get(i);
284 primaryNodeIndex = i;
285 mostRecentLastUpdate = primary.getExpectedLocation().getLastUpdate();
286 }
287 }
288 if (primary != null) {
289 primary.getExpectedLocation().addBlockToBeRecovered(this);
290 primary.setChosenAsPrimary(true);
291 NameNode.blockStateChangeLog.info("BLOCK* " + this
292 + " recovery started, primary=" + primary);
293 }
294 }
295
296 void addReplicaIfNotPresent(DatanodeDescriptor dn,
297 Block block,
298 ReplicaState rState) {
299 for(ReplicaUnderConstruction r : replicas)
300 if(r.getExpectedLocation() == dn)
301 return;
302 replicas.add(new ReplicaUnderConstruction(block, dn, rState));
303 }
304
305 @Override // BlockInfo
306 // BlockInfoUnderConstruction participates in maps the same way as BlockInfo
307 public int hashCode() {
308 return super.hashCode();
309 }
310
311 @Override // BlockInfo
312 public boolean equals(Object obj) {
313 // Sufficient to rely on super's implementation
314 return (this == obj) || super.equals(obj);
315 }
316
317 @Override
318 public String toString() {
319 final StringBuilder b = new StringBuilder(100);
320 appendStringTo(b);
321 return b.toString();
322 }
323
324 @Override
325 public void appendStringTo(StringBuilder sb) {
326 super.appendStringTo(sb);
327 appendUCParts(sb);
328 }
329
330 private void appendUCParts(StringBuilder sb) {
331 sb.append("{blockUCState=").append(blockUCState)
332 .append(", primaryNodeIndex=").append(primaryNodeIndex)
333 .append(", replicas=[");
334 Iterator<ReplicaUnderConstruction> iter = replicas.iterator();
335 if (iter.hasNext()) {
336 iter.next().appendStringTo(sb);
337 while (iter.hasNext()) {
338 sb.append(", ");
339 iter.next().appendStringTo(sb);
340 }
341 }
342 sb.append("]}");
343 }
344 }