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