001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.master; 020 021import org.apache.yetus.audience.InterfaceAudience; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024import org.apache.hadoop.hbase.ServerName; 025import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 026import org.apache.hadoop.hbase.util.Pair; 027 028import java.util.ArrayList; 029import java.util.Collections; 030import java.util.Comparator; 031import java.util.Date; 032import java.util.HashMap; 033import java.util.HashSet; 034import java.util.Iterator; 035import java.util.List; 036import java.util.Map; 037import java.util.Set; 038 039/** 040 * Class to hold dead servers list and utility querying dead server list. 041 * On znode expiration, servers are added here. 042 */ 043@InterfaceAudience.Private 044public class DeadServer { 045 private static final Logger LOG = LoggerFactory.getLogger(DeadServer.class); 046 047 /** 048 * Set of known dead servers. On znode expiration, servers are added here. 049 * This is needed in case of a network partitioning where the server's lease 050 * expires, but the server is still running. After the network is healed, 051 * and it's server logs are recovered, it will be told to call server startup 052 * because by then, its regions have probably been reassigned. 053 */ 054 private final Map<ServerName, Long> deadServers = new HashMap<>(); 055 056 /** 057 * Number of dead servers currently being processed 058 */ 059 private int numProcessing = 0; 060 061 /** 062 * Whether a dead server is being processed currently. 063 */ 064 private volatile boolean processing = false; 065 066 /** 067 * A dead server that comes back alive has a different start code. The new start code should be 068 * greater than the old one, but we don't take this into account in this method. 069 * 070 * @param newServerName Servername as either <code>host:port</code> or 071 * <code>host,port,startcode</code>. 072 * @return true if this server was dead before and coming back alive again 073 */ 074 public synchronized boolean cleanPreviousInstance(final ServerName newServerName) { 075 Iterator<ServerName> it = deadServers.keySet().iterator(); 076 while (it.hasNext()) { 077 ServerName sn = it.next(); 078 if (ServerName.isSameAddress(sn, newServerName)) { 079 it.remove(); 080 return true; 081 } 082 } 083 084 return false; 085 } 086 087 /** 088 * @param serverName server name. 089 * @return true if this server is on the dead servers list false otherwise 090 */ 091 public synchronized boolean isDeadServer(final ServerName serverName) { 092 return deadServers.containsKey(serverName); 093 } 094 095 /** 096 * Checks if there are currently any dead servers being processed by the 097 * master. Returns true if at least one region server is currently being 098 * processed as dead. 099 * 100 * @return true if any RS are being processed as dead 101 */ 102 public synchronized boolean areDeadServersInProgress() { return processing; } 103 104 public synchronized Set<ServerName> copyServerNames() { 105 Set<ServerName> clone = new HashSet<>(deadServers.size()); 106 clone.addAll(deadServers.keySet()); 107 return clone; 108 } 109 110 /** 111 * Adds the server to the dead server list if it's not there already. 112 * @param sn the server name 113 */ 114 public synchronized void add(ServerName sn) { 115 processing = true; 116 if (!deadServers.containsKey(sn)){ 117 deadServers.put(sn, EnvironmentEdgeManager.currentTime()); 118 } 119 } 120 121 /** 122 * Notify that we started processing this dead server. 123 * @param sn ServerName for the dead server. 124 */ 125 public synchronized void notifyServer(ServerName sn) { 126 if (LOG.isTraceEnabled()) { LOG.trace("Started processing " + sn); } 127 processing = true; 128 numProcessing++; 129 } 130 131 public synchronized void finish(ServerName sn) { 132 numProcessing--; 133 if (LOG.isTraceEnabled()) LOG.trace("Finished " + sn + "; numProcessing=" + numProcessing); 134 135 assert numProcessing >= 0: "Number of dead servers in processing should always be non-negative"; 136 137 if (numProcessing == 0) { processing = false; } 138 } 139 140 public synchronized int size() { 141 return deadServers.size(); 142 } 143 144 public synchronized boolean isEmpty() { 145 return deadServers.isEmpty(); 146 } 147 148 public synchronized void cleanAllPreviousInstances(final ServerName newServerName) { 149 Iterator<ServerName> it = deadServers.keySet().iterator(); 150 while (it.hasNext()) { 151 ServerName sn = it.next(); 152 if (ServerName.isSameAddress(sn, newServerName)) { 153 it.remove(); 154 } 155 } 156 } 157 158 @Override 159 public synchronized String toString() { 160 StringBuilder sb = new StringBuilder(); 161 for (ServerName sn : deadServers.keySet()) { 162 if (sb.length() > 0) { 163 sb.append(", "); 164 } 165 sb.append(sn.toString()); 166 } 167 return sb.toString(); 168 } 169 170 /** 171 * Extract all the servers dead since a given time, and sort them. 172 * @param ts the time, 0 for all 173 * @return a sorted array list, by death time, lowest values first. 174 */ 175 public synchronized List<Pair<ServerName, Long>> copyDeadServersSince(long ts){ 176 List<Pair<ServerName, Long>> res = new ArrayList<>(size()); 177 178 for (Map.Entry<ServerName, Long> entry:deadServers.entrySet()){ 179 if (entry.getValue() >= ts){ 180 res.add(new Pair<>(entry.getKey(), entry.getValue())); 181 } 182 } 183 184 Collections.sort(res, ServerNameDeathDateComparator); 185 return res; 186 } 187 188 /** 189 * Get the time when a server died 190 * @param deadServerName the dead server name 191 * @return the date when the server died 192 */ 193 public synchronized Date getTimeOfDeath(final ServerName deadServerName){ 194 Long time = deadServers.get(deadServerName); 195 return time == null ? null : new Date(time); 196 } 197 198 private static Comparator<Pair<ServerName, Long>> ServerNameDeathDateComparator = 199 new Comparator<Pair<ServerName, Long>>(){ 200 201 @Override 202 public int compare(Pair<ServerName, Long> o1, Pair<ServerName, Long> o2) { 203 return o1.getSecond().compareTo(o2.getSecond()); 204 } 205 }; 206 207 /** 208 * remove the specified dead server 209 * @param deadServerName the dead server name 210 * @return true if this server was removed 211 */ 212 213 public synchronized boolean removeDeadServer(final ServerName deadServerName) { 214 if (deadServers.remove(deadServerName) == null) { 215 return false; 216 } 217 return true; 218 } 219}