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<>();
121    uniqAddr.addAll(hostnamePort);
122
123    for (String addr : uniqAddr) {
124      addrs.append(addr).append(", ");
125    }
126    return uniqAddr.isEmpty() ? addrs.toString() : addrs.substring(0, addrs.length() - 2);
127  }
128
129  public String getExhaustiveDescription() {
130    StringWriter errorWriter = new StringWriter();
131    PrintWriter pw = new PrintWriter(errorWriter);
132    for (int i = 0; i < this.exceptions.size(); ++i) {
133      Throwable t = this.exceptions.get(i);
134      Row action = this.actions.get(i);
135      String server = this.hostnameAndPort.get(i);
136      pw.append("exception");
137      if (this.exceptions.size() > 1) {
138        pw.append(" #" + i);
139      }
140      pw.append(" from " + server + " for "
141        + ((action == null) ? "unknown key" : Bytes.toStringBinary(action.getRow())));
142      if (t != null) {
143        pw.println();
144        t.printStackTrace(pw);
145      }
146    }
147    pw.flush();
148    return errorWriter.toString();
149  }
150
151
152  public static Map<String, Integer> classifyExs(List<Throwable> ths) {
153    Map<String, Integer> cls = new HashMap<>();
154    for (Throwable t : ths) {
155      if (t == null) continue;
156      String name = "";
157      if (t instanceof DoNotRetryIOException ||
158          t instanceof RegionTooBusyException) {
159        // If RegionTooBusyException, print message since it has Region name in it.
160        // RegionTooBusyException message was edited to remove variance. Has regionname, server,
161        // and why the exception; no longer has duration it waited on lock nor current memsize.
162        name = t.getMessage();
163      } else {
164        name = t.getClass().getSimpleName();
165      }
166      Integer i = cls.get(name);
167      if (i == null) {
168        i = 0;
169      }
170      i += 1;
171      cls.put(name, i);
172    }
173    return cls;
174  }
175
176  public static String getDesc(Map<String,Integer> classificaton) {
177    StringBuilder classificatons =new StringBuilder(11);
178    for (Map.Entry<String, Integer> e : classificaton.entrySet()) {
179      classificatons.append(e.getKey());
180      classificatons.append(": ");
181      classificatons.append(e.getValue());
182      classificatons.append(" time");
183      classificatons.append(pluralize(e.getValue()));
184      classificatons.append(", ");
185    }
186    return classificatons.toString();
187  }
188}