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}