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.util.ArrayList;
021import java.util.Collection;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.Iterator;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Map;
029import java.util.Queue;
030import java.util.Set;
031
032import com.google.common.annotations.VisibleForTesting;
033
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.apache.hadoop.classification.InterfaceAudience;
037import org.apache.hadoop.classification.InterfaceStability;
038import org.apache.hadoop.fs.StorageType;
039import org.apache.hadoop.hdfs.protocol.Block;
040import org.apache.hadoop.hdfs.protocol.DatanodeID;
041import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
042import org.apache.hadoop.hdfs.server.namenode.CachedBlock;
043import org.apache.hadoop.hdfs.server.protocol.DatanodeStorage;
044import org.apache.hadoop.hdfs.server.protocol.DatanodeStorage.State;
045import org.apache.hadoop.hdfs.server.protocol.StorageReport;
046import org.apache.hadoop.hdfs.server.protocol.VolumeFailureSummary;
047import org.apache.hadoop.hdfs.util.EnumCounters;
048import org.apache.hadoop.hdfs.util.LightWeightHashSet;
049import org.apache.hadoop.util.IntrusiveCollection;
050import org.apache.hadoop.util.Time;
051
052/**
053 * This class extends the DatanodeInfo class with ephemeral information (eg
054 * health, capacity, what blocks are associated with the Datanode) that is
055 * private to the Namenode, ie this class is not exposed to clients.
056 */
057@InterfaceAudience.Private
058@InterfaceStability.Evolving
059public class DatanodeDescriptor extends DatanodeInfo {
060  public static final Log LOG = LogFactory.getLog(DatanodeDescriptor.class);
061  public static final DatanodeDescriptor[] EMPTY_ARRAY = {};
062
063  // Stores status of decommissioning.
064  // If node is not decommissioning, do not use this object for anything.
065  public final DecommissioningStatus decommissioningStatus = new DecommissioningStatus();
066
067  /** Block and targets pair */
068  @InterfaceAudience.Private
069  @InterfaceStability.Evolving
070  public static class BlockTargetPair {
071    public final Block block;
072    public final DatanodeStorageInfo[] targets;    
073
074    BlockTargetPair(Block block, DatanodeStorageInfo[] targets) {
075      this.block = block;
076      this.targets = targets;
077    }
078  }
079
080  /** A BlockTargetPair queue. */
081  private static class BlockQueue<E> {
082    private final Queue<E> blockq = new LinkedList<E>();
083
084    /** Size of the queue */
085    synchronized int size() {return blockq.size();}
086
087    /** Enqueue */
088    synchronized boolean offer(E e) { 
089      return blockq.offer(e);
090    }
091
092    /** Dequeue */
093    synchronized List<E> poll(int numBlocks) {
094      if (numBlocks <= 0 || blockq.isEmpty()) {
095        return null;
096      }
097
098      List<E> results = new ArrayList<E>();
099      for(; !blockq.isEmpty() && numBlocks > 0; numBlocks--) {
100        results.add(blockq.poll());
101      }
102      return results;
103    }
104
105    /**
106     * Returns <tt>true</tt> if the queue contains the specified element.
107     */
108    boolean contains(E e) {
109      return blockq.contains(e);
110    }
111
112    synchronized void clear() {
113      blockq.clear();
114    }
115  }
116
117  private final Map<String, DatanodeStorageInfo> storageMap = 
118      new HashMap<String, DatanodeStorageInfo>();
119
120  /**
121   * A list of CachedBlock objects on this datanode.
122   */
123  public static class CachedBlocksList extends IntrusiveCollection<CachedBlock> {
124    public enum Type {
125      PENDING_CACHED,
126      CACHED,
127      PENDING_UNCACHED
128    }
129
130    private final DatanodeDescriptor datanode;
131
132    private final Type type;
133
134    CachedBlocksList(DatanodeDescriptor datanode, Type type) {
135      this.datanode = datanode;
136      this.type = type;
137    }
138
139    public DatanodeDescriptor getDatanode() {
140      return datanode;
141    }
142
143    public Type getType() {
144      return type;
145    }
146  }
147
148  /**
149   * The blocks which we want to cache on this DataNode.
150   */
151  private final CachedBlocksList pendingCached = 
152      new CachedBlocksList(this, CachedBlocksList.Type.PENDING_CACHED);
153
154  /**
155   * The blocks which we know are cached on this datanode.
156   * This list is updated by periodic cache reports.
157   */
158  private final CachedBlocksList cached = 
159      new CachedBlocksList(this, CachedBlocksList.Type.CACHED);
160
161  /**
162   * The blocks which we want to uncache on this DataNode.
163   */
164  private final CachedBlocksList pendingUncached = 
165      new CachedBlocksList(this, CachedBlocksList.Type.PENDING_UNCACHED);
166
167  public CachedBlocksList getPendingCached() {
168    return pendingCached;
169  }
170
171  public CachedBlocksList getCached() {
172    return cached;
173  }
174
175  public CachedBlocksList getPendingUncached() {
176    return pendingUncached;
177  }
178
179  /**
180   * The time when the last batch of caching directives was sent, in
181   * monotonic milliseconds.
182   */
183  private long lastCachingDirectiveSentTimeMs;
184
185  // isAlive == heartbeats.contains(this)
186  // This is an optimization, because contains takes O(n) time on Arraylist
187  public boolean isAlive = false;
188  public boolean needKeyUpdate = false;
189
190  private boolean forceRegistration = false;
191
192  // A system administrator can tune the balancer bandwidth parameter
193  // (dfs.balance.bandwidthPerSec) dynamically by calling
194  // "dfsadmin -setBalanacerBandwidth <newbandwidth>", at which point the
195  // following 'bandwidth' variable gets updated with the new value for each
196  // node. Once the heartbeat command is issued to update the value on the
197  // specified datanode, this value will be set back to 0.
198  private long bandwidth;
199
200  /** A queue of blocks to be replicated by this datanode */
201  private final BlockQueue<BlockTargetPair> replicateBlocks = new BlockQueue<BlockTargetPair>();
202  /** A queue of blocks to be recovered by this datanode */
203  private final BlockQueue<BlockInfoContiguousUnderConstruction> recoverBlocks =
204                                new BlockQueue<BlockInfoContiguousUnderConstruction>();
205  /** A set of blocks to be invalidated by this datanode */
206  private final LightWeightHashSet<Block> invalidateBlocks = new LightWeightHashSet<Block>();
207
208  /* Variables for maintaining number of blocks scheduled to be written to
209   * this storage. This count is approximate and might be slightly bigger
210   * in case of errors (e.g. datanode does not report if an error occurs
211   * while writing the block).
212   */
213  private EnumCounters<StorageType> currApproxBlocksScheduled
214      = new EnumCounters<StorageType>(StorageType.class);
215  private EnumCounters<StorageType> prevApproxBlocksScheduled
216      = new EnumCounters<StorageType>(StorageType.class);
217  private long lastBlocksScheduledRollTime = 0;
218  private static final int BLOCKS_SCHEDULED_ROLL_INTERVAL = 600*1000; //10min
219  private int volumeFailures = 0;
220  private VolumeFailureSummary volumeFailureSummary = null;
221  
222  /** 
223   * When set to true, the node is not in include list and is not allowed
224   * to communicate with the namenode
225   */
226  private boolean disallowed = false;
227
228  // The number of replication work pending before targets are determined
229  private int PendingReplicationWithoutTargets = 0;
230
231  // HB processing can use it to tell if it is the first HB since DN restarted
232  private boolean heartbeatedSinceRegistration = false;
233
234  /**
235   * DatanodeDescriptor constructor
236   * @param nodeID id of the data node
237   */
238  public DatanodeDescriptor(DatanodeID nodeID) {
239    super(nodeID);
240    updateHeartbeatState(StorageReport.EMPTY_ARRAY, 0L, 0L, 0, 0, null);
241  }
242
243  /**
244   * DatanodeDescriptor constructor
245   * @param nodeID id of the data node
246   * @param networkLocation location of the data node in network
247   */
248  public DatanodeDescriptor(DatanodeID nodeID, 
249                            String networkLocation) {
250    super(nodeID, networkLocation);
251    updateHeartbeatState(StorageReport.EMPTY_ARRAY, 0L, 0L, 0, 0, null);
252  }
253
254  @VisibleForTesting
255  public DatanodeStorageInfo getStorageInfo(String storageID) {
256    synchronized (storageMap) {
257      return storageMap.get(storageID);
258    }
259  }
260  @VisibleForTesting
261  public DatanodeStorageInfo[] getStorageInfos() {
262    synchronized (storageMap) {
263      final Collection<DatanodeStorageInfo> storages = storageMap.values();
264      return storages.toArray(new DatanodeStorageInfo[storages.size()]);
265    }
266  }
267
268  public StorageReport[] getStorageReports() {
269    final DatanodeStorageInfo[] infos = getStorageInfos();
270    final StorageReport[] reports = new StorageReport[infos.length];
271    for(int i = 0; i < infos.length; i++) {
272      reports[i] = infos[i].toStorageReport();
273    }
274    return reports;
275  }
276
277  boolean hasStaleStorages() {
278    synchronized (storageMap) {
279      for (DatanodeStorageInfo storage : storageMap.values()) {
280        if (storage.areBlockContentsStale()) {
281          return true;
282        }
283      }
284      return false;
285    }
286  }
287
288  /**
289   * Remove block from the list of blocks belonging to the data-node. Remove
290   * data-node from the block.
291   */
292  boolean removeBlock(BlockInfoContiguous b) {
293    final DatanodeStorageInfo s = b.findStorageInfo(this);
294    // if block exists on this datanode
295    if (s != null) {
296      return s.removeBlock(b);
297    }
298    return false;
299  }
300  
301  /**
302   * Remove block from the list of blocks belonging to the data-node. Remove
303   * data-node from the block.
304   */
305  boolean removeBlock(String storageID, BlockInfoContiguous b) {
306    DatanodeStorageInfo s = getStorageInfo(storageID);
307    if (s != null) {
308      return s.removeBlock(b);
309    }
310    return false;
311  }
312
313  public void resetBlocks() {
314    updateStorageStats(this.getStorageReports(), 0L, 0L, 0, 0, null);
315    this.invalidateBlocks.clear();
316    this.volumeFailures = 0;
317    // pendingCached, cached, and pendingUncached are protected by the
318    // FSN lock.
319    this.pendingCached.clear();
320    this.cached.clear();
321    this.pendingUncached.clear();
322  }
323  
324  public void clearBlockQueues() {
325    synchronized (invalidateBlocks) {
326      this.invalidateBlocks.clear();
327      this.recoverBlocks.clear();
328      this.replicateBlocks.clear();
329    }
330    // pendingCached, cached, and pendingUncached are protected by the
331    // FSN lock.
332    this.pendingCached.clear();
333    this.cached.clear();
334    this.pendingUncached.clear();
335  }
336
337  public int numBlocks() {
338    int blocks = 0;
339    for (DatanodeStorageInfo entry : getStorageInfos()) {
340      blocks += entry.numBlocks();
341    }
342    return blocks;
343  }
344
345  @VisibleForTesting
346  public boolean isHeartbeatedSinceRegistration() {
347    return heartbeatedSinceRegistration;
348  }
349
350  /**
351   * Updates stats from datanode heartbeat.
352   */
353  public void updateHeartbeat(StorageReport[] reports, long cacheCapacity,
354      long cacheUsed, int xceiverCount, int volFailures,
355      VolumeFailureSummary volumeFailureSummary) {
356    updateHeartbeatState(reports, cacheCapacity, cacheUsed, xceiverCount,
357        volFailures, volumeFailureSummary);
358    heartbeatedSinceRegistration = true;
359  }
360
361  /**
362   * process datanode heartbeat or stats initialization.
363   */
364  public void updateHeartbeatState(StorageReport[] reports, long cacheCapacity,
365      long cacheUsed, int xceiverCount, int volFailures,
366      VolumeFailureSummary volumeFailureSummary) {
367    updateStorageStats(reports, cacheCapacity, cacheUsed, xceiverCount,
368        volFailures, volumeFailureSummary);
369    setLastUpdate(Time.now());
370    setLastUpdateMonotonic(Time.monotonicNow());
371    rollBlocksScheduled(getLastUpdateMonotonic());
372  }
373
374  private void updateStorageStats(StorageReport[] reports, long cacheCapacity,
375      long cacheUsed, int xceiverCount, int volFailures,
376      VolumeFailureSummary volumeFailureSummary) {
377    long totalCapacity = 0;
378    long totalRemaining = 0;
379    long totalBlockPoolUsed = 0;
380    long totalDfsUsed = 0;
381    long totalNonDfsUsed = 0;
382    Set<DatanodeStorageInfo> failedStorageInfos = null;
383
384    // Decide if we should check for any missing StorageReport and mark it as
385    // failed. There are different scenarios.
386    // 1. When DN is running, a storage failed. Given the current DN
387    //    implementation doesn't add recovered storage back to its storage list
388    //    until DN restart, we can assume volFailures won't decrease
389    //    during the current DN registration session.
390    //    When volumeFailures == this.volumeFailures, it implies there is no
391    //    state change. No need to check for failed storage. This is an
392    //    optimization.  Recent versions of the DataNode report a
393    //    VolumeFailureSummary containing the date/time of the last volume
394    //    failure.  If that's available, then we check that instead for greater
395    //    accuracy.
396    // 2. After DN restarts, volFailures might not increase and it is possible
397    //    we still have new failed storage. For example, admins reduce
398    //    available storages in configuration. Another corner case
399    //    is the failed volumes might change after restart; a) there
400    //    is one good storage A, one restored good storage B, so there is
401    //    one element in storageReports and that is A. b) A failed. c) Before
402    //    DN sends HB to NN to indicate A has failed, DN restarts. d) After DN
403    //    restarts, storageReports has one element which is B.
404    final boolean checkFailedStorages;
405    if (volumeFailureSummary != null && this.volumeFailureSummary != null) {
406      checkFailedStorages = volumeFailureSummary.getLastVolumeFailureDate() >
407          this.volumeFailureSummary.getLastVolumeFailureDate();
408    } else {
409      checkFailedStorages = (volFailures > this.volumeFailures) ||
410          !heartbeatedSinceRegistration;
411    }
412
413    if (checkFailedStorages) {
414      LOG.info("Number of failed storage changes from "
415          + this.volumeFailures + " to " + volFailures);
416      failedStorageInfos = new HashSet<DatanodeStorageInfo>(
417          storageMap.values());
418    }
419
420    setCacheCapacity(cacheCapacity);
421    setCacheUsed(cacheUsed);
422    setXceiverCount(xceiverCount);
423    this.volumeFailures = volFailures;
424    this.volumeFailureSummary = volumeFailureSummary;
425    for (StorageReport report : reports) {
426      DatanodeStorageInfo storage = updateStorage(report.getStorage());
427      if (checkFailedStorages) {
428        failedStorageInfos.remove(storage);
429      }
430
431      storage.receivedHeartbeat(report);
432      totalCapacity += report.getCapacity();
433      totalRemaining += report.getRemaining();
434      totalBlockPoolUsed += report.getBlockPoolUsed();
435      totalDfsUsed += report.getDfsUsed();
436      totalNonDfsUsed += report.getNonDfsUsed();
437    }
438
439    // Update total metrics for the node.
440    setCapacity(totalCapacity);
441    setRemaining(totalRemaining);
442    setBlockPoolUsed(totalBlockPoolUsed);
443    setDfsUsed(totalDfsUsed);
444    setNonDfsUsed(totalNonDfsUsed);
445    if (checkFailedStorages) {
446      updateFailedStorage(failedStorageInfos);
447    }
448
449    if (storageMap.size() != reports.length) {
450      pruneStorageMap(reports);
451    }
452  }
453
454  /**
455   * Remove stale storages from storageMap. We must not remove any storages
456   * as long as they have associated block replicas.
457   */
458  private void pruneStorageMap(final StorageReport[] reports) {
459    if (LOG.isDebugEnabled()) {
460      LOG.debug("Number of storages reported in heartbeat=" + reports.length +
461                    "; Number of storages in storageMap=" + storageMap.size());
462    }
463
464    HashMap<String, DatanodeStorageInfo> excessStorages;
465
466    synchronized (storageMap) {
467      // Init excessStorages with all known storages.
468      excessStorages = new HashMap<String, DatanodeStorageInfo>(storageMap);
469
470      // Remove storages that the DN reported in the heartbeat.
471      for (final StorageReport report : reports) {
472        excessStorages.remove(report.getStorage().getStorageID());
473      }
474
475      // For each remaining storage, remove it if there are no associated
476      // blocks.
477      for (final DatanodeStorageInfo storageInfo : excessStorages.values()) {
478        if (storageInfo.numBlocks() == 0) {
479          storageMap.remove(storageInfo.getStorageID());
480          LOG.info("Removed storage " + storageInfo + " from DataNode" + this);
481        } else if (LOG.isDebugEnabled()) {
482          // This can occur until all block reports are received.
483          LOG.debug("Deferring removal of stale storage " + storageInfo +
484                        " with " + storageInfo.numBlocks() + " blocks");
485        }
486      }
487    }
488  }
489
490  private void updateFailedStorage(
491      Set<DatanodeStorageInfo> failedStorageInfos) {
492    for (DatanodeStorageInfo storageInfo : failedStorageInfos) {
493      if (storageInfo.getState() != DatanodeStorage.State.FAILED) {
494        LOG.info(storageInfo + " failed.");
495        storageInfo.setState(DatanodeStorage.State.FAILED);
496      }
497    }
498  }
499
500  private static class BlockIterator implements Iterator<BlockInfoContiguous> {
501    private int index = 0;
502    private final List<Iterator<BlockInfoContiguous>> iterators;
503    
504    private BlockIterator(final int startBlock,
505                          final DatanodeStorageInfo... storages) {
506      if(startBlock < 0) {
507        throw new IllegalArgumentException(
508            "Illegal value startBlock = " + startBlock);
509      }
510      List<Iterator<BlockInfoContiguous>> iterators = new ArrayList<Iterator<BlockInfoContiguous>>();
511      int s = startBlock;
512      int sumBlocks = 0;
513      for (DatanodeStorageInfo e : storages) {
514        int numBlocks = e.numBlocks();
515        sumBlocks += numBlocks;
516        if(sumBlocks <= startBlock) {
517          s -= numBlocks;
518        } else {
519          iterators.add(e.getBlockIterator());
520        }
521      }
522      this.iterators = Collections.unmodifiableList(iterators);
523      // skip to the storage containing startBlock
524      for(; s > 0 && hasNext(); s--) {
525        next();
526      }
527    }
528
529    @Override
530    public boolean hasNext() {
531      update();
532      return index < iterators.size() && iterators.get(index).hasNext();
533    }
534
535    @Override
536    public BlockInfoContiguous next() {
537      update();
538      return iterators.get(index).next();
539    }
540    
541    @Override
542    public void remove() {
543      throw new UnsupportedOperationException("Remove unsupported.");
544    }
545    
546    private void update() {
547      while(index < iterators.size() - 1 && !iterators.get(index).hasNext()) {
548        index++;
549      }
550    }
551  }
552
553  Iterator<BlockInfoContiguous> getBlockIterator() {
554    return getBlockIterator(0);
555  }
556
557  /**
558   * Get iterator, which starts iterating from the specified block.
559   */
560  Iterator<BlockInfoContiguous> getBlockIterator(final int startBlock) {
561    return new BlockIterator(startBlock, getStorageInfos());
562  }
563
564  void incrementPendingReplicationWithoutTargets() {
565    PendingReplicationWithoutTargets++;
566  }
567
568  void decrementPendingReplicationWithoutTargets() {
569    PendingReplicationWithoutTargets--;
570  }
571
572  /**
573   * Store block replication work.
574   */
575  void addBlockToBeReplicated(Block block, DatanodeStorageInfo[] targets) {
576    assert(block != null && targets != null && targets.length > 0);
577    replicateBlocks.offer(new BlockTargetPair(block, targets));
578  }
579
580  /**
581   * Store block recovery work.
582   */
583  void addBlockToBeRecovered(BlockInfoContiguousUnderConstruction block) {
584    if(recoverBlocks.contains(block)) {
585      // this prevents adding the same block twice to the recovery queue
586      BlockManager.LOG.info(block + " is already in the recovery queue");
587      return;
588    }
589    recoverBlocks.offer(block);
590  }
591
592  /**
593   * Store block invalidation work.
594   */
595  void addBlocksToBeInvalidated(List<Block> blocklist) {
596    assert(blocklist != null && blocklist.size() > 0);
597    synchronized (invalidateBlocks) {
598      for(Block blk : blocklist) {
599        invalidateBlocks.add(blk);
600      }
601    }
602  }
603
604  /**
605   * The number of work items that are pending to be replicated
606   */
607  int getNumberOfBlocksToBeReplicated() {
608    return PendingReplicationWithoutTargets + replicateBlocks.size();
609  }
610
611  /**
612   * The number of block invalidation items that are pending to 
613   * be sent to the datanode
614   */
615  int getNumberOfBlocksToBeInvalidated() {
616    synchronized (invalidateBlocks) {
617      return invalidateBlocks.size();
618    }
619  }
620
621  public List<BlockTargetPair> getReplicationCommand(int maxTransfers) {
622    return replicateBlocks.poll(maxTransfers);
623  }
624
625  public BlockInfoContiguousUnderConstruction[] getLeaseRecoveryCommand(int maxTransfers) {
626    List<BlockInfoContiguousUnderConstruction> blocks = recoverBlocks.poll(maxTransfers);
627    if(blocks == null)
628      return null;
629    return blocks.toArray(new BlockInfoContiguousUnderConstruction[blocks.size()]);
630  }
631
632  /**
633   * Remove the specified number of blocks to be invalidated
634   */
635  public Block[] getInvalidateBlocks(int maxblocks) {
636    synchronized (invalidateBlocks) {
637      Block[] deleteList = invalidateBlocks.pollToArray(new Block[Math.min(
638          invalidateBlocks.size(), maxblocks)]);
639      return deleteList.length == 0 ? null : deleteList;
640    }
641  }
642
643  /**
644   * Return the sum of remaining spaces of the specified type. If the remaining
645   * space of a storage is less than minSize, it won't be counted toward the
646   * sum.
647   *
648   * @param t The storage type. If null, the type is ignored.
649   * @param minSize The minimum free space required.
650   * @return the sum of remaining spaces that are bigger than minSize.
651   */
652  public long getRemaining(StorageType t, long minSize) {
653    long remaining = 0;
654    for (DatanodeStorageInfo s : getStorageInfos()) {
655      if (s.getState() == State.NORMAL &&
656          (t == null || s.getStorageType() == t)) {
657        long r = s.getRemaining();
658        if (r >= minSize) {
659          remaining += r;
660        }
661      }
662    }
663    return remaining;
664  }
665
666  /**
667   * @return Approximate number of blocks currently scheduled to be written 
668   * to the given storage type of this datanode.
669   */
670  public int getBlocksScheduled(StorageType t) {
671    return (int)(currApproxBlocksScheduled.get(t)
672        + prevApproxBlocksScheduled.get(t));
673  }
674
675  /**
676   * @return Approximate number of blocks currently scheduled to be written 
677   * to this datanode.
678   */
679  public int getBlocksScheduled() {
680    return (int)(currApproxBlocksScheduled.sum()
681        + prevApproxBlocksScheduled.sum());
682  }
683
684  /** Increment the number of blocks scheduled. */
685  void incrementBlocksScheduled(StorageType t) {
686    currApproxBlocksScheduled.add(t, 1);;
687  }
688  
689  /** Decrement the number of blocks scheduled. */
690  void decrementBlocksScheduled(StorageType t) {
691    if (prevApproxBlocksScheduled.get(t) > 0) {
692      prevApproxBlocksScheduled.subtract(t, 1);
693    } else if (currApproxBlocksScheduled.get(t) > 0) {
694      currApproxBlocksScheduled.subtract(t, 1);
695    } 
696    // its ok if both counters are zero.
697  }
698  
699  /** Adjusts curr and prev number of blocks scheduled every few minutes. */
700  private void rollBlocksScheduled(long now) {
701    if (now - lastBlocksScheduledRollTime > BLOCKS_SCHEDULED_ROLL_INTERVAL) {
702      prevApproxBlocksScheduled.set(currApproxBlocksScheduled);
703      currApproxBlocksScheduled.reset();
704      lastBlocksScheduledRollTime = now;
705    }
706  }
707  
708  @Override
709  public int hashCode() {
710    // Super implementation is sufficient
711    return super.hashCode();
712  }
713  
714  @Override
715  public boolean equals(Object obj) {
716    // Sufficient to use super equality as datanodes are uniquely identified
717    // by DatanodeID
718    return (this == obj) || super.equals(obj);
719  }
720
721  /** Decommissioning status */
722  public class DecommissioningStatus {
723    private int underReplicatedBlocks;
724    private int decommissionOnlyReplicas;
725    private int underReplicatedInOpenFiles;
726    private long startTime;
727    
728    synchronized void set(int underRep,
729        int onlyRep, int underConstruction) {
730      if (isDecommissionInProgress() == false) {
731        return;
732      }
733      underReplicatedBlocks = underRep;
734      decommissionOnlyReplicas = onlyRep;
735      underReplicatedInOpenFiles = underConstruction;
736    }
737
738    /** @return the number of under-replicated blocks */
739    public synchronized int getUnderReplicatedBlocks() {
740      if (isDecommissionInProgress() == false) {
741        return 0;
742      }
743      return underReplicatedBlocks;
744    }
745    /** @return the number of decommission-only replicas */
746    public synchronized int getDecommissionOnlyReplicas() {
747      if (isDecommissionInProgress() == false) {
748        return 0;
749      }
750      return decommissionOnlyReplicas;
751    }
752    /** @return the number of under-replicated blocks in open files */
753    public synchronized int getUnderReplicatedInOpenFiles() {
754      if (isDecommissionInProgress() == false) {
755        return 0;
756      }
757      return underReplicatedInOpenFiles;
758    }
759    /** Set start time */
760    public synchronized void setStartTime(long time) {
761      startTime = time;
762    }
763    /** @return start time */
764    public synchronized long getStartTime() {
765      if (isDecommissionInProgress() == false) {
766        return 0;
767      }
768      return startTime;
769    }
770  }  // End of class DecommissioningStatus
771
772  /**
773   * Set the flag to indicate if this datanode is disallowed from communicating
774   * with the namenode.
775   */
776  public void setDisallowed(boolean flag) {
777    disallowed = flag;
778  }
779  /** Is the datanode disallowed from communicating with the namenode? */
780  public boolean isDisallowed() {
781    return disallowed;
782  }
783
784  /**
785   * @return number of failed volumes in the datanode.
786   */
787  public int getVolumeFailures() {
788    return volumeFailures;
789  }
790
791  /**
792   * Returns info about volume failures.
793   *
794   * @return info about volume failures, possibly null
795   */
796  public VolumeFailureSummary getVolumeFailureSummary() {
797    return volumeFailureSummary;
798  }
799
800  /**
801   * @param nodeReg DatanodeID to update registration for.
802   */
803  @Override
804  public void updateRegInfo(DatanodeID nodeReg) {
805    super.updateRegInfo(nodeReg);
806    
807    // must re-process IBR after re-registration
808    for(DatanodeStorageInfo storage : getStorageInfos()) {
809      storage.setBlockReportCount(0);
810    }
811    heartbeatedSinceRegistration = false;
812    forceRegistration = false;
813  }
814
815  /**
816   * @return balancer bandwidth in bytes per second for this datanode
817   */
818  public long getBalancerBandwidth() {
819    return this.bandwidth;
820  }
821
822  /**
823   * @param bandwidth balancer bandwidth in bytes per second for this datanode
824   */
825  public void setBalancerBandwidth(long bandwidth) {
826    this.bandwidth = bandwidth;
827  }
828
829  @Override
830  public String dumpDatanode() {
831    StringBuilder sb = new StringBuilder(super.dumpDatanode());
832    int repl = replicateBlocks.size();
833    if (repl > 0) {
834      sb.append(" ").append(repl).append(" blocks to be replicated;");
835    }
836    int inval = invalidateBlocks.size();
837    if (inval > 0) {
838      sb.append(" ").append(inval).append(" blocks to be invalidated;");      
839    }
840    int recover = recoverBlocks.size();
841    if (recover > 0) {
842      sb.append(" ").append(recover).append(" blocks to be recovered;");
843    }
844    return sb.toString();
845  }
846
847  DatanodeStorageInfo updateStorage(DatanodeStorage s) {
848    synchronized (storageMap) {
849      DatanodeStorageInfo storage = storageMap.get(s.getStorageID());
850      if (storage == null) {
851        LOG.info("Adding new storage ID " + s.getStorageID() +
852                 " for DN " + getXferAddr());
853        storage = new DatanodeStorageInfo(this, s);
854        storageMap.put(s.getStorageID(), storage);
855      } else if (storage.getState() != s.getState() ||
856                 storage.getStorageType() != s.getStorageType()) {
857        // For backwards compatibility, make sure that the type and
858        // state are updated. Some reports from older datanodes do
859        // not include these fields so we may have assumed defaults.
860        storage.updateFromStorage(s);
861        storageMap.put(storage.getStorageID(), storage);
862      }
863      return storage;
864    }
865  }
866
867  /**
868   * @return   The time at which we last sent caching directives to this 
869   *           DataNode, in monotonic milliseconds.
870   */
871  public long getLastCachingDirectiveSentTimeMs() {
872    return this.lastCachingDirectiveSentTimeMs;
873  }
874
875  /**
876   * @param time  The time at which we last sent caching directives to this 
877   *              DataNode, in monotonic milliseconds.
878   */
879  public void setLastCachingDirectiveSentTimeMs(long time) {
880    this.lastCachingDirectiveSentTimeMs = time;
881  }
882  
883  /**
884   * checks whether atleast first block report has been received
885   * @return
886   */
887  public boolean checkBlockReportReceived() {
888    if(this.getStorageInfos().length == 0) {
889      return false;
890    }
891    for(DatanodeStorageInfo storageInfo: this.getStorageInfos()) {
892      if(storageInfo.getBlockReportCount() == 0 )
893        return false;
894    }
895    return true;
896  }
897
898  public void setForceRegistration(boolean force) {
899    forceRegistration = force;
900  }
901
902  public boolean isRegistered() {
903    return isAlive && !forceRegistration;
904  }
905}
906