001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
003 * agreements. See the NOTICE file distributed with this work for additional information regarding
004 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
005 * "License"); you may not use this file except in compliance with the License. You may obtain a
006 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
007 * law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
008 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
009 * for the specific language governing permissions and limitations under the License.
010 */
011
012package org.apache.hadoop.hbase.quotas;
013
014import java.util.regex.Matcher;
015import java.util.regex.Pattern;
016import org.apache.hadoop.hbase.HBaseIOException;
017import org.apache.hadoop.util.StringUtils;
018
019import org.apache.yetus.audience.InterfaceAudience;
020
021/**
022 * Describe the throttling result. TODO: At some point this will be handled on the client side to
023 * prevent operation to go on the server if the waitInterval is greater than the one got as result
024 * of this exception.
025 */
026@InterfaceAudience.Public
027public class RpcThrottlingException extends HBaseIOException {
028
029  @InterfaceAudience.Public
030  public enum Type {
031    NumRequestsExceeded, RequestSizeExceeded, NumReadRequestsExceeded, NumWriteRequestsExceeded,
032    WriteSizeExceeded, ReadSizeExceeded, RequestCapacityUnitExceeded, ReadCapacityUnitExceeded,
033    WriteCapacityUnitExceeded
034  }
035
036  private static final String[] MSG_TYPE =
037      new String[] { "number of requests exceeded", "request size limit exceeded",
038        "number of read requests exceeded", "number of write requests exceeded",
039        "write size limit exceeded", "read size limit exceeded", "request capacity unit exceeded",
040        "read capacity unit exceeded", "write capacity unit exceeded" };
041
042  private static final String MSG_WAIT = " - wait ";
043
044  private long waitInterval;
045  private Type type;
046
047  public RpcThrottlingException(String msg) {
048    super(msg);
049
050    // Dirty workaround to get the information after
051    // ((RemoteException)e.getCause()).unwrapRemoteException()
052    for (int i = 0; i < MSG_TYPE.length; ++i) {
053      int index = msg.indexOf(MSG_TYPE[i]);
054      if (index >= 0) {
055        String waitTimeStr = msg.substring(index + MSG_TYPE[i].length() + MSG_WAIT.length());
056        type = Type.values()[i];
057        waitInterval = timeFromString(waitTimeStr);
058        break;
059      }
060    }
061  }
062
063  public RpcThrottlingException(final Type type, final long waitInterval, final String msg) {
064    super(msg);
065    this.waitInterval = waitInterval;
066    this.type = type;
067  }
068
069  public Type getType() {
070    return this.type;
071  }
072
073  public long getWaitInterval() {
074    return this.waitInterval;
075  }
076
077  public static void throwNumRequestsExceeded(final long waitInterval) throws
078      RpcThrottlingException {
079    throwThrottlingException(Type.NumRequestsExceeded, waitInterval);
080  }
081
082  public static void throwRequestSizeExceeded(final long waitInterval)
083      throws RpcThrottlingException {
084    throwThrottlingException(Type.RequestSizeExceeded, waitInterval);
085  }
086
087  public static void throwNumReadRequestsExceeded(final long waitInterval)
088      throws RpcThrottlingException {
089    throwThrottlingException(Type.NumReadRequestsExceeded, waitInterval);
090  }
091
092  public static void throwNumWriteRequestsExceeded(final long waitInterval)
093      throws RpcThrottlingException {
094    throwThrottlingException(Type.NumWriteRequestsExceeded, waitInterval);
095  }
096
097  public static void throwWriteSizeExceeded(final long waitInterval) throws RpcThrottlingException {
098    throwThrottlingException(Type.WriteSizeExceeded, waitInterval);
099  }
100
101  public static void throwReadSizeExceeded(final long waitInterval) throws RpcThrottlingException {
102    throwThrottlingException(Type.ReadSizeExceeded, waitInterval);
103  }
104
105  public static void throwRequestCapacityUnitExceeded(final long waitInterval)
106      throws RpcThrottlingException {
107    throwThrottlingException(Type.RequestCapacityUnitExceeded, waitInterval);
108  }
109
110  public static void throwReadCapacityUnitExceeded(final long waitInterval)
111      throws RpcThrottlingException {
112    throwThrottlingException(Type.ReadCapacityUnitExceeded, waitInterval);
113  }
114
115  public static void throwWriteCapacityUnitExceeded(final long waitInterval)
116      throws RpcThrottlingException {
117    throwThrottlingException(Type.WriteCapacityUnitExceeded, waitInterval);
118  }
119
120  private static void throwThrottlingException(final Type type, final long waitInterval)
121      throws RpcThrottlingException {
122    String msg = MSG_TYPE[type.ordinal()] + MSG_WAIT + StringUtils.formatTime(waitInterval);
123    throw new RpcThrottlingException(type, waitInterval, msg);
124  }
125
126  private static long timeFromString(String timeDiff) {
127    Pattern[] patterns =
128        new Pattern[] { Pattern.compile("^(\\d+\\.\\d\\d)sec"),
129            Pattern.compile("^(\\d+)mins, (\\d+\\.\\d\\d)sec"),
130            Pattern.compile("^(\\d+)hrs, (\\d+)mins, (\\d+\\.\\d\\d)sec") };
131
132    for (int i = 0; i < patterns.length; ++i) {
133      Matcher m = patterns[i].matcher(timeDiff);
134      if (m.find()) {
135        long time = Math.round(Float.parseFloat(m.group(1 + i)) * 1000);
136        if (i > 0) {
137          time += Long.parseLong(m.group(i)) * (60 * 1000);
138        }
139        if (i > 1) {
140          time += Long.parseLong(m.group(i - 1)) * (60 * 60 * 1000);
141        }
142        return time;
143      }
144    }
145
146    return -1;
147  }
148}