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