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 org.apache.hadoop.classification.InterfaceAudience; 021import org.apache.hadoop.hdfs.protocol.Block; 022import org.apache.hadoop.hdfs.server.namenode.NameNode; 023import org.apache.hadoop.ipc.Server; 024 025import java.util.*; 026 027/** 028 * Stores information about all corrupt blocks in the File System. 029 * A Block is considered corrupt only if all of its replicas are 030 * corrupt. While reporting replicas of a Block, we hide any corrupt 031 * copies. These copies are removed once Block is found to have 032 * expected number of good replicas. 033 * Mapping: Block -> TreeSet<DatanodeDescriptor> 034 */ 035 036@InterfaceAudience.Private 037public class CorruptReplicasMap{ 038 039 /** The corruption reason code */ 040 public static enum Reason { 041 NONE, // not specified. 042 ANY, // wildcard reason 043 GENSTAMP_MISMATCH, // mismatch in generation stamps 044 SIZE_MISMATCH, // mismatch in sizes 045 INVALID_STATE, // invalid state 046 CORRUPTION_REPORTED // client or datanode reported the corruption 047 } 048 049 private final SortedMap<Block, Map<DatanodeDescriptor, Reason>> corruptReplicasMap = 050 new TreeMap<Block, Map<DatanodeDescriptor, Reason>>(); 051 052 /** 053 * Mark the block belonging to datanode as corrupt. 054 * 055 * @param blk Block to be added to CorruptReplicasMap 056 * @param dn DatanodeDescriptor which holds the corrupt replica 057 * @param reason a textual reason (for logging purposes) 058 * @param reasonCode the enum representation of the reason 059 */ 060 void addToCorruptReplicasMap(Block blk, DatanodeDescriptor dn, 061 String reason, Reason reasonCode) { 062 Map <DatanodeDescriptor, Reason> nodes = corruptReplicasMap.get(blk); 063 if (nodes == null) { 064 nodes = new HashMap<DatanodeDescriptor, Reason>(); 065 corruptReplicasMap.put(blk, nodes); 066 } 067 068 String reasonText; 069 if (reason != null) { 070 reasonText = " because " + reason; 071 } else { 072 reasonText = ""; 073 } 074 075 if (!nodes.keySet().contains(dn)) { 076 NameNode.blockStateChangeLog.info( 077 "BLOCK NameSystem.addToCorruptReplicasMap: {} added as corrupt on " 078 + "{} by {} {}", blk.getBlockName(), dn, Server.getRemoteIp(), 079 reasonText); 080 } else { 081 NameNode.blockStateChangeLog.info( 082 "BLOCK NameSystem.addToCorruptReplicasMap: duplicate requested for" + 083 " {} to add as corrupt on {} by {} {}", blk.getBlockName(), dn, 084 Server.getRemoteIp(), reasonText); 085 } 086 // Add the node or update the reason. 087 nodes.put(dn, reasonCode); 088 } 089 090 /** 091 * Remove Block from CorruptBlocksMap 092 * 093 * @param blk Block to be removed 094 */ 095 void removeFromCorruptReplicasMap(Block blk) { 096 if (corruptReplicasMap != null) { 097 corruptReplicasMap.remove(blk); 098 } 099 } 100 101 /** 102 * Remove the block at the given datanode from CorruptBlockMap 103 * @param blk block to be removed 104 * @param datanode datanode where the block is located 105 * @return true if the removal is successful; 106 false if the replica is not in the map 107 */ 108 boolean removeFromCorruptReplicasMap(Block blk, DatanodeDescriptor datanode) { 109 return removeFromCorruptReplicasMap(blk, datanode, Reason.ANY); 110 } 111 112 boolean removeFromCorruptReplicasMap(Block blk, DatanodeDescriptor datanode, 113 Reason reason) { 114 Map <DatanodeDescriptor, Reason> datanodes = corruptReplicasMap.get(blk); 115 if (datanodes==null) 116 return false; 117 118 // if reasons can be compared but don't match, return false. 119 Reason storedReason = datanodes.get(datanode); 120 if (reason != Reason.ANY && storedReason != null && 121 reason != storedReason) { 122 return false; 123 } 124 125 if (datanodes.remove(datanode) != null) { // remove the replicas 126 if (datanodes.isEmpty()) { 127 // remove the block if there is no more corrupted replicas 128 corruptReplicasMap.remove(blk); 129 } 130 return true; 131 } 132 return false; 133 } 134 135 136 /** 137 * Get Nodes which have corrupt replicas of Block 138 * 139 * @param blk Block for which nodes are requested 140 * @return collection of nodes. Null if does not exists 141 */ 142 Collection<DatanodeDescriptor> getNodes(Block blk) { 143 Map <DatanodeDescriptor, Reason> nodes = corruptReplicasMap.get(blk); 144 if (nodes == null) 145 return null; 146 return nodes.keySet(); 147 } 148 149 /** 150 * Check if replica belonging to Datanode is corrupt 151 * 152 * @param blk Block to check 153 * @param node DatanodeDescriptor which holds the replica 154 * @return true if replica is corrupt, false if does not exists in this map 155 */ 156 boolean isReplicaCorrupt(Block blk, DatanodeDescriptor node) { 157 Collection<DatanodeDescriptor> nodes = getNodes(blk); 158 return ((nodes != null) && (nodes.contains(node))); 159 } 160 161 int numCorruptReplicas(Block blk) { 162 Collection<DatanodeDescriptor> nodes = getNodes(blk); 163 return (nodes == null) ? 0 : nodes.size(); 164 } 165 166 int size() { 167 return corruptReplicasMap.size(); 168 } 169 170 /** 171 * Return a range of corrupt replica block ids. Up to numExpectedBlocks 172 * blocks starting at the next block after startingBlockId are returned 173 * (fewer if numExpectedBlocks blocks are unavailable). If startingBlockId 174 * is null, up to numExpectedBlocks blocks are returned from the beginning. 175 * If startingBlockId cannot be found, null is returned. 176 * 177 * @param numExpectedBlocks Number of block ids to return. 178 * 0 <= numExpectedBlocks <= 100 179 * @param startingBlockId Block id from which to start. If null, start at 180 * beginning. 181 * @return Up to numExpectedBlocks blocks from startingBlockId if it exists 182 * 183 */ 184 long[] getCorruptReplicaBlockIds(int numExpectedBlocks, 185 Long startingBlockId) { 186 if (numExpectedBlocks < 0 || numExpectedBlocks > 100) { 187 return null; 188 } 189 190 Iterator<Block> blockIt = corruptReplicasMap.keySet().iterator(); 191 192 // if the starting block id was specified, iterate over keys until 193 // we find the matching block. If we find a matching block, break 194 // to leave the iterator on the next block after the specified block. 195 if (startingBlockId != null) { 196 boolean isBlockFound = false; 197 while (blockIt.hasNext()) { 198 Block b = blockIt.next(); 199 if (b.getBlockId() == startingBlockId) { 200 isBlockFound = true; 201 break; 202 } 203 } 204 205 if (!isBlockFound) { 206 return null; 207 } 208 } 209 210 ArrayList<Long> corruptReplicaBlockIds = new ArrayList<Long>(); 211 212 // append up to numExpectedBlocks blockIds to our list 213 for(int i=0; i<numExpectedBlocks && blockIt.hasNext(); i++) { 214 corruptReplicaBlockIds.add(blockIt.next().getBlockId()); 215 } 216 217 long[] ret = new long[corruptReplicaBlockIds.size()]; 218 for(int i=0; i<ret.length; i++) { 219 ret[i] = corruptReplicaBlockIds.get(i); 220 } 221 222 return ret; 223 } 224 225 /** 226 * return the reason about corrupted replica for a given block 227 * on a given dn 228 * @param block block that has corrupted replica 229 * @param node datanode that contains this corrupted replica 230 * @return reason 231 */ 232 String getCorruptReason(Block block, DatanodeDescriptor node) { 233 Reason reason = null; 234 if(corruptReplicasMap.containsKey(block)) { 235 if (corruptReplicasMap.get(block).containsKey(node)) { 236 reason = corruptReplicasMap.get(block).get(node); 237 } 238 } 239 if (reason != null) { 240 return reason.toString(); 241 } else { 242 return null; 243 } 244 } 245}