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 java.util.stream.Collectors.toList; 021import static org.apache.hadoop.hbase.HConstants.EMPTY_END_ROW; 022import static org.apache.hadoop.hbase.HConstants.EMPTY_START_ROW; 023 024import java.io.IOException; 025import java.lang.reflect.UndeclaredThrowableException; 026import java.net.InetAddress; 027import java.net.UnknownHostException; 028import java.util.Arrays; 029import java.util.List; 030import java.util.concurrent.CompletableFuture; 031import java.util.concurrent.ExecutorService; 032import java.util.concurrent.ThreadLocalRandom; 033import java.util.concurrent.TimeUnit; 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.hbase.Cell; 036import org.apache.hadoop.hbase.CellComparator; 037import org.apache.hadoop.hbase.HConstants; 038import org.apache.hadoop.hbase.PrivateCellUtil; 039import org.apache.hadoop.hbase.ServerName; 040import org.apache.hadoop.hbase.TableName; 041import org.apache.hadoop.hbase.client.metrics.ScanMetrics; 042import org.apache.hadoop.hbase.ipc.HBaseRpcController; 043import org.apache.hadoop.hbase.security.User; 044import org.apache.hadoop.hbase.security.UserProvider; 045import org.apache.hadoop.hbase.util.Bytes; 046import org.apache.hadoop.hbase.util.ReflectionUtils; 047import org.apache.hadoop.ipc.RemoteException; 048import org.apache.hadoop.net.DNS; 049import org.apache.yetus.audience.InterfaceAudience; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052 053import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 054import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 055import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException; 056 057import org.apache.hadoop.hbase.shaded.protobuf.ResponseConverter; 058import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService; 059import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService; 060import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse; 061import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MasterService; 062 063/** 064 * Utility used by client connections. 065 */ 066@InterfaceAudience.Private 067public final class ConnectionUtils { 068 069 private static final Logger LOG = LoggerFactory.getLogger(ConnectionUtils.class); 070 071 private ConnectionUtils() { 072 } 073 074 /** 075 * Calculate pause time. Built on {@link HConstants#RETRY_BACKOFF}. 076 * @param pause time to pause 077 * @param tries amount of tries 078 * @return How long to wait after <code>tries</code> retries 079 */ 080 public static long getPauseTime(final long pause, final int tries) { 081 int ntries = tries; 082 if (ntries >= HConstants.RETRY_BACKOFF.length) { 083 ntries = HConstants.RETRY_BACKOFF.length - 1; 084 } 085 if (ntries < 0) { 086 ntries = 0; 087 } 088 089 long normalPause = pause * HConstants.RETRY_BACKOFF[ntries]; 090 // 1% possible jitter 091 long jitter = (long) (normalPause * ThreadLocalRandom.current().nextFloat() * 0.01f); 092 return normalPause + jitter; 093 } 094 095 /** 096 * @param conn The connection for which to replace the generator. 097 * @param cnm Replaces the nonce generator used, for testing. 098 * @return old nonce generator. 099 */ 100 public static NonceGenerator injectNonceGeneratorForTesting(ClusterConnection conn, 101 NonceGenerator cnm) { 102 return ConnectionImplementation.injectNonceGeneratorForTesting(conn, cnm); 103 } 104 105 /** 106 * Changes the configuration to set the number of retries needed when using Connection internally, 107 * e.g. for updating catalog tables, etc. Call this method before we create any Connections. 108 * @param c The Configuration instance to set the retries into. 109 * @param log Used to log what we set in here. 110 */ 111 public static void setServerSideHConnectionRetriesConfig(final Configuration c, final String sn, 112 final Logger log) { 113 // TODO: Fix this. Not all connections from server side should have 10 times the retries. 114 int hcRetries = c.getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 115 HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER); 116 // Go big. Multiply by 10. If we can't get to meta after this many retries 117 // then something seriously wrong. 118 int serversideMultiplier = c.getInt(HConstants.HBASE_CLIENT_SERVERSIDE_RETRIES_MULTIPLIER, 119 HConstants.DEFAULT_HBASE_CLIENT_SERVERSIDE_RETRIES_MULTIPLIER); 120 int retries = hcRetries * serversideMultiplier; 121 c.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, retries); 122 log.info(sn + " server-side Connection retries=" + retries); 123 } 124 125 /** 126 * A ClusterConnection that will short-circuit RPC making direct invocations against the 127 * localhost if the invocation target is 'this' server; save on network and protobuf 128 * invocations. 129 */ 130 // TODO This has to still do PB marshalling/unmarshalling stuff. Check how/whether we can avoid. 131 @VisibleForTesting // Class is visible so can assert we are short-circuiting when expected. 132 public static class ShortCircuitingClusterConnection extends ConnectionImplementation { 133 private final ServerName serverName; 134 private final AdminService.BlockingInterface localHostAdmin; 135 private final ClientService.BlockingInterface localHostClient; 136 137 private ShortCircuitingClusterConnection(Configuration conf, ExecutorService pool, User user, 138 ServerName serverName, AdminService.BlockingInterface admin, 139 ClientService.BlockingInterface client) 140 throws IOException { 141 super(conf, pool, user); 142 this.serverName = serverName; 143 this.localHostAdmin = admin; 144 this.localHostClient = client; 145 } 146 147 @Override 148 public AdminService.BlockingInterface getAdmin(ServerName sn) throws IOException { 149 return serverName.equals(sn) ? this.localHostAdmin : super.getAdmin(sn); 150 } 151 152 @Override 153 public ClientService.BlockingInterface getClient(ServerName sn) throws IOException { 154 return serverName.equals(sn) ? this.localHostClient : super.getClient(sn); 155 } 156 157 @Override 158 public MasterKeepAliveConnection getMaster() throws IOException { 159 if (this.localHostClient instanceof MasterService.BlockingInterface) { 160 return new ShortCircuitMasterConnection((MasterService.BlockingInterface)this.localHostClient); 161 } 162 return super.getMaster(); 163 } 164 } 165 166 /** 167 * Creates a short-circuit connection that can bypass the RPC layer (serialization, 168 * deserialization, networking, etc..) when talking to a local server. 169 * @param conf the current configuration 170 * @param pool the thread pool to use for batch operations 171 * @param user the user the connection is for 172 * @param serverName the local server name 173 * @param admin the admin interface of the local server 174 * @param client the client interface of the local server 175 * @return an short-circuit connection. 176 * @throws IOException if IO failure occurred 177 */ 178 public static ClusterConnection createShortCircuitConnection(final Configuration conf, 179 ExecutorService pool, User user, final ServerName serverName, 180 final AdminService.BlockingInterface admin, final ClientService.BlockingInterface client) 181 throws IOException { 182 if (user == null) { 183 user = UserProvider.instantiate(conf).getCurrent(); 184 } 185 return new ShortCircuitingClusterConnection(conf, pool, user, serverName, admin, client); 186 } 187 188 /** 189 * Setup the connection class, so that it will not depend on master being online. Used for testing 190 * @param conf configuration to set 191 */ 192 @VisibleForTesting 193 public static void setupMasterlessConnection(Configuration conf) { 194 conf.set(ClusterConnection.HBASE_CLIENT_CONNECTION_IMPL, MasterlessConnection.class.getName()); 195 } 196 197 /** 198 * Some tests shut down the master. But table availability is a master RPC which is performed on 199 * region re-lookups. 200 */ 201 static class MasterlessConnection extends ConnectionImplementation { 202 MasterlessConnection(Configuration conf, ExecutorService pool, User user) throws IOException { 203 super(conf, pool, user); 204 } 205 206 @Override 207 public boolean isTableDisabled(TableName tableName) throws IOException { 208 // treat all tables as enabled 209 return false; 210 } 211 } 212 213 /** 214 * Return retires + 1. The returned value will be in range [1, Integer.MAX_VALUE]. 215 */ 216 static int retries2Attempts(int retries) { 217 return Math.max(1, retries == Integer.MAX_VALUE ? Integer.MAX_VALUE : retries + 1); 218 } 219 220 /** 221 * Get a unique key for the rpc stub to the given server. 222 */ 223 static String getStubKey(String serviceName, ServerName serverName, boolean hostnameCanChange) { 224 // Sometimes, servers go down and they come back up with the same hostname but a different 225 // IP address. Force a resolution of the rsHostname by trying to instantiate an 226 // InetSocketAddress, and this way we will rightfully get a new stubKey. 227 // Also, include the hostname in the key so as to take care of those cases where the 228 // DNS name is different but IP address remains the same. 229 String hostname = serverName.getHostname(); 230 int port = serverName.getPort(); 231 if (hostnameCanChange) { 232 try { 233 InetAddress ip = InetAddress.getByName(hostname); 234 return serviceName + "@" + hostname + "-" + ip.getHostAddress() + ":" + port; 235 } catch (UnknownHostException e) { 236 LOG.warn("Can not resolve " + hostname + ", please check your network", e); 237 } 238 } 239 return serviceName + "@" + hostname + ":" + port; 240 } 241 242 static void checkHasFamilies(Mutation mutation) { 243 Preconditions.checkArgument(mutation.numFamilies() > 0, 244 "Invalid arguments to %s, zero columns specified", mutation.toString()); 245 } 246 247 /** Dummy nonce generator for disabled nonces. */ 248 static final NonceGenerator NO_NONCE_GENERATOR = new NonceGenerator() { 249 250 @Override 251 public long newNonce() { 252 return HConstants.NO_NONCE; 253 } 254 255 @Override 256 public long getNonceGroup() { 257 return HConstants.NO_NONCE; 258 } 259 }; 260 261 // A byte array in which all elements are the max byte, and it is used to 262 // construct closest front row 263 static final byte[] MAX_BYTE_ARRAY = Bytes.createMaxByteArray(9); 264 265 /** 266 * Create the closest row after the specified row 267 */ 268 static byte[] createClosestRowAfter(byte[] row) { 269 return Arrays.copyOf(row, row.length + 1); 270 } 271 272 /** 273 * Create a row before the specified row and very close to the specified row. 274 */ 275 static byte[] createCloseRowBefore(byte[] row) { 276 if (row.length == 0) { 277 return MAX_BYTE_ARRAY; 278 } 279 if (row[row.length - 1] == 0) { 280 return Arrays.copyOf(row, row.length - 1); 281 } else { 282 byte[] nextRow = new byte[row.length + MAX_BYTE_ARRAY.length]; 283 System.arraycopy(row, 0, nextRow, 0, row.length - 1); 284 nextRow[row.length - 1] = (byte) ((row[row.length - 1] & 0xFF) - 1); 285 System.arraycopy(MAX_BYTE_ARRAY, 0, nextRow, row.length, MAX_BYTE_ARRAY.length); 286 return nextRow; 287 } 288 } 289 290 static boolean isEmptyStartRow(byte[] row) { 291 return Bytes.equals(row, EMPTY_START_ROW); 292 } 293 294 static boolean isEmptyStopRow(byte[] row) { 295 return Bytes.equals(row, EMPTY_END_ROW); 296 } 297 298 static void resetController(HBaseRpcController controller, long timeoutNs) { 299 controller.reset(); 300 if (timeoutNs >= 0) { 301 controller.setCallTimeout( 302 (int) Math.min(Integer.MAX_VALUE, TimeUnit.NANOSECONDS.toMillis(timeoutNs))); 303 } 304 } 305 306 static Throwable translateException(Throwable t) { 307 if (t instanceof UndeclaredThrowableException && t.getCause() != null) { 308 t = t.getCause(); 309 } 310 if (t instanceof RemoteException) { 311 t = ((RemoteException) t).unwrapRemoteException(); 312 } 313 if (t instanceof ServiceException && t.getCause() != null) { 314 t = translateException(t.getCause()); 315 } 316 return t; 317 } 318 319 static long calcEstimatedSize(Result rs) { 320 long estimatedHeapSizeOfResult = 0; 321 // We don't make Iterator here 322 for (Cell cell : rs.rawCells()) { 323 estimatedHeapSizeOfResult += PrivateCellUtil.estimatedSizeOfCell(cell); 324 } 325 return estimatedHeapSizeOfResult; 326 } 327 328 static Result filterCells(Result result, Cell keepCellsAfter) { 329 if (keepCellsAfter == null) { 330 // do not need to filter 331 return result; 332 } 333 // not the same row 334 if (!PrivateCellUtil.matchingRows(keepCellsAfter, result.getRow(), 0, result.getRow().length)) { 335 return result; 336 } 337 Cell[] rawCells = result.rawCells(); 338 int index = 339 Arrays.binarySearch(rawCells, keepCellsAfter, CellComparator.getInstance()::compareWithoutRow); 340 if (index < 0) { 341 index = -index - 1; 342 } else { 343 index++; 344 } 345 if (index == 0) { 346 return result; 347 } 348 if (index == rawCells.length) { 349 return null; 350 } 351 return Result.create(Arrays.copyOfRange(rawCells, index, rawCells.length), null, 352 result.isStale(), result.mayHaveMoreCellsInRow()); 353 } 354 355 // Add a delta to avoid timeout immediately after a retry sleeping. 356 static final long SLEEP_DELTA_NS = TimeUnit.MILLISECONDS.toNanos(1); 357 358 static Get toCheckExistenceOnly(Get get) { 359 if (get.isCheckExistenceOnly()) { 360 return get; 361 } 362 return ReflectionUtils.newInstance(get.getClass(), get).setCheckExistenceOnly(true); 363 } 364 365 static List<Get> toCheckExistenceOnly(List<Get> gets) { 366 return gets.stream().map(ConnectionUtils::toCheckExistenceOnly).collect(toList()); 367 } 368 369 static RegionLocateType getLocateType(Scan scan) { 370 if (scan.isReversed()) { 371 if (isEmptyStartRow(scan.getStartRow())) { 372 return RegionLocateType.BEFORE; 373 } else { 374 return scan.includeStartRow() ? RegionLocateType.CURRENT : RegionLocateType.BEFORE; 375 } 376 } else { 377 return scan.includeStartRow() ? RegionLocateType.CURRENT : RegionLocateType.AFTER; 378 } 379 } 380 381 static boolean noMoreResultsForScan(Scan scan, RegionInfo info) { 382 if (isEmptyStopRow(info.getEndKey())) { 383 return true; 384 } 385 if (isEmptyStopRow(scan.getStopRow())) { 386 return false; 387 } 388 int c = Bytes.compareTo(info.getEndKey(), scan.getStopRow()); 389 // 1. if our stop row is less than the endKey of the region 390 // 2. if our stop row is equal to the endKey of the region and we do not include the stop row 391 // for scan. 392 return c > 0 || (c == 0 && !scan.includeStopRow()); 393 } 394 395 static boolean noMoreResultsForReverseScan(Scan scan, RegionInfo info) { 396 if (isEmptyStartRow(info.getStartKey())) { 397 return true; 398 } 399 if (isEmptyStopRow(scan.getStopRow())) { 400 return false; 401 } 402 // no need to test the inclusive of the stop row as the start key of a region is included in 403 // the region. 404 return Bytes.compareTo(info.getStartKey(), scan.getStopRow()) <= 0; 405 } 406 407 static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futures) { 408 return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) 409 .thenApply(v -> futures.stream().map(f -> f.getNow(null)).collect(toList())); 410 } 411 412 public static ScanResultCache createScanResultCache(Scan scan) { 413 if (scan.getAllowPartialResults()) { 414 return new AllowPartialScanResultCache(); 415 } else if (scan.getBatch() > 0) { 416 return new BatchScanResultCache(scan.getBatch()); 417 } else { 418 return new CompleteScanResultCache(); 419 } 420 } 421 422 private static final String MY_ADDRESS = getMyAddress(); 423 424 private static String getMyAddress() { 425 try { 426 return DNS.getDefaultHost("default", "default"); 427 } catch (UnknownHostException uhe) { 428 LOG.error("cannot determine my address", uhe); 429 return null; 430 } 431 } 432 433 static boolean isRemote(String host) { 434 return !host.equalsIgnoreCase(MY_ADDRESS); 435 } 436 437 static void incRPCCallsMetrics(ScanMetrics scanMetrics, boolean isRegionServerRemote) { 438 if (scanMetrics == null) { 439 return; 440 } 441 scanMetrics.countOfRPCcalls.incrementAndGet(); 442 if (isRegionServerRemote) { 443 scanMetrics.countOfRemoteRPCcalls.incrementAndGet(); 444 } 445 } 446 447 static void incRPCRetriesMetrics(ScanMetrics scanMetrics, boolean isRegionServerRemote) { 448 if (scanMetrics == null) { 449 return; 450 } 451 scanMetrics.countOfRPCRetries.incrementAndGet(); 452 if (isRegionServerRemote) { 453 scanMetrics.countOfRemoteRPCRetries.incrementAndGet(); 454 } 455 } 456 457 static void updateResultsMetrics(ScanMetrics scanMetrics, Result[] rrs, 458 boolean isRegionServerRemote) { 459 if (scanMetrics == null || rrs == null || rrs.length == 0) { 460 return; 461 } 462 long resultSize = 0; 463 for (Result rr : rrs) { 464 for (Cell cell : rr.rawCells()) { 465 resultSize += PrivateCellUtil.estimatedSerializedSizeOf(cell); 466 } 467 } 468 scanMetrics.countOfBytesInResults.addAndGet(resultSize); 469 if (isRegionServerRemote) { 470 scanMetrics.countOfBytesInRemoteResults.addAndGet(resultSize); 471 } 472 } 473 474 /** 475 * Use the scan metrics returned by the server to add to the identically named counters in the 476 * client side metrics. If a counter does not exist with the same name as the server side metric, 477 * the attempt to increase the counter will fail. 478 */ 479 static void updateServerSideMetrics(ScanMetrics scanMetrics, ScanResponse response) { 480 if (scanMetrics == null || response == null || !response.hasScanMetrics()) { 481 return; 482 } 483 ResponseConverter.getScanMetrics(response).forEach(scanMetrics::addToCounter); 484 } 485 486 static void incRegionCountMetrics(ScanMetrics scanMetrics) { 487 if (scanMetrics == null) { 488 return; 489 } 490 scanMetrics.countOfRegions.incrementAndGet(); 491 } 492}