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; 019 020import static org.apache.hadoop.hbase.TableName.META_TABLE_NAME; 021import static org.apache.hadoop.hbase.util.FutureUtils.addListener; 022 023import java.util.concurrent.CompletableFuture; 024import java.util.concurrent.TimeUnit; 025import java.util.function.Supplier; 026import org.apache.hadoop.hbase.HRegionLocation; 027import org.apache.hadoop.hbase.RegionLocations; 028import org.apache.hadoop.hbase.TableName; 029import org.apache.hadoop.hbase.exceptions.TimeoutIOException; 030import org.apache.hadoop.hbase.util.Bytes; 031import org.apache.hadoop.hbase.util.FutureUtils; 032import org.apache.yetus.audience.InterfaceAudience; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import org.apache.hbase.thirdparty.io.netty.util.HashedWheelTimer; 037import org.apache.hbase.thirdparty.io.netty.util.Timeout; 038 039/** 040 * The asynchronous region locator. 041 */ 042@InterfaceAudience.Private 043class AsyncRegionLocator { 044 045 private static final Logger LOG = LoggerFactory.getLogger(AsyncRegionLocator.class); 046 047 private final HashedWheelTimer retryTimer; 048 049 private final AsyncMetaRegionLocator metaRegionLocator; 050 051 private final AsyncNonMetaRegionLocator nonMetaRegionLocator; 052 053 AsyncRegionLocator(AsyncConnectionImpl conn, HashedWheelTimer retryTimer) { 054 this.metaRegionLocator = new AsyncMetaRegionLocator(conn.registry); 055 this.nonMetaRegionLocator = new AsyncNonMetaRegionLocator(conn); 056 this.retryTimer = retryTimer; 057 } 058 059 private <T> CompletableFuture<T> withTimeout(CompletableFuture<T> future, long timeoutNs, 060 Supplier<String> timeoutMsg) { 061 if (future.isDone() || timeoutNs <= 0) { 062 return future; 063 } 064 Timeout timeoutTask = retryTimer.newTimeout(t -> { 065 if (future.isDone()) { 066 return; 067 } 068 future.completeExceptionally(new TimeoutIOException(timeoutMsg.get())); 069 }, timeoutNs, TimeUnit.NANOSECONDS); 070 FutureUtils.addListener(future, (loc, error) -> { 071 if (error != null && error.getClass() != TimeoutIOException.class) { 072 // cancel timeout task if we are not completed by it. 073 timeoutTask.cancel(); 074 } 075 }); 076 return future; 077 } 078 079 private boolean isMeta(TableName tableName) { 080 return TableName.isMetaTableName(tableName); 081 } 082 083 CompletableFuture<RegionLocations> getRegionLocations(TableName tableName, byte[] row, 084 RegionLocateType type, boolean reload, long timeoutNs) { 085 CompletableFuture<RegionLocations> future = isMeta(tableName) 086 ? metaRegionLocator.getRegionLocations(RegionReplicaUtil.DEFAULT_REPLICA_ID, reload) 087 : nonMetaRegionLocator.getRegionLocations(tableName, row, 088 RegionReplicaUtil.DEFAULT_REPLICA_ID, type, reload); 089 return withTimeout(future, timeoutNs, 090 () -> "Timeout(" + TimeUnit.NANOSECONDS.toMillis(timeoutNs) + 091 "ms) waiting for region locations for " + tableName + ", row='" + 092 Bytes.toStringBinary(row) + "'"); 093 } 094 095 CompletableFuture<HRegionLocation> getRegionLocation(TableName tableName, byte[] row, 096 int replicaId, RegionLocateType type, boolean reload, long timeoutNs) { 097 // meta region can not be split right now so we always call the same method. 098 // Change it later if the meta table can have more than one regions. 099 CompletableFuture<HRegionLocation> future = new CompletableFuture<>(); 100 CompletableFuture<RegionLocations> locsFuture = 101 isMeta(tableName) ? metaRegionLocator.getRegionLocations(replicaId, reload) 102 : nonMetaRegionLocator.getRegionLocations(tableName, row, replicaId, type, reload); 103 addListener(locsFuture, (locs, error) -> { 104 if (error != null) { 105 future.completeExceptionally(error); 106 return; 107 } 108 HRegionLocation loc = locs.getRegionLocation(replicaId); 109 if (loc == null) { 110 future.completeExceptionally( 111 new RegionOfflineException("No location for " + tableName + ", row='" + 112 Bytes.toStringBinary(row) + "', locateType=" + type + ", replicaId=" + replicaId)); 113 } else if (loc.getServerName() == null) { 114 future.completeExceptionally( 115 new RegionOfflineException("No server address listed for region '" + 116 loc.getRegion().getRegionNameAsString() + ", row='" + Bytes.toStringBinary(row) + 117 "', locateType=" + type + ", replicaId=" + replicaId)); 118 } else { 119 future.complete(loc); 120 } 121 }); 122 return withTimeout(future, timeoutNs, 123 () -> "Timeout(" + TimeUnit.NANOSECONDS.toMillis(timeoutNs) + 124 "ms) waiting for region location for " + tableName + ", row='" + Bytes.toStringBinary(row) + 125 "', replicaId=" + replicaId); 126 } 127 128 CompletableFuture<HRegionLocation> getRegionLocation(TableName tableName, byte[] row, 129 int replicaId, RegionLocateType type, long timeoutNs) { 130 return getRegionLocation(tableName, row, replicaId, type, false, timeoutNs); 131 } 132 133 CompletableFuture<HRegionLocation> getRegionLocation(TableName tableName, byte[] row, 134 RegionLocateType type, boolean reload, long timeoutNs) { 135 return getRegionLocation(tableName, row, RegionReplicaUtil.DEFAULT_REPLICA_ID, type, reload, 136 timeoutNs); 137 } 138 139 CompletableFuture<HRegionLocation> getRegionLocation(TableName tableName, byte[] row, 140 RegionLocateType type, long timeoutNs) { 141 return getRegionLocation(tableName, row, type, false, timeoutNs); 142 } 143 144 void updateCachedLocationOnError(HRegionLocation loc, Throwable exception) { 145 if (loc.getRegion().isMetaRegion()) { 146 metaRegionLocator.updateCachedLocationOnError(loc, exception); 147 } else { 148 nonMetaRegionLocator.updateCachedLocationOnError(loc, exception); 149 } 150 } 151 152 void clearCache(TableName tableName) { 153 if (LOG.isDebugEnabled()) { 154 LOG.debug("Clear meta cache for " + tableName); 155 } 156 if (tableName.equals(META_TABLE_NAME)) { 157 metaRegionLocator.clearCache(); 158 } else { 159 nonMetaRegionLocator.clearCache(tableName); 160 } 161 } 162}