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    }