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.quotas;
019
020import java.util.regex.Matcher;
021import java.util.regex.Pattern;
022import org.apache.hadoop.hbase.HBaseIOException;
023import org.apache.yetus.audience.InterfaceAudience;
024
025/**
026 * Describe the throttling result. TODO: At some point this will be handled on the client side to
027 * prevent operation to go on the server if the waitInterval is greater than the one got as result
028 * of this exception.
029 */
030@InterfaceAudience.Public
031public class RpcThrottlingException extends HBaseIOException {
032
033  @InterfaceAudience.Public
034  public enum Type {
035    NumRequestsExceeded,
036    RequestSizeExceeded,
037    NumReadRequestsExceeded,
038    NumWriteRequestsExceeded,
039    WriteSizeExceeded,
040    ReadSizeExceeded,
041    RequestCapacityUnitExceeded,
042    ReadCapacityUnitExceeded,
043    WriteCapacityUnitExceeded
044  }
045
046  private static final String[] MSG_TYPE =
047    new String[] { "number of requests exceeded", "request size limit exceeded",
048      "number of read requests exceeded", "number of write requests exceeded",
049      "write size limit exceeded", "read size limit exceeded", "request capacity unit exceeded",
050      "read capacity unit exceeded", "write capacity unit exceeded" };
051
052  private static final String MSG_WAIT = " - wait ";
053
054  private long waitInterval;
055  private Type type;
056
057  public RpcThrottlingException(String msg) {
058    super(msg);
059
060    // Dirty workaround to get the information after
061    // ((RemoteException)e.getCause()).unwrapRemoteException()
062    for (int i = 0; i < MSG_TYPE.length; ++i) {
063      int index = msg.indexOf(MSG_TYPE[i]);
064      if (index >= 0) {
065        String waitTimeStr = msg.substring(index + MSG_TYPE[i].length() + MSG_WAIT.length());
066        type = Type.values()[i];
067        waitInterval = timeFromString(waitTimeStr);
068        break;
069      }
070    }
071  }
072
073  public RpcThrottlingException(final Type type, final long waitInterval, final String msg) {
074    super(msg);
075    this.waitInterval = waitInterval;
076    this.type = type;
077  }
078
079  public Type getType() {
080    return this.type;
081  }
082
083  public long getWaitInterval() {
084    return this.waitInterval;
085  }
086
087  public static void throwNumRequestsExceeded(final long waitInterval)
088    throws RpcThrottlingException {
089    throwThrottlingException(Type.NumRequestsExceeded, waitInterval);
090  }
091
092  public static void throwRequestSizeExceeded(final long waitInterval)
093    throws RpcThrottlingException {
094    throwThrottlingException(Type.RequestSizeExceeded, waitInterval);
095  }
096
097  public static void throwNumReadRequestsExceeded(final long waitInterval)
098    throws RpcThrottlingException {
099    throwThrottlingException(Type.NumReadRequestsExceeded, waitInterval);
100  }
101
102  public static void throwNumWriteRequestsExceeded(final long waitInterval)
103    throws RpcThrottlingException {
104    throwThrottlingException(Type.NumWriteRequestsExceeded, waitInterval);
105  }
106
107  public static void throwWriteSizeExceeded(final long waitInterval) throws RpcThrottlingException {
108    throwThrottlingException(Type.WriteSizeExceeded, waitInterval);
109  }
110
111  public static void throwReadSizeExceeded(final long waitInterval) throws RpcThrottlingException {
112    throwThrottlingException(Type.ReadSizeExceeded, waitInterval);
113  }
114
115  public static void throwRequestCapacityUnitExceeded(final long waitInterval)
116    throws RpcThrottlingException {
117    throwThrottlingException(Type.RequestCapacityUnitExceeded, waitInterval);
118  }
119
120  public static void throwReadCapacityUnitExceeded(final long waitInterval)
121    throws RpcThrottlingException {
122    throwThrottlingException(Type.ReadCapacityUnitExceeded, waitInterval);
123  }
124
125  public static void throwWriteCapacityUnitExceeded(final long waitInterval)
126    throws RpcThrottlingException {
127    throwThrottlingException(Type.WriteCapacityUnitExceeded, waitInterval);
128  }
129
130  private static void throwThrottlingException(final Type type, final long waitInterval)
131    throws RpcThrottlingException {
132    String msg = MSG_TYPE[type.ordinal()] + MSG_WAIT + stringFromMillis(waitInterval);
133    throw new RpcThrottlingException(type, waitInterval, msg);
134  }
135
136  // Visible for TestRpcThrottlingException
137  protected static String stringFromMillis(long millis) {
138    StringBuilder buf = new StringBuilder();
139    long hours = millis / (60 * 60 * 1000);
140    long rem = (millis % (60 * 60 * 1000));
141    long minutes = rem / (60 * 1000);
142    rem = rem % (60 * 1000);
143    long seconds = rem / 1000;
144    long milliseconds = rem % 1000;
145
146    if (hours != 0) {
147      buf.append(hours);
148      buf.append(hours > 1 ? "hrs, " : "hr, ");
149    }
150    if (minutes != 0) {
151      buf.append(minutes);
152      buf.append(minutes > 1 ? "mins, " : "min, ");
153    }
154    if (seconds != 0) {
155      buf.append(seconds);
156      buf.append("sec, ");
157    }
158    buf.append(milliseconds);
159    buf.append("ms");
160    return buf.toString();
161  }
162
163  // Visible for TestRpcThrottlingException
164  protected static long timeFromString(String timeDiff) {
165    Pattern pattern =
166      Pattern.compile("^(?:(\\d+)hrs?, )?(?:(\\d+)mins?, )?(?:(\\d+)sec[, ]{0,2})?(?:(\\d+)ms)?");
167    long[] factors = new long[] { 60 * 60 * 1000, 60 * 1000, 1000, 1 };
168    Matcher m = pattern.matcher(timeDiff);
169    if (m.find()) {
170      int numGroups = m.groupCount();
171      long time = 0;
172      for (int j = 1; j <= numGroups; j++) {
173        String group = m.group(j);
174        if (group == null) {
175          continue;
176        }
177        time += Math.round(Float.parseFloat(group) * factors[j - 1]);
178      }
179      return time;
180    }
181    return -1;
182  }
183}