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