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.hbase.client;
019
020import java.io.IOException;
021import java.io.PrintWriter;
022import java.io.StringWriter;
023import java.util.Collection;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029import org.apache.hadoop.hbase.DoNotRetryIOException;
030import org.apache.hadoop.hbase.RegionTooBusyException;
031import org.apache.hadoop.hbase.util.Bytes;
032import org.apache.yetus.audience.InterfaceAudience;
033
034/**
035 * This subclass of {@link org.apache.hadoop.hbase.client.RetriesExhaustedException} is thrown when
036 * we have more information about which rows were causing which exceptions on what servers. You can
037 * call {@link #mayHaveClusterIssues()} and if the result is false, you have input error problems,
038 * otherwise you may have cluster issues. You can iterate over the causes, rows and last known
039 * server addresses via {@link #getNumExceptions()} and {@link #getCause(int)}, {@link #getRow(int)}
040 * and {@link #getHostnamePort(int)}.
041 */
042@SuppressWarnings("serial")
043@InterfaceAudience.Public
044public class RetriesExhaustedWithDetailsException extends RetriesExhaustedException {
045  List<Throwable> exceptions;
046  List<Row> actions;
047  List<String> hostnameAndPort;
048
049  public RetriesExhaustedWithDetailsException(final String msg) {
050    super(msg);
051  }
052
053  public RetriesExhaustedWithDetailsException(final String msg, final IOException e) {
054    super(msg, e);
055  }
056
057  public RetriesExhaustedWithDetailsException(List<Throwable> exceptions, List<Row> actions,
058    List<String> hostnameAndPort) {
059    super("Failed " + exceptions.size() + " action" + pluralize(exceptions) + ": "
060      + getDesc(exceptions, actions, hostnameAndPort));
061
062    this.exceptions = exceptions;
063    this.actions = actions;
064    this.hostnameAndPort = hostnameAndPort;
065  }
066
067  public List<Throwable> getCauses() {
068    return exceptions;
069  }
070
071  public int getNumExceptions() {
072    return exceptions.size();
073  }
074
075  public Throwable getCause(int i) {
076    return exceptions.get(i);
077  }
078
079  public Row getRow(int i) {
080    return actions.get(i);
081  }
082
083  public String getHostnamePort(final int i) {
084    return this.hostnameAndPort.get(i);
085  }
086
087  public boolean mayHaveClusterIssues() {
088    boolean res = false;
089
090    // If all of the exceptions are DNRIOE not exception
091    for (Throwable t : exceptions) {
092      if (!(t instanceof DoNotRetryIOException)) {
093        res = true;
094      }
095    }
096    return res;
097  }
098
099  public static String pluralize(Collection<?> c) {
100    return pluralize(c.size());
101  }
102
103  public static String pluralize(int c) {
104    return c > 1 ? "s" : "";
105  }
106
107  public static String getDesc(List<Throwable> exceptions, List<? extends Row> actions,
108    List<String> hostnamePort) {
109    String s = getDesc(classifyExs(exceptions));
110    StringBuilder addrs = new StringBuilder(s);
111    addrs.append("servers with issues: ");
112    Set<String> uniqAddr = new HashSet<>(hostnamePort);
113
114    for (String addr : uniqAddr) {
115      addrs.append(addr).append(", ");
116    }
117    return uniqAddr.isEmpty() ? addrs.toString() : addrs.substring(0, addrs.length() - 2);
118  }
119
120  public String getExhaustiveDescription() {
121    StringWriter errorWriter = new StringWriter();
122    PrintWriter pw = new PrintWriter(errorWriter);
123    for (int i = 0; i < this.exceptions.size(); ++i) {
124      Throwable t = this.exceptions.get(i);
125      Row action = this.actions.get(i);
126      String server = this.hostnameAndPort.get(i);
127      pw.append("exception");
128      if (this.exceptions.size() > 1) {
129        pw.append(" #" + i);
130      }
131      pw.append(" from " + server + " for "
132        + ((action == null) ? "unknown key" : Bytes.toStringBinary(action.getRow())));
133      if (t != null) {
134        pw.println();
135        t.printStackTrace(pw);
136      }
137    }
138    pw.flush();
139    return errorWriter.toString();
140  }
141
142  public static Map<String, Integer> classifyExs(List<Throwable> ths) {
143    Map<String, Integer> cls = new HashMap<>();
144    for (Throwable t : ths) {
145      if (t == null) continue;
146      String name = "";
147      if (t instanceof DoNotRetryIOException || t instanceof RegionTooBusyException) {
148        // If RegionTooBusyException, print message since it has Region name in it.
149        // RegionTooBusyException message was edited to remove variance. Has regionname, server,
150        // and why the exception; no longer has duration it waited on lock nor current memsize.
151        name = t.getMessage();
152      } else {
153        name = t.getClass().getSimpleName();
154      }
155      Integer i = cls.get(name);
156      if (i == null) {
157        i = 0;
158      }
159      i += 1;
160      cls.put(name, i);
161    }
162    return cls;
163  }
164
165  public static String getDesc(Map<String, Integer> classificaton) {
166    StringBuilder classificatons = new StringBuilder(11);
167    for (Map.Entry<String, Integer> e : classificaton.entrySet()) {
168      classificatons.append(e.getKey());
169      classificatons.append(": ");
170      classificatons.append(e.getValue());
171      classificatons.append(" time");
172      classificatons.append(pluralize(e.getValue()));
173      classificatons.append(", ");
174    }
175    return classificatons.toString();
176  }
177}