001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.client; 020 021// DO NOT MAKE USE OF THESE IMPORTS! THEY ARE HERE FOR COPROCESSOR ENDPOINTS ONLY. 022// Internally, we use shaded protobuf. This below are part of our public API. 023//SEE ABOVE NOTE! 024import com.google.protobuf.Descriptors; 025import com.google.protobuf.Message; 026import com.google.protobuf.Service; 027import com.google.protobuf.ServiceException; 028 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.Cell; 031import org.apache.hadoop.hbase.CompareOperator; 032import org.apache.hadoop.hbase.HConstants; 033import org.apache.hadoop.hbase.HRegionLocation; 034import org.apache.hadoop.hbase.HTableDescriptor; 035import org.apache.hadoop.hbase.KeyValueUtil; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.io.TimeRange; 038import org.apache.yetus.audience.InterfaceAudience; 039import org.apache.yetus.audience.InterfaceStability; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042import org.apache.hadoop.hbase.client.coprocessor.Batch; 043import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback; 044import org.apache.hadoop.hbase.filter.BinaryComparator; 045import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; 046import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; 047import org.apache.hadoop.hbase.ipc.RpcControllerFactory; 048import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 049import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 050import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 051import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter; 052import org.apache.hadoop.hbase.shaded.protobuf.ResponseConverter; 053import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos; 054import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MultiRequest; 055import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MutateRequest; 056import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MutateResponse; 057import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionAction; 058import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.CompareType; 059import org.apache.hadoop.hbase.util.Bytes; 060import org.apache.hadoop.hbase.util.Pair; 061import org.apache.hadoop.hbase.util.ReflectionUtils; 062import org.apache.hadoop.hbase.util.Threads; 063 064import java.io.IOException; 065import java.io.InterruptedIOException; 066import java.util.ArrayList; 067import java.util.Collections; 068import java.util.List; 069import java.util.Map; 070import java.util.TreeMap; 071import java.util.concurrent.Callable; 072import java.util.concurrent.ExecutionException; 073import java.util.concurrent.ExecutorService; 074import java.util.concurrent.Future; 075import java.util.concurrent.SynchronousQueue; 076import java.util.concurrent.ThreadPoolExecutor; 077import java.util.concurrent.TimeUnit; 078 079import static org.apache.hadoop.hbase.client.ConnectionUtils.checkHasFamilies; 080 081/** 082 * An implementation of {@link Table}. Used to communicate with a single HBase table. 083 * Lightweight. Get as needed and just close when done. 084 * Instances of this class SHOULD NOT be constructed directly. 085 * Obtain an instance via {@link Connection}. See {@link ConnectionFactory} 086 * class comment for an example of how. 087 * 088 * <p>This class is thread safe since 2.0.0 if not invoking any of the setter methods. 089 * All setters are moved into {@link TableBuilder} and reserved here only for keeping 090 * backward compatibility, and TODO will be removed soon. 091 * 092 * <p>HTable is no longer a client API. Use {@link Table} instead. It is marked 093 * InterfaceAudience.Private indicating that this is an HBase-internal class as defined in 094 * <a href="https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/InterfaceClassification.html">Hadoop 095 * Interface Classification</a> 096 * There are no guarantees for backwards source / binary compatibility and methods or class can 097 * change or go away without deprecation. 098 * 099 * @see Table 100 * @see Admin 101 * @see Connection 102 * @see ConnectionFactory 103 */ 104@InterfaceAudience.Private 105@InterfaceStability.Stable 106public class HTable implements Table { 107 private static final Logger LOG = LoggerFactory.getLogger(HTable.class); 108 private static final Consistency DEFAULT_CONSISTENCY = Consistency.STRONG; 109 private final ClusterConnection connection; 110 private final TableName tableName; 111 private final Configuration configuration; 112 private final ConnectionConfiguration connConfiguration; 113 private boolean closed = false; 114 private final int scannerCaching; 115 private final long scannerMaxResultSize; 116 private final ExecutorService pool; // For Multi & Scan 117 private int operationTimeoutMs; // global timeout for each blocking method with retrying rpc 118 private final int rpcTimeoutMs; // FIXME we should use this for rpc like batch and checkAndXXX 119 private int readRpcTimeoutMs; // timeout for each read rpc request 120 private int writeRpcTimeoutMs; // timeout for each write rpc request 121 private final boolean cleanupPoolOnClose; // shutdown the pool in close() 122 private final HRegionLocator locator; 123 124 /** The Async process for batch */ 125 @VisibleForTesting 126 AsyncProcess multiAp; 127 private final RpcRetryingCallerFactory rpcCallerFactory; 128 private final RpcControllerFactory rpcControllerFactory; 129 130 // Marked Private @since 1.0 131 @InterfaceAudience.Private 132 public static ThreadPoolExecutor getDefaultExecutor(Configuration conf) { 133 int maxThreads = conf.getInt("hbase.htable.threads.max", Integer.MAX_VALUE); 134 if (maxThreads == 0) { 135 maxThreads = 1; // is there a better default? 136 } 137 int corePoolSize = conf.getInt("hbase.htable.threads.coresize", 1); 138 long keepAliveTime = conf.getLong("hbase.htable.threads.keepalivetime", 60); 139 140 // Using the "direct handoff" approach, new threads will only be created 141 // if it is necessary and will grow unbounded. This could be bad but in HCM 142 // we only create as many Runnables as there are region servers. It means 143 // it also scales when new region servers are added. 144 ThreadPoolExecutor pool = new ThreadPoolExecutor(corePoolSize, maxThreads, keepAliveTime, 145 TimeUnit.SECONDS, new SynchronousQueue<>(), Threads.newDaemonThreadFactory("htable")); 146 pool.allowCoreThreadTimeOut(true); 147 return pool; 148 } 149 150 /** 151 * Creates an object to access a HBase table. 152 * Used by HBase internally. DO NOT USE. See {@link ConnectionFactory} class comment for how to 153 * get a {@link Table} instance (use {@link Table} instead of {@link HTable}). 154 * @param connection Connection to be used. 155 * @param builder The table builder 156 * @param rpcCallerFactory The RPC caller factory 157 * @param rpcControllerFactory The RPC controller factory 158 * @param pool ExecutorService to be used. 159 */ 160 @InterfaceAudience.Private 161 protected HTable(final ClusterConnection connection, 162 final TableBuilderBase builder, 163 final RpcRetryingCallerFactory rpcCallerFactory, 164 final RpcControllerFactory rpcControllerFactory, 165 final ExecutorService pool) { 166 this.connection = Preconditions.checkNotNull(connection, "connection is null"); 167 this.configuration = connection.getConfiguration(); 168 this.connConfiguration = connection.getConnectionConfiguration(); 169 if (pool == null) { 170 this.pool = getDefaultExecutor(this.configuration); 171 this.cleanupPoolOnClose = true; 172 } else { 173 this.pool = pool; 174 this.cleanupPoolOnClose = false; 175 } 176 if (rpcCallerFactory == null) { 177 this.rpcCallerFactory = connection.getNewRpcRetryingCallerFactory(configuration); 178 } else { 179 this.rpcCallerFactory = rpcCallerFactory; 180 } 181 182 if (rpcControllerFactory == null) { 183 this.rpcControllerFactory = RpcControllerFactory.instantiate(configuration); 184 } else { 185 this.rpcControllerFactory = rpcControllerFactory; 186 } 187 188 this.tableName = builder.tableName; 189 this.operationTimeoutMs = builder.operationTimeout; 190 this.rpcTimeoutMs = builder.rpcTimeout; 191 this.readRpcTimeoutMs = builder.readRpcTimeout; 192 this.writeRpcTimeoutMs = builder.writeRpcTimeout; 193 this.scannerCaching = connConfiguration.getScannerCaching(); 194 this.scannerMaxResultSize = connConfiguration.getScannerMaxResultSize(); 195 196 // puts need to track errors globally due to how the APIs currently work. 197 multiAp = this.connection.getAsyncProcess(); 198 this.locator = new HRegionLocator(tableName, connection); 199 } 200 201 /** 202 * @return maxKeyValueSize from configuration. 203 */ 204 public static int getMaxKeyValueSize(Configuration conf) { 205 return conf.getInt(ConnectionConfiguration.MAX_KEYVALUE_SIZE_KEY, -1); 206 } 207 208 @Override 209 public Configuration getConfiguration() { 210 return configuration; 211 } 212 213 @Override 214 public TableName getName() { 215 return tableName; 216 } 217 218 /** 219 * <em>INTERNAL</em> Used by unit tests and tools to do low-level 220 * manipulations. 221 * @return A Connection instance. 222 */ 223 @VisibleForTesting 224 protected Connection getConnection() { 225 return this.connection; 226 } 227 228 @Override 229 @Deprecated 230 public HTableDescriptor getTableDescriptor() throws IOException { 231 HTableDescriptor htd = HBaseAdmin.getHTableDescriptor(tableName, connection, rpcCallerFactory, 232 rpcControllerFactory, operationTimeoutMs, readRpcTimeoutMs); 233 if (htd != null) { 234 return new ImmutableHTableDescriptor(htd); 235 } 236 return null; 237 } 238 239 @Override 240 public TableDescriptor getDescriptor() throws IOException { 241 return HBaseAdmin.getTableDescriptor(tableName, connection, rpcCallerFactory, 242 rpcControllerFactory, operationTimeoutMs, readRpcTimeoutMs); 243 } 244 245 /** 246 * Get the corresponding start keys and regions for an arbitrary range of 247 * keys. 248 * <p> 249 * @param startKey Starting row in range, inclusive 250 * @param endKey Ending row in range 251 * @param includeEndKey true if endRow is inclusive, false if exclusive 252 * @return A pair of list of start keys and list of HRegionLocations that 253 * contain the specified range 254 * @throws IOException if a remote or network exception occurs 255 */ 256 private Pair<List<byte[]>, List<HRegionLocation>> getKeysAndRegionsInRange( 257 final byte[] startKey, final byte[] endKey, final boolean includeEndKey) 258 throws IOException { 259 return getKeysAndRegionsInRange(startKey, endKey, includeEndKey, false); 260 } 261 262 /** 263 * Get the corresponding start keys and regions for an arbitrary range of 264 * keys. 265 * <p> 266 * @param startKey Starting row in range, inclusive 267 * @param endKey Ending row in range 268 * @param includeEndKey true if endRow is inclusive, false if exclusive 269 * @param reload true to reload information or false to use cached information 270 * @return A pair of list of start keys and list of HRegionLocations that 271 * contain the specified range 272 * @throws IOException if a remote or network exception occurs 273 */ 274 private Pair<List<byte[]>, List<HRegionLocation>> getKeysAndRegionsInRange( 275 final byte[] startKey, final byte[] endKey, final boolean includeEndKey, 276 final boolean reload) throws IOException { 277 final boolean endKeyIsEndOfTable = Bytes.equals(endKey,HConstants.EMPTY_END_ROW); 278 if ((Bytes.compareTo(startKey, endKey) > 0) && !endKeyIsEndOfTable) { 279 throw new IllegalArgumentException( 280 "Invalid range: " + Bytes.toStringBinary(startKey) + 281 " > " + Bytes.toStringBinary(endKey)); 282 } 283 List<byte[]> keysInRange = new ArrayList<>(); 284 List<HRegionLocation> regionsInRange = new ArrayList<>(); 285 byte[] currentKey = startKey; 286 do { 287 HRegionLocation regionLocation = getRegionLocator().getRegionLocation(currentKey, reload); 288 keysInRange.add(currentKey); 289 regionsInRange.add(regionLocation); 290 currentKey = regionLocation.getRegionInfo().getEndKey(); 291 } while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW) 292 && (endKeyIsEndOfTable || Bytes.compareTo(currentKey, endKey) < 0 293 || (includeEndKey && Bytes.compareTo(currentKey, endKey) == 0))); 294 return new Pair<>(keysInRange, regionsInRange); 295 } 296 297 /** 298 * The underlying {@link HTable} must not be closed. 299 * {@link Table#getScanner(Scan)} has other usage details. 300 */ 301 @Override 302 public ResultScanner getScanner(Scan scan) throws IOException { 303 if (scan.getCaching() <= 0) { 304 scan.setCaching(scannerCaching); 305 } 306 if (scan.getMaxResultSize() <= 0) { 307 scan.setMaxResultSize(scannerMaxResultSize); 308 } 309 if (scan.getMvccReadPoint() > 0) { 310 // it is not supposed to be set by user, clear 311 scan.resetMvccReadPoint(); 312 } 313 Boolean async = scan.isAsyncPrefetch(); 314 if (async == null) { 315 async = connConfiguration.isClientScannerAsyncPrefetch(); 316 } 317 318 if (scan.isReversed()) { 319 return new ReversedClientScanner(getConfiguration(), scan, getName(), 320 this.connection, this.rpcCallerFactory, this.rpcControllerFactory, 321 pool, connConfiguration.getReplicaCallTimeoutMicroSecondScan()); 322 } else { 323 if (async) { 324 return new ClientAsyncPrefetchScanner(getConfiguration(), scan, getName(), this.connection, 325 this.rpcCallerFactory, this.rpcControllerFactory, 326 pool, connConfiguration.getReplicaCallTimeoutMicroSecondScan()); 327 } else { 328 return new ClientSimpleScanner(getConfiguration(), scan, getName(), this.connection, 329 this.rpcCallerFactory, this.rpcControllerFactory, 330 pool, connConfiguration.getReplicaCallTimeoutMicroSecondScan()); 331 } 332 } 333 } 334 335 /** 336 * The underlying {@link HTable} must not be closed. 337 * {@link Table#getScanner(byte[])} has other usage details. 338 */ 339 @Override 340 public ResultScanner getScanner(byte [] family) throws IOException { 341 Scan scan = new Scan(); 342 scan.addFamily(family); 343 return getScanner(scan); 344 } 345 346 /** 347 * The underlying {@link HTable} must not be closed. 348 * {@link Table#getScanner(byte[], byte[])} has other usage details. 349 */ 350 @Override 351 public ResultScanner getScanner(byte [] family, byte [] qualifier) 352 throws IOException { 353 Scan scan = new Scan(); 354 scan.addColumn(family, qualifier); 355 return getScanner(scan); 356 } 357 358 @Override 359 public Result get(final Get get) throws IOException { 360 return get(get, get.isCheckExistenceOnly()); 361 } 362 363 private Result get(Get get, final boolean checkExistenceOnly) throws IOException { 364 // if we are changing settings to the get, clone it. 365 if (get.isCheckExistenceOnly() != checkExistenceOnly || get.getConsistency() == null) { 366 get = ReflectionUtils.newInstance(get.getClass(), get); 367 get.setCheckExistenceOnly(checkExistenceOnly); 368 if (get.getConsistency() == null){ 369 get.setConsistency(DEFAULT_CONSISTENCY); 370 } 371 } 372 373 if (get.getConsistency() == Consistency.STRONG) { 374 final Get configuredGet = get; 375 ClientServiceCallable<Result> callable = new ClientServiceCallable<Result>(this.connection, getName(), 376 get.getRow(), this.rpcControllerFactory.newController(), get.getPriority()) { 377 @Override 378 protected Result rpcCall() throws Exception { 379 ClientProtos.GetRequest request = RequestConverter.buildGetRequest( 380 getLocation().getRegionInfo().getRegionName(), configuredGet); 381 ClientProtos.GetResponse response = doGet(request); 382 return response == null? null: 383 ProtobufUtil.toResult(response.getResult(), getRpcControllerCellScanner()); 384 } 385 }; 386 return rpcCallerFactory.<Result>newCaller(readRpcTimeoutMs).callWithRetries(callable, 387 this.operationTimeoutMs); 388 } 389 390 // Call that takes into account the replica 391 RpcRetryingCallerWithReadReplicas callable = new RpcRetryingCallerWithReadReplicas( 392 rpcControllerFactory, tableName, this.connection, get, pool, 393 connConfiguration.getRetriesNumber(), operationTimeoutMs, readRpcTimeoutMs, 394 connConfiguration.getPrimaryCallTimeoutMicroSecond()); 395 return callable.call(operationTimeoutMs); 396 } 397 398 @Override 399 public Result[] get(List<Get> gets) throws IOException { 400 if (gets.size() == 1) { 401 return new Result[]{get(gets.get(0))}; 402 } 403 try { 404 Object[] r1 = new Object[gets.size()]; 405 batch((List<? extends Row>)gets, r1, readRpcTimeoutMs); 406 // Translate. 407 Result [] results = new Result[r1.length]; 408 int i = 0; 409 for (Object obj: r1) { 410 // Batch ensures if there is a failure we get an exception instead 411 results[i++] = (Result)obj; 412 } 413 return results; 414 } catch (InterruptedException e) { 415 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 416 } 417 } 418 419 @Override 420 public void batch(final List<? extends Row> actions, final Object[] results) 421 throws InterruptedException, IOException { 422 int rpcTimeout = writeRpcTimeoutMs; 423 boolean hasRead = false; 424 boolean hasWrite = false; 425 for (Row action : actions) { 426 if (action instanceof Mutation) { 427 hasWrite = true; 428 } else { 429 hasRead = true; 430 } 431 if (hasRead && hasWrite) { 432 break; 433 } 434 } 435 if (hasRead && !hasWrite) { 436 rpcTimeout = readRpcTimeoutMs; 437 } 438 batch(actions, results, rpcTimeout); 439 } 440 441 public void batch(final List<? extends Row> actions, final Object[] results, int rpcTimeout) 442 throws InterruptedException, IOException { 443 AsyncProcessTask task = AsyncProcessTask.newBuilder() 444 .setPool(pool) 445 .setTableName(tableName) 446 .setRowAccess(actions) 447 .setResults(results) 448 .setRpcTimeout(rpcTimeout) 449 .setOperationTimeout(operationTimeoutMs) 450 .setSubmittedRows(AsyncProcessTask.SubmittedRows.ALL) 451 .build(); 452 AsyncRequestFuture ars = multiAp.submit(task); 453 ars.waitUntilDone(); 454 if (ars.hasError()) { 455 throw ars.getErrors(); 456 } 457 } 458 459 @Override 460 public <R> void batchCallback( 461 final List<? extends Row> actions, final Object[] results, final Batch.Callback<R> callback) 462 throws IOException, InterruptedException { 463 doBatchWithCallback(actions, results, callback, connection, pool, tableName); 464 } 465 466 public static <R> void doBatchWithCallback(List<? extends Row> actions, Object[] results, 467 Callback<R> callback, ClusterConnection connection, ExecutorService pool, TableName tableName) 468 throws InterruptedIOException, RetriesExhaustedWithDetailsException { 469 int operationTimeout = connection.getConnectionConfiguration().getOperationTimeout(); 470 int writeTimeout = connection.getConfiguration().getInt(HConstants.HBASE_RPC_WRITE_TIMEOUT_KEY, 471 connection.getConfiguration().getInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 472 HConstants.DEFAULT_HBASE_RPC_TIMEOUT)); 473 AsyncProcessTask<R> task = AsyncProcessTask.newBuilder(callback) 474 .setPool(pool) 475 .setTableName(tableName) 476 .setRowAccess(actions) 477 .setResults(results) 478 .setOperationTimeout(operationTimeout) 479 .setRpcTimeout(writeTimeout) 480 .setSubmittedRows(AsyncProcessTask.SubmittedRows.ALL) 481 .build(); 482 AsyncRequestFuture ars = connection.getAsyncProcess().submit(task); 483 ars.waitUntilDone(); 484 if (ars.hasError()) { 485 throw ars.getErrors(); 486 } 487 } 488 489 @Override 490 public void delete(final Delete delete) 491 throws IOException { 492 CancellableRegionServerCallable<SingleResponse> callable = 493 new CancellableRegionServerCallable<SingleResponse>( 494 connection, getName(), delete.getRow(), this.rpcControllerFactory.newController(), 495 writeRpcTimeoutMs, new RetryingTimeTracker().start(), delete.getPriority()) { 496 @Override 497 protected SingleResponse rpcCall() throws Exception { 498 MutateRequest request = RequestConverter.buildMutateRequest( 499 getLocation().getRegionInfo().getRegionName(), delete); 500 MutateResponse response = doMutate(request); 501 return ResponseConverter.getResult(request, response, getRpcControllerCellScanner()); 502 } 503 }; 504 List<Delete> rows = Collections.singletonList(delete); 505 AsyncProcessTask task = AsyncProcessTask.newBuilder() 506 .setPool(pool) 507 .setTableName(tableName) 508 .setRowAccess(rows) 509 .setCallable(callable) 510 .setRpcTimeout(writeRpcTimeoutMs) 511 .setOperationTimeout(operationTimeoutMs) 512 .setSubmittedRows(AsyncProcessTask.SubmittedRows.ALL) 513 .build(); 514 AsyncRequestFuture ars = multiAp.submit(task); 515 ars.waitUntilDone(); 516 if (ars.hasError()) { 517 throw ars.getErrors(); 518 } 519 } 520 521 @Override 522 public void delete(final List<Delete> deletes) 523 throws IOException { 524 Object[] results = new Object[deletes.size()]; 525 try { 526 batch(deletes, results, writeRpcTimeoutMs); 527 } catch (InterruptedException e) { 528 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 529 } finally { 530 // TODO: to be consistent with batch put(), do not modify input list 531 // mutate list so that it is empty for complete success, or contains only failed records 532 // results are returned in the same order as the requests in list walk the list backwards, 533 // so we can remove from list without impacting the indexes of earlier members 534 for (int i = results.length - 1; i>=0; i--) { 535 // if result is not null, it succeeded 536 if (results[i] instanceof Result) { 537 deletes.remove(i); 538 } 539 } 540 } 541 } 542 543 @Override 544 public void put(final Put put) throws IOException { 545 validatePut(put); 546 ClientServiceCallable<Void> callable = 547 new ClientServiceCallable<Void>(this.connection, getName(), put.getRow(), 548 this.rpcControllerFactory.newController(), put.getPriority()) { 549 @Override 550 protected Void rpcCall() throws Exception { 551 MutateRequest request = 552 RequestConverter.buildMutateRequest(getLocation().getRegionInfo().getRegionName(), put); 553 doMutate(request); 554 return null; 555 } 556 }; 557 rpcCallerFactory.<Void> newCaller(this.writeRpcTimeoutMs).callWithRetries(callable, 558 this.operationTimeoutMs); 559 } 560 561 @Override 562 public void put(final List<Put> puts) throws IOException { 563 for (Put put : puts) { 564 validatePut(put); 565 } 566 Object[] results = new Object[puts.size()]; 567 try { 568 batch(puts, results, writeRpcTimeoutMs); 569 } catch (InterruptedException e) { 570 throw (InterruptedIOException) new InterruptedIOException().initCause(e); 571 } 572 } 573 574 @Override 575 public void mutateRow(final RowMutations rm) throws IOException { 576 CancellableRegionServerCallable<MultiResponse> callable = 577 new CancellableRegionServerCallable<MultiResponse>(this.connection, getName(), rm.getRow(), 578 rpcControllerFactory.newController(), writeRpcTimeoutMs, 579 new RetryingTimeTracker().start(), rm.getMaxPriority()) { 580 @Override 581 protected MultiResponse rpcCall() throws Exception { 582 RegionAction.Builder regionMutationBuilder = RequestConverter.buildRegionAction( 583 getLocation().getRegionInfo().getRegionName(), rm); 584 regionMutationBuilder.setAtomic(true); 585 MultiRequest request = 586 MultiRequest.newBuilder().addRegionAction(regionMutationBuilder.build()).build(); 587 ClientProtos.MultiResponse response = doMulti(request); 588 ClientProtos.RegionActionResult res = response.getRegionActionResultList().get(0); 589 if (res.hasException()) { 590 Throwable ex = ProtobufUtil.toException(res.getException()); 591 if (ex instanceof IOException) { 592 throw (IOException) ex; 593 } 594 throw new IOException("Failed to mutate row: " + Bytes.toStringBinary(rm.getRow()), ex); 595 } 596 return ResponseConverter.getResults(request, response, getRpcControllerCellScanner()); 597 } 598 }; 599 AsyncProcessTask task = AsyncProcessTask.newBuilder() 600 .setPool(pool) 601 .setTableName(tableName) 602 .setRowAccess(rm.getMutations()) 603 .setCallable(callable) 604 .setRpcTimeout(writeRpcTimeoutMs) 605 .setOperationTimeout(operationTimeoutMs) 606 .setSubmittedRows(AsyncProcessTask.SubmittedRows.ALL) 607 .build(); 608 AsyncRequestFuture ars = multiAp.submit(task); 609 ars.waitUntilDone(); 610 if (ars.hasError()) { 611 throw ars.getErrors(); 612 } 613 } 614 615 @Override 616 public Result append(final Append append) throws IOException { 617 checkHasFamilies(append); 618 NoncedRegionServerCallable<Result> callable = 619 new NoncedRegionServerCallable<Result>(this.connection, getName(), append.getRow(), 620 this.rpcControllerFactory.newController(), append.getPriority()) { 621 @Override 622 protected Result rpcCall() throws Exception { 623 MutateRequest request = RequestConverter.buildMutateRequest( 624 getLocation().getRegionInfo().getRegionName(), append, getNonceGroup(), getNonce()); 625 MutateResponse response = doMutate(request); 626 if (!response.hasResult()) return null; 627 return ProtobufUtil.toResult(response.getResult(), getRpcControllerCellScanner()); 628 } 629 }; 630 return rpcCallerFactory.<Result> newCaller(this.writeRpcTimeoutMs). 631 callWithRetries(callable, this.operationTimeoutMs); 632 } 633 634 @Override 635 public Result increment(final Increment increment) throws IOException { 636 checkHasFamilies(increment); 637 NoncedRegionServerCallable<Result> callable = 638 new NoncedRegionServerCallable<Result>(this.connection, getName(), increment.getRow(), 639 this.rpcControllerFactory.newController(), increment.getPriority()) { 640 @Override 641 protected Result rpcCall() throws Exception { 642 MutateRequest request = RequestConverter.buildMutateRequest( 643 getLocation().getRegionInfo().getRegionName(), increment, getNonceGroup(), getNonce()); 644 MutateResponse response = doMutate(request); 645 // Should this check for null like append does? 646 return ProtobufUtil.toResult(response.getResult(), getRpcControllerCellScanner()); 647 } 648 }; 649 return rpcCallerFactory.<Result> newCaller(writeRpcTimeoutMs).callWithRetries(callable, 650 this.operationTimeoutMs); 651 } 652 653 @Override 654 public long incrementColumnValue(final byte [] row, final byte [] family, 655 final byte [] qualifier, final long amount) 656 throws IOException { 657 return incrementColumnValue(row, family, qualifier, amount, Durability.SYNC_WAL); 658 } 659 660 @Override 661 public long incrementColumnValue(final byte [] row, final byte [] family, 662 final byte [] qualifier, final long amount, final Durability durability) 663 throws IOException { 664 NullPointerException npe = null; 665 if (row == null) { 666 npe = new NullPointerException("row is null"); 667 } else if (family == null) { 668 npe = new NullPointerException("family is null"); 669 } 670 if (npe != null) { 671 throw new IOException( 672 "Invalid arguments to incrementColumnValue", npe); 673 } 674 675 NoncedRegionServerCallable<Long> callable = 676 new NoncedRegionServerCallable<Long>(this.connection, getName(), row, 677 this.rpcControllerFactory.newController(), HConstants.PRIORITY_UNSET) { 678 @Override 679 protected Long rpcCall() throws Exception { 680 MutateRequest request = RequestConverter.buildIncrementRequest( 681 getLocation().getRegionInfo().getRegionName(), row, family, 682 qualifier, amount, durability, getNonceGroup(), getNonce()); 683 MutateResponse response = doMutate(request); 684 Result result = ProtobufUtil.toResult(response.getResult(), getRpcControllerCellScanner()); 685 return Long.valueOf(Bytes.toLong(result.getValue(family, qualifier))); 686 } 687 }; 688 return rpcCallerFactory.<Long> newCaller(this.writeRpcTimeoutMs). 689 callWithRetries(callable, this.operationTimeoutMs); 690 } 691 692 @Override 693 @Deprecated 694 public boolean checkAndPut(final byte [] row, final byte [] family, final byte [] qualifier, 695 final byte [] value, final Put put) throws IOException { 696 return doCheckAndPut(row, family, qualifier, CompareOperator.EQUAL.name(), value, null, put); 697 } 698 699 @Override 700 @Deprecated 701 public boolean checkAndPut(final byte [] row, final byte [] family, final byte [] qualifier, 702 final CompareOp compareOp, final byte [] value, final Put put) throws IOException { 703 return doCheckAndPut(row, family, qualifier, compareOp.name(), value, null, put); 704 } 705 706 @Override 707 @Deprecated 708 public boolean checkAndPut(final byte [] row, final byte [] family, final byte [] qualifier, 709 final CompareOperator op, final byte [] value, final Put put) throws IOException { 710 // The name of the operators in CompareOperator are intentionally those of the 711 // operators in the filter's CompareOp enum. 712 return doCheckAndPut(row, family, qualifier, op.name(), value, null, put); 713 } 714 715 private boolean doCheckAndPut(final byte[] row, final byte[] family, final byte[] qualifier, 716 final String opName, final byte[] value, final TimeRange timeRange, final Put put) 717 throws IOException { 718 ClientServiceCallable<Boolean> callable = 719 new ClientServiceCallable<Boolean>(this.connection, getName(), row, 720 this.rpcControllerFactory.newController(), put.getPriority()) { 721 @Override 722 protected Boolean rpcCall() throws Exception { 723 CompareType compareType = CompareType.valueOf(opName); 724 MutateRequest request = RequestConverter.buildMutateRequest( 725 getLocation().getRegionInfo().getRegionName(), row, family, qualifier, 726 new BinaryComparator(value), compareType, timeRange, put); 727 MutateResponse response = doMutate(request); 728 return Boolean.valueOf(response.getProcessed()); 729 } 730 }; 731 return rpcCallerFactory.<Boolean> newCaller(this.writeRpcTimeoutMs) 732 .callWithRetries(callable, this.operationTimeoutMs); 733 } 734 735 @Override 736 @Deprecated 737 public boolean checkAndDelete(final byte[] row, final byte[] family, final byte[] qualifier, 738 final byte[] value, final Delete delete) throws IOException { 739 return doCheckAndDelete(row, family, qualifier, CompareOperator.EQUAL.name(), value, null, 740 delete); 741 } 742 743 @Override 744 @Deprecated 745 public boolean checkAndDelete(final byte[] row, final byte[] family, final byte[] qualifier, 746 final CompareOp compareOp, final byte[] value, final Delete delete) throws IOException { 747 return doCheckAndDelete(row, family, qualifier, compareOp.name(), value, null, delete); 748 } 749 750 @Override 751 @Deprecated 752 public boolean checkAndDelete(final byte[] row, final byte[] family, final byte[] qualifier, 753 final CompareOperator op, final byte[] value, final Delete delete) throws IOException { 754 return doCheckAndDelete(row, family, qualifier, op.name(), value, null, delete); 755 } 756 757 private boolean doCheckAndDelete(final byte[] row, final byte[] family, final byte[] qualifier, 758 final String opName, final byte[] value, final TimeRange timeRange, final Delete delete) 759 throws IOException { 760 CancellableRegionServerCallable<SingleResponse> callable = 761 new CancellableRegionServerCallable<SingleResponse>(this.connection, getName(), row, 762 this.rpcControllerFactory.newController(), writeRpcTimeoutMs, 763 new RetryingTimeTracker().start(), delete.getPriority()) { 764 @Override 765 protected SingleResponse rpcCall() throws Exception { 766 CompareType compareType = CompareType.valueOf(opName); 767 MutateRequest request = RequestConverter 768 .buildMutateRequest(getLocation().getRegionInfo().getRegionName(), row, family, 769 qualifier, new BinaryComparator(value), compareType, timeRange, delete); 770 MutateResponse response = doMutate(request); 771 return ResponseConverter.getResult(request, response, getRpcControllerCellScanner()); 772 } 773 }; 774 List<Delete> rows = Collections.singletonList(delete); 775 Object[] results = new Object[1]; 776 AsyncProcessTask task = 777 AsyncProcessTask.newBuilder().setPool(pool).setTableName(tableName).setRowAccess(rows) 778 .setCallable(callable) 779 // TODO any better timeout? 780 .setRpcTimeout(Math.max(readRpcTimeoutMs, writeRpcTimeoutMs)) 781 .setOperationTimeout(operationTimeoutMs) 782 .setSubmittedRows(AsyncProcessTask.SubmittedRows.ALL).setResults(results).build(); 783 AsyncRequestFuture ars = multiAp.submit(task); 784 ars.waitUntilDone(); 785 if (ars.hasError()) { 786 throw ars.getErrors(); 787 } 788 return ((SingleResponse.Entry) results[0]).isProcessed(); 789 } 790 791 @Override 792 public CheckAndMutateBuilder checkAndMutate(byte[] row, byte[] family) { 793 return new CheckAndMutateBuilderImpl(row, family); 794 } 795 796 private boolean doCheckAndMutate(final byte[] row, final byte[] family, final byte[] qualifier, 797 final String opName, final byte[] value, final TimeRange timeRange, final RowMutations rm) 798 throws IOException { 799 CancellableRegionServerCallable<MultiResponse> callable = 800 new CancellableRegionServerCallable<MultiResponse>(connection, getName(), rm.getRow(), 801 rpcControllerFactory.newController(), writeRpcTimeoutMs, new RetryingTimeTracker().start(), 802 rm.getMaxPriority()) { 803 @Override 804 protected MultiResponse rpcCall() throws Exception { 805 CompareType compareType = CompareType.valueOf(opName); 806 MultiRequest request = RequestConverter 807 .buildMutateRequest(getLocation().getRegionInfo().getRegionName(), row, family, qualifier, 808 new BinaryComparator(value), compareType, timeRange, rm); 809 ClientProtos.MultiResponse response = doMulti(request); 810 ClientProtos.RegionActionResult res = response.getRegionActionResultList().get(0); 811 if (res.hasException()) { 812 Throwable ex = ProtobufUtil.toException(res.getException()); 813 if (ex instanceof IOException) { 814 throw (IOException) ex; 815 } 816 throw new IOException( 817 "Failed to checkAndMutate row: " + Bytes.toStringBinary(rm.getRow()), ex); 818 } 819 return ResponseConverter.getResults(request, response, getRpcControllerCellScanner()); 820 } 821 }; 822 823 /** 824 * Currently, we use one array to store 'processed' flag which is returned by server. 825 * It is excessive to send such a large array, but that is required by the framework right now 826 * */ 827 Object[] results = new Object[rm.getMutations().size()]; 828 AsyncProcessTask task = AsyncProcessTask.newBuilder() 829 .setPool(pool) 830 .setTableName(tableName) 831 .setRowAccess(rm.getMutations()) 832 .setResults(results) 833 .setCallable(callable) 834 // TODO any better timeout? 835 .setRpcTimeout(Math.max(readRpcTimeoutMs, writeRpcTimeoutMs)) 836 .setOperationTimeout(operationTimeoutMs) 837 .setSubmittedRows(AsyncProcessTask.SubmittedRows.ALL) 838 .build(); 839 AsyncRequestFuture ars = multiAp.submit(task); 840 ars.waitUntilDone(); 841 if (ars.hasError()) { 842 throw ars.getErrors(); 843 } 844 845 return ((Result)results[0]).getExists(); 846 } 847 848 @Override 849 @Deprecated 850 public boolean checkAndMutate(final byte [] row, final byte [] family, final byte [] qualifier, 851 final CompareOp compareOp, final byte [] value, final RowMutations rm) 852 throws IOException { 853 return doCheckAndMutate(row, family, qualifier, compareOp.name(), value, null, rm); 854 } 855 856 @Override 857 @Deprecated 858 public boolean checkAndMutate(final byte [] row, final byte [] family, final byte [] qualifier, 859 final CompareOperator op, final byte [] value, final RowMutations rm) throws IOException { 860 return doCheckAndMutate(row, family, qualifier, op.name(), value, null, rm); 861 } 862 863 @Override 864 public boolean exists(final Get get) throws IOException { 865 Result r = get(get, true); 866 assert r.getExists() != null; 867 return r.getExists(); 868 } 869 870 @Override 871 public boolean[] exists(List<Get> gets) throws IOException { 872 if (gets.isEmpty()) return new boolean[]{}; 873 if (gets.size() == 1) return new boolean[]{exists(gets.get(0))}; 874 875 ArrayList<Get> exists = new ArrayList<>(gets.size()); 876 for (Get g: gets){ 877 Get ge = new Get(g); 878 ge.setCheckExistenceOnly(true); 879 exists.add(ge); 880 } 881 882 Object[] r1= new Object[exists.size()]; 883 try { 884 batch(exists, r1, readRpcTimeoutMs); 885 } catch (InterruptedException e) { 886 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 887 } 888 889 // translate. 890 boolean[] results = new boolean[r1.length]; 891 int i = 0; 892 for (Object o : r1) { 893 // batch ensures if there is a failure we get an exception instead 894 results[i++] = ((Result)o).getExists(); 895 } 896 897 return results; 898 } 899 900 /** 901 * Process a mixed batch of Get, Put and Delete actions. All actions for a 902 * RegionServer are forwarded in one RPC call. Queries are executed in parallel. 903 * 904 * @param list The collection of actions. 905 * @param results An empty array, same size as list. If an exception is thrown, 906 * you can test here for partial results, and to determine which actions 907 * processed successfully. 908 * @throws IOException if there are problems talking to META. Per-item 909 * exceptions are stored in the results array. 910 */ 911 public <R> void processBatchCallback( 912 final List<? extends Row> list, final Object[] results, final Batch.Callback<R> callback) 913 throws IOException, InterruptedException { 914 this.batchCallback(list, results, callback); 915 } 916 917 @Override 918 public void close() throws IOException { 919 if (this.closed) { 920 return; 921 } 922 if (cleanupPoolOnClose) { 923 this.pool.shutdown(); 924 try { 925 boolean terminated = false; 926 do { 927 // wait until the pool has terminated 928 terminated = this.pool.awaitTermination(60, TimeUnit.SECONDS); 929 } while (!terminated); 930 } catch (InterruptedException e) { 931 this.pool.shutdownNow(); 932 LOG.warn("waitForTermination interrupted"); 933 } 934 } 935 this.closed = true; 936 } 937 938 // validate for well-formedness 939 public void validatePut(final Put put) throws IllegalArgumentException { 940 validatePut(put, connConfiguration.getMaxKeyValueSize()); 941 } 942 943 // validate for well-formedness 944 public static void validatePut(Put put, int maxKeyValueSize) throws IllegalArgumentException { 945 if (put.isEmpty()) { 946 throw new IllegalArgumentException("No columns to insert"); 947 } 948 if (maxKeyValueSize > 0) { 949 for (List<Cell> list : put.getFamilyCellMap().values()) { 950 for (Cell cell : list) { 951 if (KeyValueUtil.length(cell) > maxKeyValueSize) { 952 throw new IllegalArgumentException("KeyValue size too large"); 953 } 954 } 955 } 956 } 957 } 958 959 /** 960 * The pool is used for mutli requests for this HTable 961 * @return the pool used for mutli 962 */ 963 ExecutorService getPool() { 964 return this.pool; 965 } 966 967 /** 968 * Explicitly clears the region cache to fetch the latest value from META. 969 * This is a power user function: avoid unless you know the ramifications. 970 */ 971 public void clearRegionCache() { 972 this.connection.clearRegionCache(); 973 } 974 975 @Override 976 public CoprocessorRpcChannel coprocessorService(byte[] row) { 977 return new RegionCoprocessorRpcChannel(connection, tableName, row); 978 } 979 980 @Override 981 public <T extends Service, R> Map<byte[],R> coprocessorService(final Class<T> service, 982 byte[] startKey, byte[] endKey, final Batch.Call<T,R> callable) 983 throws ServiceException, Throwable { 984 final Map<byte[],R> results = Collections.synchronizedMap( 985 new TreeMap<byte[], R>(Bytes.BYTES_COMPARATOR)); 986 coprocessorService(service, startKey, endKey, callable, new Batch.Callback<R>() { 987 @Override 988 public void update(byte[] region, byte[] row, R value) { 989 if (region != null) { 990 results.put(region, value); 991 } 992 } 993 }); 994 return results; 995 } 996 997 @Override 998 public <T extends Service, R> void coprocessorService(final Class<T> service, 999 byte[] startKey, byte[] endKey, final Batch.Call<T,R> callable, 1000 final Batch.Callback<R> callback) throws ServiceException, Throwable { 1001 // get regions covered by the row range 1002 List<byte[]> keys = getStartKeysInRange(startKey, endKey); 1003 Map<byte[],Future<R>> futures = new TreeMap<>(Bytes.BYTES_COMPARATOR); 1004 for (final byte[] r : keys) { 1005 final RegionCoprocessorRpcChannel channel = 1006 new RegionCoprocessorRpcChannel(connection, tableName, r); 1007 Future<R> future = pool.submit(new Callable<R>() { 1008 @Override 1009 public R call() throws Exception { 1010 T instance = 1011 org.apache.hadoop.hbase.protobuf.ProtobufUtil.newServiceStub(service, channel); 1012 R result = callable.call(instance); 1013 byte[] region = channel.getLastRegion(); 1014 if (callback != null) { 1015 callback.update(region, r, result); 1016 } 1017 return result; 1018 } 1019 }); 1020 futures.put(r, future); 1021 } 1022 for (Map.Entry<byte[],Future<R>> e : futures.entrySet()) { 1023 try { 1024 e.getValue().get(); 1025 } catch (ExecutionException ee) { 1026 LOG.warn("Error calling coprocessor service " + service.getName() + " for row " 1027 + Bytes.toStringBinary(e.getKey()), ee); 1028 throw ee.getCause(); 1029 } catch (InterruptedException ie) { 1030 throw new InterruptedIOException("Interrupted calling coprocessor service " 1031 + service.getName() + " for row " + Bytes.toStringBinary(e.getKey())).initCause(ie); 1032 } 1033 } 1034 } 1035 1036 private List<byte[]> getStartKeysInRange(byte[] start, byte[] end) 1037 throws IOException { 1038 if (start == null) { 1039 start = HConstants.EMPTY_START_ROW; 1040 } 1041 if (end == null) { 1042 end = HConstants.EMPTY_END_ROW; 1043 } 1044 return getKeysAndRegionsInRange(start, end, true).getFirst(); 1045 } 1046 1047 @Override 1048 public long getRpcTimeout(TimeUnit unit) { 1049 return unit.convert(rpcTimeoutMs, TimeUnit.MILLISECONDS); 1050 } 1051 1052 @Override 1053 @Deprecated 1054 public int getRpcTimeout() { 1055 return rpcTimeoutMs; 1056 } 1057 1058 @Override 1059 @Deprecated 1060 public void setRpcTimeout(int rpcTimeout) { 1061 setReadRpcTimeout(rpcTimeout); 1062 setWriteRpcTimeout(rpcTimeout); 1063 } 1064 1065 @Override 1066 public long getReadRpcTimeout(TimeUnit unit) { 1067 return unit.convert(readRpcTimeoutMs, TimeUnit.MILLISECONDS); 1068 } 1069 1070 @Override 1071 @Deprecated 1072 public int getReadRpcTimeout() { 1073 return readRpcTimeoutMs; 1074 } 1075 1076 @Override 1077 @Deprecated 1078 public void setReadRpcTimeout(int readRpcTimeout) { 1079 this.readRpcTimeoutMs = readRpcTimeout; 1080 } 1081 1082 @Override 1083 public long getWriteRpcTimeout(TimeUnit unit) { 1084 return unit.convert(writeRpcTimeoutMs, TimeUnit.MILLISECONDS); 1085 } 1086 1087 @Override 1088 @Deprecated 1089 public int getWriteRpcTimeout() { 1090 return writeRpcTimeoutMs; 1091 } 1092 1093 @Override 1094 @Deprecated 1095 public void setWriteRpcTimeout(int writeRpcTimeout) { 1096 this.writeRpcTimeoutMs = writeRpcTimeout; 1097 } 1098 1099 @Override 1100 public long getOperationTimeout(TimeUnit unit) { 1101 return unit.convert(operationTimeoutMs, TimeUnit.MILLISECONDS); 1102 } 1103 1104 @Override 1105 @Deprecated 1106 public int getOperationTimeout() { 1107 return operationTimeoutMs; 1108 } 1109 1110 @Override 1111 @Deprecated 1112 public void setOperationTimeout(int operationTimeout) { 1113 this.operationTimeoutMs = operationTimeout; 1114 } 1115 1116 @Override 1117 public String toString() { 1118 return tableName + ";" + connection; 1119 } 1120 1121 @Override 1122 public <R extends Message> Map<byte[], R> batchCoprocessorService( 1123 Descriptors.MethodDescriptor methodDescriptor, Message request, 1124 byte[] startKey, byte[] endKey, R responsePrototype) throws ServiceException, Throwable { 1125 final Map<byte[], R> results = Collections.synchronizedMap(new TreeMap<byte[], R>( 1126 Bytes.BYTES_COMPARATOR)); 1127 batchCoprocessorService(methodDescriptor, request, startKey, endKey, responsePrototype, 1128 new Callback<R>() { 1129 @Override 1130 public void update(byte[] region, byte[] row, R result) { 1131 if (region != null) { 1132 results.put(region, result); 1133 } 1134 } 1135 }); 1136 return results; 1137 } 1138 1139 @Override 1140 public <R extends Message> void batchCoprocessorService( 1141 final Descriptors.MethodDescriptor methodDescriptor, final Message request, 1142 byte[] startKey, byte[] endKey, final R responsePrototype, final Callback<R> callback) 1143 throws ServiceException, Throwable { 1144 1145 if (startKey == null) { 1146 startKey = HConstants.EMPTY_START_ROW; 1147 } 1148 if (endKey == null) { 1149 endKey = HConstants.EMPTY_END_ROW; 1150 } 1151 // get regions covered by the row range 1152 Pair<List<byte[]>, List<HRegionLocation>> keysAndRegions = 1153 getKeysAndRegionsInRange(startKey, endKey, true); 1154 List<byte[]> keys = keysAndRegions.getFirst(); 1155 List<HRegionLocation> regions = keysAndRegions.getSecond(); 1156 1157 // check if we have any calls to make 1158 if (keys.isEmpty()) { 1159 LOG.info("No regions were selected by key range start=" + Bytes.toStringBinary(startKey) + 1160 ", end=" + Bytes.toStringBinary(endKey)); 1161 return; 1162 } 1163 1164 List<RegionCoprocessorServiceExec> execs = new ArrayList<>(keys.size()); 1165 final Map<byte[], RegionCoprocessorServiceExec> execsByRow = new TreeMap<>(Bytes.BYTES_COMPARATOR); 1166 for (int i = 0; i < keys.size(); i++) { 1167 final byte[] rowKey = keys.get(i); 1168 final byte[] region = regions.get(i).getRegionInfo().getRegionName(); 1169 RegionCoprocessorServiceExec exec = 1170 new RegionCoprocessorServiceExec(region, rowKey, methodDescriptor, request); 1171 execs.add(exec); 1172 execsByRow.put(rowKey, exec); 1173 } 1174 1175 // tracking for any possible deserialization errors on success callback 1176 // TODO: it would be better to be able to reuse AsyncProcess.BatchErrors here 1177 final List<Throwable> callbackErrorExceptions = new ArrayList<>(); 1178 final List<Row> callbackErrorActions = new ArrayList<>(); 1179 final List<String> callbackErrorServers = new ArrayList<>(); 1180 Object[] results = new Object[execs.size()]; 1181 1182 AsyncProcess asyncProcess = new AsyncProcess(connection, configuration, 1183 RpcRetryingCallerFactory.instantiate(configuration, connection.getStatisticsTracker()), 1184 RpcControllerFactory.instantiate(configuration)); 1185 1186 Callback<ClientProtos.CoprocessorServiceResult> resultsCallback 1187 = (byte[] region, byte[] row, ClientProtos.CoprocessorServiceResult serviceResult) -> { 1188 if (LOG.isTraceEnabled()) { 1189 LOG.trace("Received result for endpoint " + methodDescriptor.getFullName() + 1190 ": region=" + Bytes.toStringBinary(region) + 1191 ", row=" + Bytes.toStringBinary(row) + 1192 ", value=" + serviceResult.getValue().getValue()); 1193 } 1194 try { 1195 Message.Builder builder = responsePrototype.newBuilderForType(); 1196 org.apache.hadoop.hbase.protobuf.ProtobufUtil.mergeFrom(builder, 1197 serviceResult.getValue().getValue().toByteArray()); 1198 callback.update(region, row, (R) builder.build()); 1199 } catch (IOException e) { 1200 LOG.error("Unexpected response type from endpoint " + methodDescriptor.getFullName(), 1201 e); 1202 callbackErrorExceptions.add(e); 1203 callbackErrorActions.add(execsByRow.get(row)); 1204 callbackErrorServers.add("null"); 1205 } 1206 }; 1207 AsyncProcessTask<ClientProtos.CoprocessorServiceResult> task = 1208 AsyncProcessTask.newBuilder(resultsCallback) 1209 .setPool(pool) 1210 .setTableName(tableName) 1211 .setRowAccess(execs) 1212 .setResults(results) 1213 .setRpcTimeout(readRpcTimeoutMs) 1214 .setOperationTimeout(operationTimeoutMs) 1215 .setSubmittedRows(AsyncProcessTask.SubmittedRows.ALL) 1216 .build(); 1217 AsyncRequestFuture future = asyncProcess.submit(task); 1218 future.waitUntilDone(); 1219 1220 if (future.hasError()) { 1221 throw future.getErrors(); 1222 } else if (!callbackErrorExceptions.isEmpty()) { 1223 throw new RetriesExhaustedWithDetailsException(callbackErrorExceptions, callbackErrorActions, 1224 callbackErrorServers); 1225 } 1226 } 1227 1228 public RegionLocator getRegionLocator() { 1229 return this.locator; 1230 } 1231 1232 private class CheckAndMutateBuilderImpl implements CheckAndMutateBuilder { 1233 1234 private final byte[] row; 1235 private final byte[] family; 1236 private byte[] qualifier; 1237 private TimeRange timeRange; 1238 private CompareOperator op; 1239 private byte[] value; 1240 1241 CheckAndMutateBuilderImpl(byte[] row, byte[] family) { 1242 this.row = Preconditions.checkNotNull(row, "row is null"); 1243 this.family = Preconditions.checkNotNull(family, "family is null"); 1244 } 1245 1246 @Override 1247 public CheckAndMutateBuilder qualifier(byte[] qualifier) { 1248 this.qualifier = Preconditions.checkNotNull(qualifier, "qualifier is null. Consider using" + 1249 " an empty byte array, or just do not call this method if you want a null qualifier"); 1250 return this; 1251 } 1252 1253 @Override 1254 public CheckAndMutateBuilder timeRange(TimeRange timeRange) { 1255 this.timeRange = timeRange; 1256 return this; 1257 } 1258 1259 @Override 1260 public CheckAndMutateBuilder ifNotExists() { 1261 this.op = CompareOperator.EQUAL; 1262 this.value = null; 1263 return this; 1264 } 1265 1266 @Override 1267 public CheckAndMutateBuilder ifMatches(CompareOperator compareOp, byte[] value) { 1268 this.op = Preconditions.checkNotNull(compareOp, "compareOp is null"); 1269 this.value = Preconditions.checkNotNull(value, "value is null"); 1270 return this; 1271 } 1272 1273 private void preCheck() { 1274 Preconditions.checkNotNull(op, "condition is null. You need to specify the condition by" + 1275 " calling ifNotExists/ifEquals/ifMatches before executing the request"); 1276 } 1277 1278 @Override 1279 public boolean thenPut(Put put) throws IOException { 1280 preCheck(); 1281 return doCheckAndPut(row, family, qualifier, op.name(), value, timeRange, put); 1282 } 1283 1284 @Override 1285 public boolean thenDelete(Delete delete) throws IOException { 1286 preCheck(); 1287 return doCheckAndDelete(row, family, qualifier, op.name(), value, timeRange, delete); 1288 } 1289 1290 @Override 1291 public boolean thenMutate(RowMutations mutation) throws IOException { 1292 preCheck(); 1293 return doCheckAndMutate(row, family, qualifier, op.name(), value, timeRange, mutation); 1294 } 1295 } 1296}