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