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