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}