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}