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, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.hadoop.hbase.client; 020 021import java.io.IOException; 022import java.time.Instant; 023import java.time.format.DateTimeFormatter; 024import java.util.List; 025import java.util.StringJoiner; 026 027import org.apache.commons.lang3.StringUtils; 028import org.apache.yetus.audience.InterfaceAudience; 029 030/** 031 * Exception thrown by HTable methods when an attempt to do something (like 032 * commit changes) fails after a bunch of retries. 033 */ 034@InterfaceAudience.Public 035public class RetriesExhaustedException extends IOException { 036 private static final long serialVersionUID = 1876775844L; 037 038 public RetriesExhaustedException(final String msg) { 039 super(msg); 040 } 041 042 public RetriesExhaustedException(final String msg, final IOException e) { 043 super(msg, e); 044 } 045 046 /** 047 * Data structure that allows adding more info around Throwable incident. 048 */ 049 @InterfaceAudience.Private 050 public static class ThrowableWithExtraContext { 051 private final Throwable throwable; 052 private final long whenAsEpochMilli; 053 private final String extras; 054 055 public ThrowableWithExtraContext(final Throwable throwable, final long whenAsEpochMilli, 056 final String extras) { 057 this.throwable = throwable; 058 this.whenAsEpochMilli = whenAsEpochMilli; 059 this.extras = extras; 060 } 061 062 @Override 063 public String toString() { 064 final StringJoiner joiner = new StringJoiner(", "); 065 if (whenAsEpochMilli != 0) { 066 joiner.add(DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(whenAsEpochMilli))); 067 } 068 if (StringUtils.isNotEmpty(extras)) { 069 joiner.add(extras); 070 } 071 if (throwable != null) { 072 joiner.add(throwable.toString()); 073 } 074 return joiner.toString(); 075 } 076 } 077 078 /** 079 * Create a new RetriesExhaustedException from the list of prior failures. 080 * @param callableVitals Details from the Callable we were using 081 * when we got this exception. 082 * @param numTries The number of tries we made 083 * @param exceptions List of exceptions that failed before giving up 084 */ 085 public RetriesExhaustedException(final String callableVitals, int numTries, 086 List<Throwable> exceptions) { 087 super(getMessage(callableVitals, numTries, exceptions)); 088 } 089 090 /** 091 * Create a new RetriesExhaustedException from the list of prior failures. 092 * @param numRetries How many times we have retried, one less than total attempts 093 * @param exceptions List of exceptions that failed before giving up 094 */ 095 @InterfaceAudience.Private 096 public RetriesExhaustedException(final int numRetries, 097 final List<ThrowableWithExtraContext> exceptions) { 098 super(getMessage(numRetries, exceptions), 099 exceptions.isEmpty()? null: exceptions.get(exceptions.size() - 1).throwable); 100 } 101 102 private static String getMessage(String callableVitals, int numTries, 103 List<Throwable> exceptions) { 104 StringBuilder buffer = new StringBuilder("Failed contacting "); 105 buffer.append(callableVitals); 106 buffer.append(" after "); 107 buffer.append(numTries); 108 buffer.append(" attempts.\nExceptions:\n"); 109 for (Throwable t : exceptions) { 110 buffer.append(t.toString()); 111 buffer.append("\n"); 112 } 113 return buffer.toString(); 114 } 115 116 private static String getMessage(final int numRetries, 117 final List<ThrowableWithExtraContext> exceptions) { 118 StringBuilder buffer = new StringBuilder("Failed after attempts="); 119 buffer.append(numRetries + 1); 120 buffer.append(", exceptions:\n"); 121 for (ThrowableWithExtraContext t : exceptions) { 122 buffer.append(t.toString()); 123 buffer.append("\n"); 124 } 125 return buffer.toString(); 126 } 127}