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    AtomicRequestNumberExceeded,
045    AtomicReadSizeExceeded,
046    AtomicWriteSizeExceeded,
047    RequestHandlerUsageTimeExceeded,
048  }
049
050  private static final String[] MSG_TYPE = new String[] { "number of requests exceeded",
051    "request size limit exceeded", "number of read requests exceeded",
052    "number of write requests exceeded", "write size limit exceeded", "read size limit exceeded",
053    "request capacity unit exceeded", "read capacity unit exceeded", "write capacity unit exceeded",
054    "atomic request number exceeded", "atomic read size exceeded", "atomic write size exceeded",
055    "request handler usage time exceeded" };
056
057  private static final String MSG_WAIT = " - wait ";
058
059  private long waitInterval;
060  private Type type;
061
062  public RpcThrottlingException(String msg) {
063    super(msg);
064
065    // Dirty workaround to get the information after
066    // ((RemoteException)e.getCause()).unwrapRemoteException()
067    for (int i = 0; i < MSG_TYPE.length; ++i) {
068      int index = msg.indexOf(MSG_TYPE[i]);
069      if (index >= 0) {
070        String waitTimeStr = msg.substring(index + MSG_TYPE[i].length() + MSG_WAIT.length());
071        type = Type.values()[i];
072        waitInterval = timeFromString(waitTimeStr);
073        break;
074      }
075    }
076  }
077
078  public RpcThrottlingException(final Type type, final long waitInterval, final String msg) {
079    super(msg);
080    this.waitInterval = waitInterval;
081    this.type = type;
082  }
083
084  public Type getType() {
085    return this.type;
086  }
087
088  public long getWaitInterval() {
089    return this.waitInterval;
090  }
091
092  public static void throwNumRequestsExceeded(final long waitInterval)
093    throws RpcThrottlingException {
094    throwThrottlingException(Type.NumRequestsExceeded, waitInterval);
095  }
096
097  public static void throwRequestSizeExceeded(final long waitInterval)
098    throws RpcThrottlingException {
099    throwThrottlingException(Type.RequestSizeExceeded, waitInterval);
100  }
101
102  public static void throwNumReadRequestsExceeded(final long waitInterval)
103    throws RpcThrottlingException {
104    throwThrottlingException(Type.NumReadRequestsExceeded, waitInterval);
105  }
106
107  public static void throwNumWriteRequestsExceeded(final long waitInterval)
108    throws RpcThrottlingException {
109    throwThrottlingException(Type.NumWriteRequestsExceeded, waitInterval);
110  }
111
112  public static void throwWriteSizeExceeded(final long waitInterval) throws RpcThrottlingException {
113    throwThrottlingException(Type.WriteSizeExceeded, waitInterval);
114  }
115
116  public static void throwReadSizeExceeded(final long waitInterval) throws RpcThrottlingException {
117    throwThrottlingException(Type.ReadSizeExceeded, waitInterval);
118  }
119
120  public static void throwRequestCapacityUnitExceeded(final long waitInterval)
121    throws RpcThrottlingException {
122    throwThrottlingException(Type.RequestCapacityUnitExceeded, waitInterval);
123  }
124
125  public static void throwReadCapacityUnitExceeded(final long waitInterval)
126    throws RpcThrottlingException {
127    throwThrottlingException(Type.ReadCapacityUnitExceeded, waitInterval);
128  }
129
130  public static void throwWriteCapacityUnitExceeded(final long waitInterval)
131    throws RpcThrottlingException {
132    throwThrottlingException(Type.WriteCapacityUnitExceeded, waitInterval);
133  }
134
135  public static void throwAtomicRequestNumberExceeded(final long waitInterval)
136    throws RpcThrottlingException {
137    throwThrottlingException(Type.AtomicRequestNumberExceeded, waitInterval);
138  }
139
140  public static void throwAtomicReadSizeExceeded(final long waitInterval)
141    throws RpcThrottlingException {
142    throwThrottlingException(Type.AtomicReadSizeExceeded, waitInterval);
143  }
144
145  public static void throwAtomicWriteSizeExceeded(final long waitInterval)
146    throws RpcThrottlingException {
147    throwThrottlingException(Type.AtomicWriteSizeExceeded, waitInterval);
148  }
149
150  public static void throwRequestHandlerUsageTimeExceeded(final long waitInterval)
151    throws RpcThrottlingException {
152    throwThrottlingException(Type.RequestHandlerUsageTimeExceeded, waitInterval);
153  }
154
155  private static void throwThrottlingException(final Type type, final long waitInterval)
156    throws RpcThrottlingException {
157    String msg = MSG_TYPE[type.ordinal()] + MSG_WAIT + stringFromMillis(waitInterval);
158    throw new RpcThrottlingException(type, waitInterval, msg);
159  }
160
161  // Visible for TestRpcThrottlingException
162  protected static String stringFromMillis(long millis) {
163    StringBuilder buf = new StringBuilder();
164    long hours = millis / (60 * 60 * 1000);
165    long rem = (millis % (60 * 60 * 1000));
166    long minutes = rem / (60 * 1000);
167    rem = rem % (60 * 1000);
168    long seconds = rem / 1000;
169    long milliseconds = rem % 1000;
170
171    if (hours != 0) {
172      buf.append(hours);
173      buf.append(hours > 1 ? "hrs, " : "hr, ");
174    }
175    if (minutes != 0) {
176      buf.append(minutes);
177      buf.append(minutes > 1 ? "mins, " : "min, ");
178    }
179    if (seconds != 0) {
180      buf.append(seconds);
181      buf.append("sec, ");
182    }
183    buf.append(milliseconds);
184    buf.append("ms");
185    return buf.toString();
186  }
187
188  // Visible for TestRpcThrottlingException
189  protected static long timeFromString(String timeDiff) {
190    Pattern pattern =
191      Pattern.compile("^(?:(\\d+)hrs?, )?(?:(\\d+)mins?, )?(?:(\\d+)sec[, ]{0,2})?(?:(\\d+)ms)?");
192    long[] factors = new long[] { 60 * 60 * 1000, 60 * 1000, 1000, 1 };
193    Matcher m = pattern.matcher(timeDiff);
194    if (m.find()) {
195      int numGroups = m.groupCount();
196      long time = 0;
197      for (int j = 1; j <= numGroups; j++) {
198        String group = m.group(j);
199        if (group == null) {
200          continue;
201        }
202        time += Math.round(Float.parseFloat(group) * factors[j - 1]);
203      }
204      return time;
205    }
206    return -1;
207  }
208}