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}