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}