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.client.backoff; 019 020import static org.apache.hadoop.hbase.client.ConnectionUtils.SLEEP_DELTA_NS; 021import static org.apache.hadoop.hbase.client.ConnectionUtils.getPauseTime; 022 023import java.util.OptionalLong; 024import java.util.concurrent.TimeUnit; 025import org.apache.hadoop.hbase.HBaseServerException; 026import org.apache.hadoop.hbase.quotas.RpcThrottlingException; 027import org.apache.yetus.audience.InterfaceAudience; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031@InterfaceAudience.Private 032public class HBaseServerExceptionPauseManager { 033 private static final Logger LOG = LoggerFactory.getLogger(HBaseServerExceptionPauseManager.class); 034 035 private final long pauseNs; 036 private final long pauseNsForServerOverloaded; 037 private final long timeoutNs; 038 039 public HBaseServerExceptionPauseManager(long pauseNs, long pauseNsForServerOverloaded, 040 long timeoutNs) { 041 this.pauseNs = pauseNs; 042 this.pauseNsForServerOverloaded = pauseNsForServerOverloaded; 043 this.timeoutNs = timeoutNs; 044 } 045 046 /** 047 * Returns the nanos, if any, for which the client should wait 048 * @param error The exception from the server 049 * @param tries The current retry count 050 * @return The time, in nanos, to pause. If empty then pausing would exceed our timeout, so we 051 * should throw now 052 */ 053 public OptionalLong getPauseNsFromException(Throwable error, int tries, long startNs) { 054 long expectedSleepNs; 055 long remainingTimeNs = remainingTimeNs(startNs) - SLEEP_DELTA_NS; 056 if (error instanceof RpcThrottlingException) { 057 RpcThrottlingException rpcThrottlingException = (RpcThrottlingException) error; 058 expectedSleepNs = TimeUnit.MILLISECONDS.toNanos(rpcThrottlingException.getWaitInterval()); 059 if (expectedSleepNs > remainingTimeNs && remainingTimeNs > 0) { 060 if (LOG.isDebugEnabled()) { 061 LOG.debug("RpcThrottlingException suggested pause of {}ns which would exceed " 062 + "the timeout. We should throw instead.", expectedSleepNs, rpcThrottlingException); 063 } 064 return OptionalLong.empty(); 065 } 066 if (LOG.isDebugEnabled()) { 067 LOG.debug("Sleeping for {}ns after catching RpcThrottlingException", expectedSleepNs, 068 rpcThrottlingException); 069 } 070 } else { 071 expectedSleepNs = 072 HBaseServerException.isServerOverloaded(error) ? pauseNsForServerOverloaded : pauseNs; 073 // RpcThrottlingException tells us exactly how long the client should wait for, 074 // so we should not factor in the retry count for said exception 075 expectedSleepNs = getPauseTime(expectedSleepNs, tries - 1); 076 } 077 078 if (timeoutNs > 0) { 079 if (remainingTimeNs <= 0) { 080 return OptionalLong.empty(); 081 } 082 expectedSleepNs = Math.min(remainingTimeNs, expectedSleepNs); 083 } 084 085 return OptionalLong.of(expectedSleepNs); 086 } 087 088 public long remainingTimeNs(long startNs) { 089 return timeoutNs - (System.nanoTime() - startNs); 090 } 091 092}