1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.client;
21
22 import com.google.common.annotations.VisibleForTesting;
23 import java.io.IOException;
24 import java.io.InterruptedIOException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.LinkedList;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.TreeSet;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.concurrent.ConcurrentMap;
40 import java.util.concurrent.ConcurrentSkipListMap;
41 import java.util.concurrent.ExecutorService;
42 import java.util.concurrent.RejectedExecutionException;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.atomic.AtomicInteger;
45 import java.util.concurrent.atomic.AtomicLong;
46 import org.apache.commons.logging.Log;
47 import org.apache.commons.logging.LogFactory;
48 import org.apache.hadoop.conf.Configuration;
49 import org.apache.hadoop.hbase.client.AsyncProcess.RowChecker.ReturnCode;
50 import org.apache.hadoop.hbase.CallQueueTooBigException;
51 import org.apache.hadoop.hbase.DoNotRetryIOException;
52 import org.apache.hadoop.hbase.HConstants;
53 import org.apache.hadoop.hbase.HRegionInfo;
54 import org.apache.hadoop.hbase.HRegionLocation;
55 import org.apache.hadoop.hbase.RegionLocations;
56 import org.apache.hadoop.hbase.RetryImmediatelyException;
57 import org.apache.hadoop.hbase.ServerName;
58 import org.apache.hadoop.hbase.TableName;
59 import org.apache.hadoop.hbase.classification.InterfaceAudience;
60 import org.apache.hadoop.hbase.client.backoff.ServerStatistics;
61 import org.apache.hadoop.hbase.client.coprocessor.Batch;
62 import org.apache.hadoop.hbase.exceptions.ClientExceptionsUtil;
63 import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
64 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
65 import org.apache.hadoop.hbase.util.Bytes;
66 import org.apache.hadoop.hbase.util.EnvironmentEdge;
67 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
68 import org.apache.htrace.Trace;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 @InterfaceAudience.Private
106 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER",
107 justification="Synchronization on tasks in progress counter is intended")
108 class AsyncProcess {
109 private static final Log LOG = LogFactory.getLog(AsyncProcess.class);
110 protected static final AtomicLong COUNTER = new AtomicLong();
111
112 public static final String PRIMARY_CALL_TIMEOUT_KEY = "hbase.client.primaryCallTimeout.multiget";
113
114
115
116
117
118
119
120 public static final String START_LOG_ERRORS_AFTER_COUNT_KEY =
121 "hbase.client.start.log.errors.counter";
122 public static final int DEFAULT_START_LOG_ERRORS_AFTER_COUNT = 9;
123
124
125
126
127 public static final String LOG_DETAILS_FOR_BATCH_ERROR = "hbase.client.log.batcherrors.details";
128
129 private final int thresholdToLogUndoneTaskDetails;
130 private static final String THRESHOLD_TO_LOG_UNDONE_TASK_DETAILS =
131 "hbase.client.threshold.log.details";
132 private static final int DEFAULT_THRESHOLD_TO_LOG_UNDONE_TASK_DETAILS = 10;
133 private final int THRESHOLD_TO_LOG_REGION_DETAILS = 2;
134
135
136
137
138 public static final String HBASE_CLIENT_MAX_PERREQUEST_HEAPSIZE = "hbase.client.max.perrequest.heapsize";
139
140
141
142
143 public static final long DEFAULT_HBASE_CLIENT_MAX_PERREQUEST_HEAPSIZE = 4194304;
144
145
146
147
148 public static final String HBASE_CLIENT_MAX_SUBMIT_HEAPSIZE = "hbase.client.max.submit.heapsize";
149
150
151
152 public static final long DEFAULT_HBASE_CLIENT_MAX_SUBMIT_HEAPSIZE = DEFAULT_HBASE_CLIENT_MAX_PERREQUEST_HEAPSIZE;
153
154
155
156
157
158
159
160 public static interface AsyncRequestFuture {
161 public boolean hasError();
162 public RetriesExhaustedWithDetailsException getErrors();
163 public List<? extends Row> getFailedOperations();
164 public Object[] getResults() throws InterruptedIOException;
165
166 public void waitUntilDone() throws InterruptedIOException;
167 }
168
169
170
171
172 private static final AsyncRequestFuture NO_REQS_RESULT = new AsyncRequestFuture() {
173
174 final Object[] result = new Object[0];
175
176 @Override
177 public boolean hasError() {
178 return false;
179 }
180
181 @Override
182 public RetriesExhaustedWithDetailsException getErrors() {
183 return null;
184 }
185
186 @Override
187 public List<? extends Row> getFailedOperations() {
188 return null;
189 }
190
191 @Override
192 public Object[] getResults() {
193 return result;
194 }
195
196 @Override
197 public void waitUntilDone() throws InterruptedIOException {
198 }
199 };
200
201
202
203
204
205 private static class ReplicaResultState {
206 public ReplicaResultState(int callCount) {
207 this.callCount = callCount;
208 }
209
210
211 int callCount;
212
213
214 BatchErrors replicaErrors = null;
215
216 @Override
217 public String toString() {
218 return "[call count " + callCount + "; errors " + replicaErrors + "]";
219 }
220 }
221
222
223
224 protected final long id;
225
226 protected final ClusterConnection connection;
227 protected final RpcRetryingCallerFactory rpcCallerFactory;
228 protected final RpcControllerFactory rpcFactory;
229 protected final BatchErrors globalErrors;
230 protected final ExecutorService pool;
231
232 protected final AtomicLong tasksInProgress = new AtomicLong(0);
233 protected final ConcurrentMap<byte[], AtomicInteger> taskCounterPerRegion =
234 new ConcurrentSkipListMap<byte[], AtomicInteger>(Bytes.BYTES_COMPARATOR);
235 protected final ConcurrentMap<ServerName, AtomicInteger> taskCounterPerServer =
236 new ConcurrentHashMap<ServerName, AtomicInteger>();
237
238 private final int startLogErrorsCnt;
239
240
241
242
243 protected final int maxTotalConcurrentTasks;
244
245
246
247
248 protected final long maxHeapSizePerRequest;
249 protected final long maxHeapSizeSubmit;
250
251
252
253
254
255
256 protected final int maxConcurrentTasksPerRegion;
257
258
259
260
261 protected final int maxConcurrentTasksPerServer;
262 protected final long pause;
263 protected final long pauseForCQTBE;
264 protected int numTries;
265 protected int serverTrackerTimeout;
266 protected int rpcTimeout;
267 protected int operationTimeout;
268 protected long primaryCallTimeoutMicroseconds;
269
270 private final boolean logBatchErrorDetails;
271
272
273 protected static class BatchErrors {
274 private final List<Throwable> throwables = new ArrayList<Throwable>();
275 private final List<Row> actions = new ArrayList<Row>();
276 private final List<String> addresses = new ArrayList<String>();
277
278 public synchronized void add(Throwable ex, Row row, ServerName serverName) {
279 if (row == null){
280 throw new IllegalArgumentException("row cannot be null. location=" + serverName);
281 }
282
283 throwables.add(ex);
284 actions.add(row);
285 addresses.add(serverName != null ? serverName.toString() : "null");
286 }
287
288 public boolean hasErrors() {
289 return !throwables.isEmpty();
290 }
291
292 private synchronized RetriesExhaustedWithDetailsException makeException(boolean logDetails) {
293 if (logDetails) {
294 LOG.error("Exception occurred! Exception details: " + throwables + ";\nActions: "
295 + actions);
296 }
297 return new RetriesExhaustedWithDetailsException(new ArrayList<Throwable>(throwables),
298 new ArrayList<Row>(actions), new ArrayList<String>(addresses));
299 }
300
301 public synchronized void clear() {
302 throwables.clear();
303 actions.clear();
304 addresses.clear();
305 }
306
307 public synchronized void merge(BatchErrors other) {
308 throwables.addAll(other.throwables);
309 actions.addAll(other.actions);
310 addresses.addAll(other.addresses);
311 }
312 }
313 public AsyncProcess(ClusterConnection hc, Configuration conf, ExecutorService pool,
314 RpcRetryingCallerFactory rpcCaller, boolean useGlobalErrors, RpcControllerFactory rpcFactory,
315 int rpcTimeout) {
316 if (hc == null) {
317 throw new IllegalArgumentException("HConnection cannot be null.");
318 }
319
320 this.connection = hc;
321 this.pool = pool;
322 this.globalErrors = useGlobalErrors ? new BatchErrors() : null;
323
324 this.id = COUNTER.incrementAndGet();
325
326 this.pause = conf.getLong(HConstants.HBASE_CLIENT_PAUSE,
327 HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
328 long configuredPauseForCQTBE = conf.getLong(HConstants.HBASE_CLIENT_PAUSE_FOR_CQTBE, pause);
329 if (configuredPauseForCQTBE < pause) {
330 LOG.warn("The " + HConstants.HBASE_CLIENT_PAUSE_FOR_CQTBE + " setting: "
331 + configuredPauseForCQTBE + " is smaller than " + HConstants.HBASE_CLIENT_PAUSE
332 + ", will use " + pause + " instead.");
333 this.pauseForCQTBE = pause;
334 } else {
335 this.pauseForCQTBE = configuredPauseForCQTBE;
336 }
337 this.numTries = conf.getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER,
338 HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER);
339 this.rpcTimeout = rpcTimeout;
340 this.operationTimeout = conf.getInt(HConstants.HBASE_CLIENT_OPERATION_TIMEOUT,
341 HConstants.DEFAULT_HBASE_CLIENT_OPERATION_TIMEOUT);
342 this.primaryCallTimeoutMicroseconds = conf.getInt(PRIMARY_CALL_TIMEOUT_KEY, 10000);
343
344 this.maxTotalConcurrentTasks = conf.getInt(HConstants.HBASE_CLIENT_MAX_TOTAL_TASKS,
345 HConstants.DEFAULT_HBASE_CLIENT_MAX_TOTAL_TASKS);
346 this.maxConcurrentTasksPerServer = conf.getInt(HConstants.HBASE_CLIENT_MAX_PERSERVER_TASKS,
347 HConstants.DEFAULT_HBASE_CLIENT_MAX_PERSERVER_TASKS);
348 this.maxConcurrentTasksPerRegion = conf.getInt(HConstants.HBASE_CLIENT_MAX_PERREGION_TASKS,
349 HConstants.DEFAULT_HBASE_CLIENT_MAX_PERREGION_TASKS);
350 this.maxHeapSizePerRequest = conf.getLong(HBASE_CLIENT_MAX_PERREQUEST_HEAPSIZE,
351 DEFAULT_HBASE_CLIENT_MAX_PERREQUEST_HEAPSIZE);
352 this.maxHeapSizeSubmit = conf.getLong(HBASE_CLIENT_MAX_SUBMIT_HEAPSIZE, DEFAULT_HBASE_CLIENT_MAX_SUBMIT_HEAPSIZE);
353 this.startLogErrorsCnt =
354 conf.getInt(START_LOG_ERRORS_AFTER_COUNT_KEY, DEFAULT_START_LOG_ERRORS_AFTER_COUNT);
355
356 if (this.maxTotalConcurrentTasks <= 0) {
357 throw new IllegalArgumentException("maxTotalConcurrentTasks=" + maxTotalConcurrentTasks);
358 }
359 if (this.maxConcurrentTasksPerServer <= 0) {
360 throw new IllegalArgumentException("maxConcurrentTasksPerServer=" +
361 maxConcurrentTasksPerServer);
362 }
363 if (this.maxConcurrentTasksPerRegion <= 0) {
364 throw new IllegalArgumentException("maxConcurrentTasksPerRegion=" +
365 maxConcurrentTasksPerRegion);
366 }
367 if (this.maxHeapSizePerRequest <= 0) {
368 throw new IllegalArgumentException("maxHeapSizePerServer=" +
369 maxHeapSizePerRequest);
370 }
371
372 if (this.maxHeapSizeSubmit <= 0) {
373 throw new IllegalArgumentException("maxHeapSizeSubmit=" +
374 maxHeapSizeSubmit);
375 }
376
377
378
379
380
381
382
383 this.serverTrackerTimeout = 0;
384 for (int i = 0; i < this.numTries; ++i) {
385 serverTrackerTimeout = (int) (serverTrackerTimeout + ConnectionUtils.getPauseTime(this.pause, i));
386 }
387
388 this.rpcCallerFactory = rpcCaller;
389 this.rpcFactory = rpcFactory;
390 this.logBatchErrorDetails = conf.getBoolean(LOG_DETAILS_FOR_BATCH_ERROR, false);
391
392 this.thresholdToLogUndoneTaskDetails =
393 conf.getInt(THRESHOLD_TO_LOG_UNDONE_TASK_DETAILS,
394 DEFAULT_THRESHOLD_TO_LOG_UNDONE_TASK_DETAILS);
395 }
396
397 public void setRpcTimeout(int rpcTimeout) {
398 this.rpcTimeout = rpcTimeout;
399 }
400
401 public void setOperationTimeout(int operationTimeout) {
402 this.operationTimeout = operationTimeout;
403 }
404
405
406
407
408
409 @VisibleForTesting
410 ExecutorService getPool(ExecutorService pool) {
411 if (pool != null) {
412 return pool;
413 }
414 if (this.pool != null) {
415 return this.pool;
416 }
417 throw new RuntimeException("Neither AsyncProcess nor request have ExecutorService");
418 }
419
420
421
422
423
424 public <CResult> AsyncRequestFuture submit(TableName tableName, final List<? extends Row> rows,
425 boolean atLeastOne, Batch.Callback<CResult> callback, boolean needResults)
426 throws InterruptedIOException {
427 return submit(null, tableName, rows, atLeastOne, callback, needResults);
428 }
429
430
431
432
433 public <CResult> AsyncRequestFuture submit(TableName tableName,
434 final RowAccess<? extends Row> rows, boolean atLeastOne, Batch.Callback<CResult> callback,
435 boolean needResults) throws InterruptedIOException {
436 return submit(null, tableName, rows, atLeastOne, callback, needResults);
437 }
438
439
440
441
442 public <CResult> AsyncRequestFuture submit(ExecutorService pool, TableName tableName,
443 List<? extends Row> rows, boolean atLeastOne, Batch.Callback<CResult> callback,
444 boolean needResults) throws InterruptedIOException {
445 return submit(pool, tableName, new ListRowAccess(rows), atLeastOne,
446 callback, needResults);
447 }
448
449
450
451
452
453
454
455
456
457
458
459
460
461 public <CResult> AsyncRequestFuture submit(ExecutorService pool, TableName tableName,
462 RowAccess<? extends Row> rows, boolean atLeastOne, Batch.Callback<CResult> callback,
463 boolean needResults) throws InterruptedIOException {
464 if (rows.isEmpty()) {
465 return NO_REQS_RESULT;
466 }
467
468 Map<ServerName, MultiAction<Row>> actionsByServer =
469 new HashMap<ServerName, MultiAction<Row>>();
470 List<Action<Row>> retainedActions = new ArrayList<Action<Row>>(rows.size());
471
472 NonceGenerator ng = this.connection.getNonceGenerator();
473 long nonceGroup = ng.getNonceGroup();
474
475
476 List<Exception> locationErrors = null;
477 List<Integer> locationErrorRows = null;
478 RowCheckerHost checker = createRowCheckerHost();
479 boolean firstIter = true;
480 do {
481
482 waitForMaximumCurrentTasks(maxTotalConcurrentTasks - 1, tableName.getNameAsString());
483 int posInList = -1;
484 if (!firstIter) {
485 checker.reset();
486 }
487 Iterator<? extends Row> it = rows.iterator();
488 while (it.hasNext()) {
489 Row r = it.next();
490 HRegionLocation loc;
491 try {
492 if (r == null) {
493 throw new IllegalArgumentException("#" + id + ", row cannot be null");
494 }
495
496 RegionLocations locs = connection.locateRegion(
497 tableName, r.getRow(), true, true, RegionReplicaUtil.DEFAULT_REPLICA_ID);
498 if (locs == null || locs.isEmpty() || locs.getDefaultRegionLocation() == null) {
499 throw new IOException("#" + id + ", no location found, aborting submit for"
500 + " tableName=" + tableName + " rowkey=" + Bytes.toStringBinary(r.getRow()));
501 }
502 loc = locs.getDefaultRegionLocation();
503 } catch (IOException ex) {
504 locationErrors = new ArrayList<Exception>();
505 locationErrorRows = new ArrayList<Integer>();
506 LOG.error("Failed to get region location ", ex);
507
508
509 int priority = HConstants.NORMAL_QOS;
510 if (r instanceof Mutation) {
511 priority = ((Mutation) r).getPriority();
512 }
513 retainedActions.add(new Action<Row>(r, ++posInList, priority));
514 locationErrors.add(ex);
515 locationErrorRows.add(posInList);
516 it.remove();
517 break;
518 }
519 long rowSize = (r instanceof Mutation) ? ((Mutation) r).heapSize() : 0;
520 ReturnCode code = checker.canTakeOperation(loc, rowSize);
521 if (code == ReturnCode.END) {
522 break;
523 }
524 if (code == ReturnCode.INCLUDE) {
525 int priority = HConstants.NORMAL_QOS;
526 if (r instanceof Mutation) {
527 priority = ((Mutation) r).getPriority();
528 }
529 Action<Row> action = new Action<Row>(r, ++posInList, priority);
530 setNonce(ng, r, action);
531 retainedActions.add(action);
532
533 byte[] regionName = loc.getRegionInfo().getRegionName();
534 addAction(loc.getServerName(), regionName, action, actionsByServer, nonceGroup);
535 it.remove();
536 }
537 }
538 firstIter = false;
539 } while (retainedActions.isEmpty() && atLeastOne && (locationErrors == null));
540
541 if (retainedActions.isEmpty()) return NO_REQS_RESULT;
542
543 return submitMultiActions(tableName, retainedActions, nonceGroup, callback, null, needResults,
544 locationErrors, locationErrorRows, actionsByServer, pool);
545 }
546
547 private RowCheckerHost createRowCheckerHost() {
548 return new RowCheckerHost(Arrays.asList(
549 new TaskCountChecker(maxTotalConcurrentTasks,
550 maxConcurrentTasksPerServer,
551 maxConcurrentTasksPerRegion,
552 tasksInProgress,
553 taskCounterPerServer,
554 taskCounterPerRegion)
555 , new RequestSizeChecker(maxHeapSizePerRequest)
556 , new SubmittedSizeChecker(maxHeapSizeSubmit)
557 ));
558 }
559 <CResult> AsyncRequestFuture submitMultiActions(TableName tableName,
560 List<Action<Row>> retainedActions, long nonceGroup, Batch.Callback<CResult> callback,
561 Object[] results, boolean needResults, List<Exception> locationErrors,
562 List<Integer> locationErrorRows, Map<ServerName, MultiAction<Row>> actionsByServer,
563 ExecutorService pool) {
564 AsyncRequestFutureImpl<CResult> ars = createAsyncRequestFuture(
565 tableName, retainedActions, nonceGroup, pool, callback, results, needResults, null,
566 operationTimeout, rpcTimeout);
567
568 if (locationErrors != null) {
569 for (int i = 0; i < locationErrors.size(); ++i) {
570 int originalIndex = locationErrorRows.get(i);
571 Row row = retainedActions.get(originalIndex).getAction();
572 ars.manageError(originalIndex, row,
573 Retry.NO_LOCATION_PROBLEM, locationErrors.get(i), null);
574 }
575 }
576 ars.sendMultiAction(actionsByServer, 1, null, false);
577 return ars;
578 }
579
580
581
582
583
584
585
586
587
588 private static void addAction(ServerName server, byte[] regionName, Action<Row> action,
589 Map<ServerName, MultiAction<Row>> actionsByServer, long nonceGroup) {
590 MultiAction<Row> multiAction = actionsByServer.get(server);
591 if (multiAction == null) {
592 multiAction = new MultiAction<Row>();
593 actionsByServer.put(server, multiAction);
594 }
595 if (action.hasNonce() && !multiAction.hasNonceGroup()) {
596 multiAction.setNonceGroup(nonceGroup);
597 }
598
599 multiAction.add(regionName, action);
600 }
601
602
603
604
605 public <CResult> AsyncRequestFuture submitAll(TableName tableName,
606 List<? extends Row> rows, Batch.Callback<CResult> callback, Object[] results) {
607 return submitAll(null, tableName, rows, callback, results, null, operationTimeout, rpcTimeout);
608 }
609
610 public <CResult> AsyncRequestFuture submitAll(ExecutorService pool, TableName tableName,
611 List<? extends Row> rows, Batch.Callback<CResult> callback, Object[] results) {
612 return submitAll(pool, tableName, rows, callback, results, null, operationTimeout, rpcTimeout);
613 }
614
615
616
617
618
619
620
621
622
623
624 public <CResult> AsyncRequestFuture submitAll(ExecutorService pool, TableName tableName,
625 List<? extends Row> rows, Batch.Callback<CResult> callback, Object[] results,
626 PayloadCarryingServerCallable callable, int operationTimeout, int rpcTimeout) {
627 List<Action<Row>> actions = new ArrayList<Action<Row>>(rows.size());
628
629
630 int posInList = -1;
631 NonceGenerator ng = this.connection.getNonceGenerator();
632 int highestPriority = HConstants.PRIORITY_UNSET;
633 for (Row r : rows) {
634 posInList++;
635 if (r instanceof Put) {
636 Put put = (Put) r;
637 if (put.isEmpty()) {
638 throw new IllegalArgumentException("No columns to insert for #" + (posInList+1)+ " item");
639 }
640 highestPriority = Math.max(put.getPriority(), highestPriority);
641 }
642 Action<Row> action = new Action<Row>(r, posInList, highestPriority);
643 setNonce(ng, r, action);
644 actions.add(action);
645 }
646 AsyncRequestFutureImpl<CResult> ars = createAsyncRequestFuture(
647 tableName, actions, ng.getNonceGroup(), getPool(pool), callback, results, results != null,
648 callable, operationTimeout, rpcTimeout);
649 ars.groupAndSendMultiAction(actions, 1);
650 return ars;
651 }
652
653 private static void setNonce(NonceGenerator ng, Row r, Action<Row> action) {
654 if (!(r instanceof Append) && !(r instanceof Increment)) return;
655 action.setNonce(ng.newNonce());
656 }
657
658
659
660
661
662
663
664
665
666
667 protected class AsyncRequestFutureImpl<CResult> implements AsyncRequestFuture {
668
669
670
671
672
673
674
675
676 private final class ReplicaCallIssuingRunnable implements Runnable {
677 private final long startTime;
678 private final List<Action<Row>> initialActions;
679
680 public ReplicaCallIssuingRunnable(List<Action<Row>> initialActions, long startTime) {
681 this.initialActions = initialActions;
682 this.startTime = startTime;
683 }
684
685 @Override
686 public void run() {
687 boolean done = false;
688 if (primaryCallTimeoutMicroseconds > 0) {
689 try {
690 done = waitUntilDone(startTime * 1000L + primaryCallTimeoutMicroseconds);
691 } catch (InterruptedException ex) {
692 LOG.error("Replica thread was interrupted - no replica calls: " + ex.getMessage());
693 return;
694 }
695 }
696 if (done) return;
697 Map<ServerName, MultiAction<Row>> actionsByServer =
698 new HashMap<ServerName, MultiAction<Row>>();
699 List<Action<Row>> unknownLocActions = new ArrayList<Action<Row>>();
700 if (replicaGetIndices == null) {
701 for (int i = 0; i < results.length; ++i) {
702 addReplicaActions(i, actionsByServer, unknownLocActions);
703 }
704 } else {
705 for (int replicaGetIndice : replicaGetIndices) {
706 addReplicaActions(replicaGetIndice, actionsByServer, unknownLocActions);
707 }
708 }
709 if (!actionsByServer.isEmpty()) {
710 sendMultiAction(actionsByServer, 1, null, unknownLocActions.isEmpty());
711 }
712 if (!unknownLocActions.isEmpty()) {
713 actionsByServer = new HashMap<ServerName, MultiAction<Row>>();
714 for (Action<Row> action : unknownLocActions) {
715 addReplicaActionsAgain(action, actionsByServer);
716 }
717
718 if (!actionsByServer.isEmpty()) {
719 sendMultiAction(actionsByServer, 1, null, true);
720 }
721 }
722 }
723
724
725
726
727
728
729 private void addReplicaActions(int index, Map<ServerName, MultiAction<Row>> actionsByServer,
730 List<Action<Row>> unknownReplicaActions) {
731 if (results[index] != null) return;
732 Action<Row> action = initialActions.get(index);
733 RegionLocations loc = findAllLocationsOrFail(action, true);
734 if (loc == null) return;
735 HRegionLocation[] locs = loc.getRegionLocations();
736 if (locs.length == 1) {
737 LOG.warn("No replicas found for " + action.getAction());
738 return;
739 }
740 synchronized (replicaResultLock) {
741
742
743
744 if (results[index] != null) return;
745
746
747 results[index] = new ReplicaResultState(locs.length);
748 }
749 for (int i = 1; i < locs.length; ++i) {
750 Action<Row> replicaAction = new Action<Row>(action, i);
751 if (locs[i] != null) {
752 addAction(locs[i].getServerName(), locs[i].getRegionInfo().getRegionName(),
753 replicaAction, actionsByServer, nonceGroup);
754 } else {
755 unknownReplicaActions.add(replicaAction);
756 }
757 }
758 }
759
760 private void addReplicaActionsAgain(
761 Action<Row> action, Map<ServerName, MultiAction<Row>> actionsByServer) {
762 if (action.getReplicaId() == RegionReplicaUtil.DEFAULT_REPLICA_ID) {
763 throw new AssertionError("Cannot have default replica here");
764 }
765 HRegionLocation loc = getReplicaLocationOrFail(action);
766 if (loc == null) return;
767 addAction(loc.getServerName(), loc.getRegionInfo().getRegionName(),
768 action, actionsByServer, nonceGroup);
769 }
770 }
771
772
773
774
775
776 @VisibleForTesting
777 class SingleServerRequestRunnable implements Runnable {
778 private final MultiAction<Row> multiAction;
779 private final int numAttempt;
780 private final ServerName server;
781 private final Set<PayloadCarryingServerCallable> callsInProgress;
782 @VisibleForTesting
783 SingleServerRequestRunnable(
784 MultiAction<Row> multiAction, int numAttempt, ServerName server,
785 Set<PayloadCarryingServerCallable> callsInProgress) {
786 this.multiAction = multiAction;
787 this.numAttempt = numAttempt;
788 this.server = server;
789 this.callsInProgress = callsInProgress;
790 }
791
792 @Override
793 public void run() {
794 MultiResponse res = null;
795 PayloadCarryingServerCallable callable = currentCallable;
796 try {
797
798 if (callable == null) {
799 callable = createCallable(server, tableName, multiAction);
800 }
801 RpcRetryingCaller<MultiResponse> caller = createCaller(callable, rpcTimeout);
802 try {
803 if (callsInProgress != null) {
804 callsInProgress.add(callable);
805 }
806 res = caller.callWithoutRetries(callable, operationTimeout);
807 if (res == null) {
808
809 return;
810 }
811 } catch (IOException e) {
812
813
814 receiveGlobalFailure(multiAction, server, numAttempt, e);
815 return;
816 } catch (Throwable t) {
817
818 LOG.error("#" + id + ", Caught throwable while calling. This is unexpected." +
819 " Retrying. Server is " + server + ", tableName=" + tableName, t);
820 receiveGlobalFailure(multiAction, server, numAttempt, t);
821 return;
822 }
823
824
825 receiveMultiAction(multiAction, server, res, numAttempt);
826 } catch (Throwable t) {
827
828 LOG.error("Internal AsyncProcess #" + id + " error for "
829 + tableName + " processing for " + server, t);
830 throw new RuntimeException(t);
831 } finally {
832 decTaskCounters(multiAction.getRegions(), server);
833 if (callsInProgress != null && callable != null && res != null) {
834 callsInProgress.remove(callable);
835 }
836 }
837 }
838 }
839
840 private final Batch.Callback<CResult> callback;
841 private final BatchErrors errors;
842 private final ConnectionManager.ServerErrorTracker errorsByServer;
843 private final ExecutorService pool;
844 private final Set<PayloadCarryingServerCallable> callsInProgress;
845
846
847 private final TableName tableName;
848 private final AtomicLong actionsInProgress = new AtomicLong(-1);
849
850
851
852
853
854 private final Object replicaResultLock = new Object();
855
856
857
858
859
860
861
862
863
864 private final Object[] results;
865
866
867
868 private final int[] replicaGetIndices;
869 private final boolean hasAnyReplicaGets;
870 private final long nonceGroup;
871 private PayloadCarryingServerCallable currentCallable;
872 private int operationTimeout;
873 private int rpcTimeout;
874 private RetryingTimeTracker tracker;
875
876 public AsyncRequestFutureImpl(TableName tableName, List<Action<Row>> actions, long nonceGroup,
877 ExecutorService pool, boolean needResults, Object[] results,
878 Batch.Callback<CResult> callback, PayloadCarryingServerCallable callable,
879 int operationTimeout, int rpcTimeout) {
880 this.pool = pool;
881 this.callback = callback;
882 this.nonceGroup = nonceGroup;
883 this.tableName = tableName;
884 this.actionsInProgress.set(actions.size());
885 if (results != null) {
886 assert needResults;
887 if (results.length != actions.size()) {
888 throw new AssertionError("results.length");
889 }
890 this.results = results;
891 for (int i = 0; i != this.results.length; ++i) {
892 results[i] = null;
893 }
894 } else {
895 this.results = needResults ? new Object[actions.size()] : null;
896 }
897 List<Integer> replicaGetIndices = null;
898 boolean hasAnyReplicaGets = false;
899 if (needResults) {
900
901
902
903
904
905 boolean hasAnyNonReplicaReqs = false;
906 int posInList = 0;
907 for (Action<Row> action : actions) {
908 boolean isReplicaGet = isReplicaGet(action.getAction());
909 if (isReplicaGet) {
910 hasAnyReplicaGets = true;
911 if (hasAnyNonReplicaReqs) {
912 if (replicaGetIndices == null) {
913 replicaGetIndices = new ArrayList<Integer>(actions.size() - 1);
914 }
915 replicaGetIndices.add(posInList);
916 }
917 } else if (!hasAnyNonReplicaReqs) {
918
919 hasAnyNonReplicaReqs = true;
920 if (posInList > 0) {
921
922
923 replicaGetIndices = new ArrayList<Integer>(actions.size() - 1);
924 for (int i = 0; i < posInList; ++i) {
925 replicaGetIndices.add(i);
926 }
927 }
928 }
929 ++posInList;
930 }
931 }
932 this.hasAnyReplicaGets = hasAnyReplicaGets;
933 if (replicaGetIndices != null) {
934 this.replicaGetIndices = new int[replicaGetIndices.size()];
935 int i = 0;
936 for (Integer el : replicaGetIndices) {
937 this.replicaGetIndices[i++] = el;
938 }
939 } else {
940 this.replicaGetIndices = null;
941 }
942 this.callsInProgress = !hasAnyReplicaGets ? null :
943 Collections.newSetFromMap(
944 new ConcurrentHashMap<PayloadCarryingServerCallable, Boolean>());
945
946 this.errorsByServer = createServerErrorTracker();
947 this.errors = (globalErrors != null) ? globalErrors : new BatchErrors();
948 this.currentCallable = callable;
949 this.operationTimeout = operationTimeout;
950 this.rpcTimeout = rpcTimeout;
951 if (callable == null) {
952 tracker = new RetryingTimeTracker();
953 tracker.start();
954 }
955 }
956
957 public Set<PayloadCarryingServerCallable> getCallsInProgress() {
958 return callsInProgress;
959 }
960
961 @VisibleForTesting
962 SingleServerRequestRunnable createSingleServerRequest(MultiAction<Row> multiAction, int numAttempt, ServerName server,
963 Set<PayloadCarryingServerCallable> callsInProgress) {
964 return new SingleServerRequestRunnable(multiAction, numAttempt, server, callsInProgress);
965 }
966
967
968
969
970
971
972
973 private void groupAndSendMultiAction(List<Action<Row>> currentActions, int numAttempt) {
974 Map<ServerName, MultiAction<Row>> actionsByServer =
975 new HashMap<ServerName, MultiAction<Row>>();
976
977 boolean isReplica = false;
978 List<Action<Row>> unknownReplicaActions = null;
979 for (Action<Row> action : currentActions) {
980 RegionLocations locs = findAllLocationsOrFail(action, true);
981 if (locs == null) continue;
982 boolean isReplicaAction = !RegionReplicaUtil.isDefaultReplica(action.getReplicaId());
983 if (isReplica && !isReplicaAction) {
984
985 throw new AssertionError("Replica and non-replica actions in the same retry");
986 }
987 isReplica = isReplicaAction;
988 HRegionLocation loc = locs.getRegionLocation(action.getReplicaId());
989 if (loc == null || loc.getServerName() == null) {
990 if (isReplica) {
991 if (unknownReplicaActions == null) {
992 unknownReplicaActions = new ArrayList<Action<Row>>();
993 }
994 unknownReplicaActions.add(action);
995 } else {
996
997 manageLocationError(action, null);
998 }
999 } else {
1000 byte[] regionName = loc.getRegionInfo().getRegionName();
1001 addAction(loc.getServerName(), regionName, action, actionsByServer, nonceGroup);
1002 }
1003 }
1004 boolean doStartReplica = (numAttempt == 1 && !isReplica && hasAnyReplicaGets);
1005 boolean hasUnknown = unknownReplicaActions != null && !unknownReplicaActions.isEmpty();
1006
1007 if (!actionsByServer.isEmpty()) {
1008
1009 sendMultiAction(actionsByServer, numAttempt, (doStartReplica && !hasUnknown)
1010 ? currentActions : null, numAttempt > 1 && !hasUnknown);
1011 }
1012
1013 if (hasUnknown) {
1014 actionsByServer = new HashMap<ServerName, MultiAction<Row>>();
1015 for (Action<Row> action : unknownReplicaActions) {
1016 HRegionLocation loc = getReplicaLocationOrFail(action);
1017 if (loc == null) continue;
1018 byte[] regionName = loc.getRegionInfo().getRegionName();
1019 addAction(loc.getServerName(), regionName, action, actionsByServer, nonceGroup);
1020 }
1021 if (!actionsByServer.isEmpty()) {
1022 sendMultiAction(
1023 actionsByServer, numAttempt, doStartReplica ? currentActions : null, true);
1024 }
1025 }
1026 }
1027
1028 private HRegionLocation getReplicaLocationOrFail(Action<Row> action) {
1029
1030
1031 int replicaId = action.getReplicaId();
1032 RegionLocations locs = findAllLocationsOrFail(action, true);
1033 if (locs == null) return null;
1034 HRegionLocation loc = locs.getRegionLocation(replicaId);
1035 if (loc == null || loc.getServerName() == null) {
1036 locs = findAllLocationsOrFail(action, false);
1037 if (locs == null) return null;
1038 loc = locs.getRegionLocation(replicaId);
1039 }
1040 if (loc == null || loc.getServerName() == null) {
1041 manageLocationError(action, null);
1042 return null;
1043 }
1044 return loc;
1045 }
1046
1047 private void manageLocationError(Action<Row> action, Exception ex) {
1048 String msg = "Cannot get replica " + action.getReplicaId()
1049 + " location for " + action.getAction();
1050 LOG.error(msg);
1051 if (ex == null) {
1052 ex = new IOException(msg);
1053 }
1054 manageError(action.getOriginalIndex(), action.getAction(),
1055 Retry.NO_LOCATION_PROBLEM, ex, null);
1056 }
1057
1058 private RegionLocations findAllLocationsOrFail(Action<Row> action, boolean useCache) {
1059 if (action.getAction() == null) throw new IllegalArgumentException("#" + id +
1060 ", row cannot be null");
1061 RegionLocations loc = null;
1062 try {
1063 loc = connection.locateRegion(
1064 tableName, action.getAction().getRow(), useCache, true, action.getReplicaId());
1065 } catch (IOException ex) {
1066 manageLocationError(action, ex);
1067 }
1068 return loc;
1069 }
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079 private void sendMultiAction(Map<ServerName, MultiAction<Row>> actionsByServer,
1080 int numAttempt, List<Action<Row>> actionsForReplicaThread, boolean reuseThread) {
1081
1082
1083 int actionsRemaining = actionsByServer.size();
1084
1085 for (Map.Entry<ServerName, MultiAction<Row>> e : actionsByServer.entrySet()) {
1086 ServerName server = e.getKey();
1087 MultiAction<Row> multiAction = e.getValue();
1088 Collection<? extends Runnable> runnables = getNewMultiActionRunnable(server, multiAction,
1089 numAttempt);
1090
1091
1092 if (runnables.size() > actionsRemaining) {
1093 actionsRemaining = runnables.size();
1094 }
1095
1096
1097
1098
1099 for (Runnable runnable : runnables) {
1100 if ((--actionsRemaining == 0) && reuseThread
1101 && numAttempt % HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER != 0) {
1102 runnable.run();
1103 } else {
1104 try {
1105 pool.submit(runnable);
1106 } catch (Throwable t) {
1107 if (t instanceof RejectedExecutionException) {
1108
1109
1110 LOG.warn("#" + id + ", the task was rejected by the pool. This is unexpected." +
1111 " Server is " + server.getServerName(), t);
1112 } else {
1113
1114 LOG.warn("Caught unexpected exception/error: ", t);
1115 }
1116 decTaskCounters(multiAction.getRegions(), server);
1117
1118
1119 receiveGlobalFailure(multiAction, server, numAttempt, t);
1120 }
1121 }
1122 }
1123 }
1124
1125 if (actionsForReplicaThread != null) {
1126 startWaitingForReplicaCalls(actionsForReplicaThread);
1127 }
1128 }
1129
1130 private Collection<? extends Runnable> getNewMultiActionRunnable(ServerName server,
1131 MultiAction<Row> multiAction,
1132 int numAttempt) {
1133
1134 if (AsyncProcess.this.connection.getStatisticsTracker() == null) {
1135 if (connection.getConnectionMetrics() != null) {
1136 connection.getConnectionMetrics().incrNormalRunners();
1137 }
1138 incTaskCounters(multiAction.getRegions(), server);
1139 SingleServerRequestRunnable runnable = createSingleServerRequest(multiAction, numAttempt, server, callsInProgress);
1140 return Collections.singletonList(Trace.wrap("AsyncProcess.sendMultiAction", runnable));
1141 }
1142
1143
1144 Map<Long, DelayingRunner> actions = new HashMap<Long, DelayingRunner>(multiAction
1145 .size());
1146
1147
1148 for (Map.Entry<byte[], List<Action<Row>>> e : multiAction.actions.entrySet()) {
1149 Long backoff = getBackoff(server, e.getKey());
1150 DelayingRunner runner = actions.get(backoff);
1151 if (runner == null) {
1152 actions.put(backoff, new DelayingRunner(backoff, e));
1153 } else {
1154 runner.add(e);
1155 }
1156 }
1157
1158 List<Runnable> toReturn = new ArrayList<Runnable>(actions.size());
1159 for (DelayingRunner runner : actions.values()) {
1160 incTaskCounters(runner.getActions().getRegions(), server);
1161 String traceText = "AsyncProcess.sendMultiAction";
1162 Runnable runnable = createSingleServerRequest(runner.getActions(), numAttempt, server, callsInProgress);
1163
1164 if (runner.getSleepTime() > 0) {
1165 runner.setRunner(runnable);
1166 traceText = "AsyncProcess.clientBackoff.sendMultiAction";
1167 runnable = runner;
1168 if (connection.getConnectionMetrics() != null) {
1169 connection.getConnectionMetrics().incrDelayRunners();
1170 connection.getConnectionMetrics().updateDelayInterval(runner.getSleepTime());
1171 }
1172 } else {
1173 if (connection.getConnectionMetrics() != null) {
1174 connection.getConnectionMetrics().incrNormalRunners();
1175 }
1176 }
1177 runnable = Trace.wrap(traceText, runnable);
1178 toReturn.add(runnable);
1179
1180 }
1181 return toReturn;
1182 }
1183
1184
1185
1186
1187
1188
1189
1190 private Long getBackoff(ServerName server, byte[] regionName) {
1191 ServerStatisticTracker tracker = AsyncProcess.this.connection.getStatisticsTracker();
1192 ServerStatistics stats = tracker.getStats(server);
1193 return AsyncProcess.this.connection.getBackoffPolicy()
1194 .getBackoffTime(server, regionName, stats);
1195 }
1196
1197
1198
1199
1200 private void startWaitingForReplicaCalls(List<Action<Row>> actionsForReplicaThread) {
1201 long startTime = EnvironmentEdgeManager.currentTime();
1202 ReplicaCallIssuingRunnable replicaRunnable = new ReplicaCallIssuingRunnable(
1203 actionsForReplicaThread, startTime);
1204 if (primaryCallTimeoutMicroseconds == 0) {
1205
1206 replicaRunnable.run();
1207 } else {
1208
1209
1210 try {
1211 pool.submit(replicaRunnable);
1212 } catch (RejectedExecutionException ree) {
1213 LOG.warn("#" + id + ", replica task was rejected by the pool - no replica calls", ree);
1214 }
1215 }
1216 }
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228 public Retry manageError(int originalIndex, Row row, Retry canRetry,
1229 Throwable throwable, ServerName server) {
1230 if (canRetry == Retry.YES
1231 && throwable != null && (throwable instanceof DoNotRetryIOException ||
1232 throwable instanceof NeedUnmanagedConnectionException)) {
1233 canRetry = Retry.NO_NOT_RETRIABLE;
1234 }
1235
1236 if (canRetry != Retry.YES) {
1237
1238 setError(originalIndex, row, throwable, server);
1239 } else if (isActionComplete(originalIndex, row)) {
1240 canRetry = Retry.NO_OTHER_SUCCEEDED;
1241 }
1242 return canRetry;
1243 }
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253 private void receiveGlobalFailure(
1254 MultiAction<Row> rsActions, ServerName server, int numAttempt, Throwable t) {
1255 errorsByServer.reportServerError(server);
1256 Retry canRetry = errorsByServer.canRetryMore(numAttempt)
1257 ? Retry.YES : Retry.NO_RETRIES_EXHAUSTED;
1258
1259 if (tableName == null && ClientExceptionsUtil.isMetaClearingException(t)) {
1260
1261 connection.clearCaches(server);
1262 }
1263 int failed = 0, stopped = 0;
1264 List<Action<Row>> toReplay = new ArrayList<Action<Row>>();
1265 for (Map.Entry<byte[], List<Action<Row>>> e : rsActions.actions.entrySet()) {
1266 byte[] regionName = e.getKey();
1267 byte[] row = e.getValue().iterator().next().getAction().getRow();
1268
1269
1270 try {
1271 if (tableName != null) {
1272 connection.updateCachedLocations(tableName, regionName, row,
1273 ClientExceptionsUtil.isMetaClearingException(t) ? null : t, server);
1274 }
1275 } catch (Throwable ex) {
1276
1277
1278 LOG.error("Couldn't update cached region locations: " + ex);
1279 }
1280 for (Action<Row> action : e.getValue()) {
1281 Retry retry = manageError(
1282 action.getOriginalIndex(), action.getAction(), canRetry, t, server);
1283 if (retry == Retry.YES) {
1284 toReplay.add(action);
1285 } else if (retry == Retry.NO_OTHER_SUCCEEDED) {
1286 ++stopped;
1287 } else {
1288 ++failed;
1289 }
1290 }
1291 }
1292
1293 if (toReplay.isEmpty()) {
1294 logNoResubmit(server, numAttempt, rsActions.size(), t, failed, stopped);
1295 } else {
1296 resubmit(server, toReplay, numAttempt, rsActions.size(), t);
1297 }
1298 }
1299
1300
1301
1302
1303
1304 private void resubmit(ServerName oldServer, List<Action<Row>> toReplay,
1305 int numAttempt, int failureCount, Throwable throwable) {
1306
1307
1308
1309
1310
1311
1312
1313
1314 boolean retryImmediately = throwable instanceof RetryImmediatelyException;
1315 int nextAttemptNumber = retryImmediately ? numAttempt : numAttempt + 1;
1316 long backOffTime;
1317 if (retryImmediately) {
1318 backOffTime = 0;
1319 } else if (throwable instanceof CallQueueTooBigException) {
1320
1321 backOffTime = errorsByServer.calculateBackoffTime(oldServer, pauseForCQTBE);
1322 } else {
1323 backOffTime = errorsByServer.calculateBackoffTime(oldServer, pause);
1324 }
1325 if (numAttempt > startLogErrorsCnt) {
1326
1327
1328 LOG.info(createLog(numAttempt, failureCount, toReplay.size(),
1329 oldServer, throwable, backOffTime, true, null, -1, -1));
1330 }
1331
1332 try {
1333 if (backOffTime > 0) {
1334 Thread.sleep(backOffTime);
1335 }
1336 } catch (InterruptedException e) {
1337 LOG.warn("#" + id + ", not sent: " + toReplay.size() + " operations, " + oldServer, e);
1338 Thread.currentThread().interrupt();
1339 return;
1340 }
1341
1342 groupAndSendMultiAction(toReplay, nextAttemptNumber);
1343 }
1344
1345 private void logNoResubmit(ServerName oldServer, int numAttempt,
1346 int failureCount, Throwable throwable, int failed, int stopped) {
1347 if (failureCount != 0 || numAttempt > startLogErrorsCnt + 1) {
1348 String timeStr = new Date(errorsByServer.getStartTrackingTime()).toString();
1349 String logMessage = createLog(numAttempt, failureCount, 0, oldServer,
1350 throwable, -1, false, timeStr, failed, stopped);
1351 if (failed != 0) {
1352
1353 LOG.warn(logMessage);
1354 } else {
1355 LOG.info(logMessage);
1356 }
1357 }
1358 }
1359
1360 @VisibleForTesting
1361 long getActionsInProgress() {
1362 return actionsInProgress.get();
1363 }
1364
1365
1366
1367
1368
1369
1370
1371
1372 private void receiveMultiAction(MultiAction<Row> multiAction,
1373 ServerName server, MultiResponse responses, int numAttempt) {
1374 assert responses != null;
1375
1376
1377
1378
1379
1380
1381
1382 List<Action<Row>> toReplay = new ArrayList<Action<Row>>();
1383 Throwable throwable = null;
1384 int failureCount = 0;
1385 Retry retry = null;
1386
1387 Map<byte[], MultiResponse.RegionResult> results = responses.getResults();
1388 updateStats(server, results);
1389
1390 int failed = 0;
1391 int stopped = 0;
1392
1393 for (Map.Entry<byte[], List<Action<Row>>> regionEntry : multiAction.actions.entrySet()) {
1394 byte[] regionName = regionEntry.getKey();
1395 Throwable regionException = responses.getExceptions().get(regionName);
1396 if (tableName == null && ClientExceptionsUtil.isMetaClearingException(regionException)) {
1397
1398
1399
1400 connection.clearCaches(server);
1401 }
1402 Map<Integer, Object> regionResults;
1403 if (results.containsKey(regionName)) {
1404 regionResults = results.get(regionName).result;
1405 } else {
1406 regionResults = Collections.emptyMap();
1407 }
1408 boolean regionFailureRegistered = false;
1409 for (Action<Row> sentAction : regionEntry.getValue()) {
1410 Object result = regionResults.get(sentAction.getOriginalIndex());
1411 if (result == null) {
1412 if (regionException == null) {
1413 LOG.error("Server sent us neither results nor exceptions for " + Bytes
1414 .toStringBinary(regionName) + ", numAttempt:" + numAttempt);
1415 regionException = new RuntimeException("Invalid response");
1416 }
1417
1418
1419 result = regionException;
1420 }
1421
1422 if (result instanceof Throwable) {
1423 Row row = sentAction.getAction();
1424 throwable = regionException != null ? regionException
1425 : ClientExceptionsUtil.findException(result);
1426
1427 if (!regionFailureRegistered) {
1428 regionFailureRegistered = true;
1429 try {
1430 connection.updateCachedLocations(
1431 tableName, regionName, row.getRow(), result, server);
1432 } catch (Throwable ex) {
1433
1434
1435 LOG.error("Couldn't update cached region locations: " + ex);
1436 }
1437 }
1438 if (retry == null) {
1439 errorsByServer.reportServerError(server);
1440
1441 retry =
1442 errorsByServer.canRetryMore(numAttempt) ? Retry.YES : Retry.NO_RETRIES_EXHAUSTED;
1443 }
1444 ++failureCount;
1445 switch (manageError(sentAction.getOriginalIndex(), row, retry, (Throwable) result,
1446 server)) {
1447 case YES:
1448 toReplay.add(sentAction);
1449 break;
1450 case NO_OTHER_SUCCEEDED:
1451 ++stopped;
1452 break;
1453 default:
1454 ++failed;
1455 break;
1456 }
1457 } else {
1458 if (callback != null) {
1459 try {
1460
1461
1462 this.callback.update(regionName, sentAction.getAction().getRow(), (CResult) result);
1463 } catch (Throwable t) {
1464 LOG.error("User callback threw an exception for "
1465 + Bytes.toStringBinary(regionName) + ", ignoring", t);
1466 }
1467 }
1468 setResult(sentAction, result);
1469 }
1470 }
1471 }
1472 if (toReplay.isEmpty()) {
1473 logNoResubmit(server, numAttempt, failureCount, throwable, failed, stopped);
1474 } else {
1475 resubmit(server, toReplay, numAttempt, failureCount, throwable);
1476 }
1477 }
1478
1479 private String createLog(int numAttempt, int failureCount, int replaySize, ServerName sn,
1480 Throwable error, long backOffTime, boolean willRetry, String startTime,
1481 int failed, int stopped) {
1482 StringBuilder sb = new StringBuilder();
1483 sb.append("#").append(id).append(", table=").append(tableName).append(", ")
1484 .append("attempt=").append(numAttempt)
1485 .append("/").append(numTries).append(" ");
1486
1487 if (failureCount > 0 || error != null){
1488 sb.append("failed=").append(failureCount).append("ops").append(", last exception: ").
1489 append(error == null ? "null" : error);
1490 } else {
1491 sb.append("succeeded");
1492 }
1493
1494 sb.append(" on ").append(sn).append(", tracking started ").append(startTime);
1495
1496 if (willRetry) {
1497 sb.append(", retrying after=").append(backOffTime).append("ms").
1498 append(", replay=").append(replaySize).append("ops");
1499 } else if (failureCount > 0) {
1500 if (stopped > 0) {
1501 sb.append("; not retrying ").append(stopped).append(" due to success from other replica");
1502 }
1503 if (failed > 0) {
1504 sb.append("; not retrying ").append(failed).append(" - final failure");
1505 }
1506
1507 }
1508
1509 return sb.toString();
1510 }
1511
1512
1513
1514
1515
1516
1517 private void setResult(Action<Row> action, Object result) {
1518 if (result == null) {
1519 throw new RuntimeException("Result cannot be null");
1520 }
1521 ReplicaResultState state = null;
1522 boolean isStale = !RegionReplicaUtil.isDefaultReplica(action.getReplicaId());
1523 int index = action.getOriginalIndex();
1524 if (results == null) {
1525 decActionCounter(index);
1526 return;
1527 } else if ((state = trySetResultSimple(
1528 index, action.getAction(), false, result, null, isStale)) == null) {
1529 return;
1530 }
1531 assert state != null;
1532
1533
1534
1535
1536
1537 synchronized (state) {
1538 if (state.callCount == 0) {
1539 return;
1540 }
1541 state.callCount = 0;
1542 }
1543 synchronized (replicaResultLock) {
1544 if (results[index] != state) {
1545 throw new AssertionError("We set the callCount but someone else replaced the result");
1546 }
1547 results[index] = result;
1548 }
1549
1550 decActionCounter(index);
1551 }
1552
1553
1554
1555
1556
1557
1558
1559
1560 private void setError(int index, Row row, Throwable throwable, ServerName server) {
1561 ReplicaResultState state = null;
1562 if (results == null) {
1563
1564
1565
1566 errors.add(throwable, row, server);
1567 decActionCounter(index);
1568 return;
1569 } else if ((state = trySetResultSimple(
1570 index, row, true, throwable, server, false)) == null) {
1571 return;
1572 }
1573 assert state != null;
1574 BatchErrors target = null;
1575 boolean isActionDone = false;
1576 synchronized (state) {
1577 switch (state.callCount) {
1578 case 0: return;
1579 case 1: {
1580 target = errors;
1581 isActionDone = true;
1582 break;
1583 }
1584 default: {
1585 assert state.callCount > 1;
1586 if (state.replicaErrors == null) {
1587 state.replicaErrors = new BatchErrors();
1588 }
1589 target = state.replicaErrors;
1590 break;
1591 }
1592 }
1593 --state.callCount;
1594 }
1595 target.add(throwable, row, server);
1596 if (isActionDone) {
1597 if (state.replicaErrors != null) {
1598 errors.merge(state.replicaErrors);
1599 }
1600
1601 synchronized (replicaResultLock) {
1602 if (results[index] != state) {
1603 throw new AssertionError("We set the callCount but someone else replaced the result");
1604 }
1605 results[index] = throwable;
1606 }
1607 decActionCounter(index);
1608 }
1609 }
1610
1611
1612
1613
1614
1615
1616
1617
1618 private boolean isActionComplete(int index, Row row) {
1619 if (!isReplicaGet(row)) return false;
1620 Object resObj = results[index];
1621 return (resObj != null) && (!(resObj instanceof ReplicaResultState)
1622 || ((ReplicaResultState)resObj).callCount == 0);
1623 }
1624
1625
1626
1627
1628
1629 private ReplicaResultState trySetResultSimple(int index, Row row, boolean isError,
1630 Object result, ServerName server, boolean isFromReplica) {
1631 Object resObj = null;
1632 if (!isReplicaGet(row)) {
1633 if (isFromReplica) {
1634 throw new AssertionError("Unexpected stale result for " + row);
1635 }
1636 results[index] = result;
1637 } else {
1638 synchronized (replicaResultLock) {
1639 if ((resObj = results[index]) == null) {
1640 if (isFromReplica) {
1641 throw new AssertionError("Unexpected stale result for " + row);
1642 }
1643 results[index] = result;
1644 }
1645 }
1646 }
1647
1648 ReplicaResultState rrs =
1649 (resObj instanceof ReplicaResultState) ? (ReplicaResultState)resObj : null;
1650 if (rrs == null && isError) {
1651
1652 errors.add((Throwable)result, row, server);
1653 }
1654
1655 if (resObj == null) {
1656
1657 decActionCounter(index);
1658 return null;
1659 }
1660 return rrs;
1661 }
1662
1663 private void decActionCounter(int index) {
1664 long actionsRemaining = actionsInProgress.decrementAndGet();
1665 if (actionsRemaining < 0) {
1666 String error = buildDetailedErrorMsg("Incorrect actions in progress", index);
1667 throw new AssertionError(error);
1668 } else if (actionsRemaining == 0) {
1669 synchronized (actionsInProgress) {
1670 actionsInProgress.notifyAll();
1671 }
1672 }
1673 }
1674
1675 private String buildDetailedErrorMsg(String string, int index) {
1676 StringBuilder error = new StringBuilder(string);
1677 error.append("; called for ").
1678 append(index).
1679 append(", actionsInProgress ").
1680 append(actionsInProgress.get()).
1681 append("; replica gets: ");
1682 if (replicaGetIndices != null) {
1683 for (int i = 0; i < replicaGetIndices.length; ++i) {
1684 error.append(replicaGetIndices[i]).append(", ");
1685 }
1686 } else {
1687 error.append(hasAnyReplicaGets ? "all" : "none");
1688 }
1689 error.append("; results ");
1690 if (results != null) {
1691 for (int i = 0; i < results.length; ++i) {
1692 Object o = results[i];
1693 error.append(((o == null) ? "null" : o.toString())).append(", ");
1694 }
1695 }
1696 return error.toString();
1697 }
1698
1699 @Override
1700 public void waitUntilDone() throws InterruptedIOException {
1701 try {
1702 waitUntilDone(Long.MAX_VALUE);
1703 } catch (InterruptedException iex) {
1704 throw new InterruptedIOException(iex.getMessage());
1705 } finally {
1706 if (callsInProgress != null) {
1707 for (PayloadCarryingServerCallable clb : callsInProgress) {
1708 clb.cancel();
1709 }
1710 }
1711 }
1712 }
1713
1714 private boolean waitUntilDone(long cutoff) throws InterruptedException {
1715 boolean hasWait = cutoff != Long.MAX_VALUE;
1716 long lastLog = EnvironmentEdgeManager.currentTime();
1717 long currentInProgress;
1718 while (0 != (currentInProgress = actionsInProgress.get())) {
1719 long now = EnvironmentEdgeManager.currentTime();
1720 if (hasWait && (now * 1000L) > cutoff) {
1721 return false;
1722 }
1723 if (!hasWait) {
1724 if (now > lastLog + 10000) {
1725 lastLog = now;
1726 LOG.info("#" + id + ", waiting for " + currentInProgress
1727 + " actions to finish on table: " + tableName);
1728 if (currentInProgress <= thresholdToLogUndoneTaskDetails) {
1729 logDetailsOfUndoneTasks(currentInProgress);
1730 }
1731 }
1732 }
1733 synchronized (actionsInProgress) {
1734 if (actionsInProgress.get() == 0) break;
1735 if (!hasWait) {
1736 actionsInProgress.wait(10);
1737 } else {
1738 long waitMicroSecond = Math.min(100000L, (cutoff - now * 1000L));
1739 TimeUnit.MICROSECONDS.timedWait(actionsInProgress, waitMicroSecond);
1740 }
1741 }
1742 }
1743 return true;
1744 }
1745
1746 @Override
1747 public boolean hasError() {
1748 return errors.hasErrors();
1749 }
1750
1751 @Override
1752 public List<? extends Row> getFailedOperations() {
1753 return errors.actions;
1754 }
1755
1756 @Override
1757 public RetriesExhaustedWithDetailsException getErrors() {
1758 return errors.makeException(logBatchErrorDetails);
1759 }
1760
1761 @Override
1762 public Object[] getResults() throws InterruptedIOException {
1763 waitUntilDone();
1764 return results;
1765 }
1766
1767
1768
1769
1770 @VisibleForTesting
1771 protected MultiServerCallable<Row> createCallable(final ServerName server,
1772 TableName tableName, final MultiAction<Row> multi) {
1773 return new MultiServerCallable<Row>(connection, tableName, server,
1774 AsyncProcess.this.rpcFactory, multi, rpcTimeout, tracker, multi.getPriority());
1775 }
1776 }
1777
1778 @VisibleForTesting
1779 protected void updateStats(ServerName server, Map<byte[], MultiResponse.RegionResult> results) {
1780 boolean metrics = AsyncProcess.this.connection.getConnectionMetrics() != null;
1781 boolean stats = AsyncProcess.this.connection.getStatisticsTracker() != null;
1782 if (!stats && !metrics) {
1783 return;
1784 }
1785 for (Map.Entry<byte[], MultiResponse.RegionResult> regionStats : results.entrySet()) {
1786 byte[] regionName = regionStats.getKey();
1787 ClientProtos.RegionLoadStats stat = regionStats.getValue().getStat();
1788 ResultStatsUtil.updateStats(AsyncProcess.this.connection.getStatisticsTracker(), server,
1789 regionName, stat);
1790 ResultStatsUtil.updateStats(AsyncProcess.this.connection.getConnectionMetrics(),
1791 server, regionName, stat);
1792 }
1793 }
1794
1795 @VisibleForTesting
1796 <CResult> AsyncRequestFutureImpl<CResult> createAsyncRequestFuture(
1797 TableName tableName, List<Action<Row>> actions, long nonceGroup, ExecutorService pool,
1798 Batch.Callback<CResult> callback, Object[] results, boolean needResults,
1799 PayloadCarryingServerCallable callable, int operationTimeout, int rpcTimeout) {
1800 return new AsyncRequestFutureImpl<CResult>(
1801 tableName, actions, nonceGroup, getPool(pool), needResults,
1802 results, callback, callable, operationTimeout, rpcTimeout);
1803 }
1804
1805
1806
1807
1808 @VisibleForTesting
1809 protected RpcRetryingCaller<MultiResponse> createCaller(PayloadCarryingServerCallable callable,
1810 int rpcTimeout) {
1811 return rpcCallerFactory.<MultiResponse> newCaller(rpcTimeout);
1812 }
1813
1814 @VisibleForTesting
1815
1816 void waitUntilDone() throws InterruptedIOException {
1817 waitForMaximumCurrentTasks(0, null);
1818 }
1819
1820
1821 private void waitForMaximumCurrentTasks(int max, String tableName)
1822 throws InterruptedIOException {
1823 waitForMaximumCurrentTasks(max, tasksInProgress, id, tableName);
1824 }
1825
1826
1827 @VisibleForTesting
1828 void waitForMaximumCurrentTasks(int max, final AtomicLong tasksInProgress, final long id,
1829 String tableName) throws InterruptedIOException {
1830 long lastLog = EnvironmentEdgeManager.currentTime();
1831 long currentInProgress, oldInProgress = Long.MAX_VALUE;
1832 while ((currentInProgress = tasksInProgress.get()) > max) {
1833 if (oldInProgress != currentInProgress) {
1834 long now = EnvironmentEdgeManager.currentTime();
1835 if (now > lastLog + 10000) {
1836 lastLog = now;
1837 LOG.info("#" + id + ", waiting for some tasks to finish. Expected max="
1838 + max + ", tasksInProgress=" + currentInProgress +
1839 " hasError=" + hasError() + (tableName == null ? "" : ", tableName=" + tableName));
1840 if (currentInProgress <= thresholdToLogUndoneTaskDetails) {
1841 logDetailsOfUndoneTasks(currentInProgress);
1842 }
1843 }
1844 }
1845 oldInProgress = currentInProgress;
1846 try {
1847 synchronized (tasksInProgress) {
1848 if (tasksInProgress.get() == oldInProgress) {
1849 tasksInProgress.wait(10);
1850 }
1851 }
1852 } catch (InterruptedException e) {
1853 throw new InterruptedIOException("#" + id + ", interrupted." +
1854 " currentNumberOfTask=" + currentInProgress);
1855 }
1856 }
1857 }
1858
1859 private void logDetailsOfUndoneTasks(long taskInProgress) {
1860 ArrayList<ServerName> servers = new ArrayList<ServerName>();
1861 for (Map.Entry<ServerName, AtomicInteger> entry : taskCounterPerServer.entrySet()) {
1862 if (entry.getValue().get() > 0) {
1863 servers.add(entry.getKey());
1864 }
1865 }
1866 LOG.info("Left over " + taskInProgress + " task(s) are processed on server(s): " + servers);
1867 if (taskInProgress <= THRESHOLD_TO_LOG_REGION_DETAILS) {
1868 ArrayList<String> regions = new ArrayList<String>();
1869 for (Map.Entry<byte[], AtomicInteger> entry : taskCounterPerRegion.entrySet()) {
1870 if (entry.getValue().get() > 0) {
1871 regions.add(Bytes.toString(entry.getKey()));
1872 }
1873 }
1874 LOG.info("Regions against which left over task(s) are processed: " + regions);
1875 }
1876 }
1877
1878
1879
1880
1881
1882
1883 public boolean hasError() {
1884 return globalErrors.hasErrors();
1885 }
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897 public RetriesExhaustedWithDetailsException waitForAllPreviousOpsAndReset(
1898 List<Row> failedRows, String tableName) throws InterruptedIOException {
1899 waitForMaximumCurrentTasks(0, tableName);
1900 if (!globalErrors.hasErrors()) {
1901 return null;
1902 }
1903 if (failedRows != null) {
1904 failedRows.addAll(globalErrors.actions);
1905 }
1906 RetriesExhaustedWithDetailsException result = globalErrors.makeException(logBatchErrorDetails);
1907 globalErrors.clear();
1908 return result;
1909 }
1910
1911
1912
1913
1914 protected void incTaskCounters(Collection<byte[]> regions, ServerName sn) {
1915 tasksInProgress.incrementAndGet();
1916
1917 AtomicInteger serverCnt = taskCounterPerServer.get(sn);
1918 if (serverCnt == null) {
1919 taskCounterPerServer.putIfAbsent(sn, new AtomicInteger());
1920 serverCnt = taskCounterPerServer.get(sn);
1921 }
1922 serverCnt.incrementAndGet();
1923
1924 for (byte[] regBytes : regions) {
1925 AtomicInteger regionCnt = taskCounterPerRegion.get(regBytes);
1926 if (regionCnt == null) {
1927 regionCnt = new AtomicInteger();
1928 AtomicInteger oldCnt = taskCounterPerRegion.putIfAbsent(regBytes, regionCnt);
1929 if (oldCnt != null) {
1930 regionCnt = oldCnt;
1931 }
1932 }
1933 regionCnt.incrementAndGet();
1934 }
1935 }
1936
1937
1938
1939
1940 protected void decTaskCounters(Collection<byte[]> regions, ServerName sn) {
1941 for (byte[] regBytes : regions) {
1942 AtomicInteger regionCnt = taskCounterPerRegion.get(regBytes);
1943 regionCnt.decrementAndGet();
1944 }
1945
1946 taskCounterPerServer.get(sn).decrementAndGet();
1947 tasksInProgress.decrementAndGet();
1948 synchronized (tasksInProgress) {
1949 tasksInProgress.notifyAll();
1950 }
1951 }
1952
1953
1954
1955
1956
1957
1958
1959
1960 protected ConnectionManager.ServerErrorTracker createServerErrorTracker() {
1961 return new ConnectionManager.ServerErrorTracker(
1962 this.serverTrackerTimeout, this.numTries);
1963 }
1964
1965 private static boolean isReplicaGet(Row row) {
1966 return (row instanceof Get) && (((Get)row).getConsistency() == Consistency.TIMELINE);
1967 }
1968
1969
1970
1971
1972 private enum Retry {
1973 YES,
1974 NO_LOCATION_PROBLEM,
1975 NO_NOT_RETRIABLE,
1976 NO_RETRIES_EXHAUSTED,
1977 NO_OTHER_SUCCEEDED
1978 }
1979
1980
1981
1982
1983 @VisibleForTesting
1984 static class RowCheckerHost {
1985 private final List<RowChecker> checkers;
1986 private boolean isEnd = false;
1987 RowCheckerHost(final List<RowChecker> checkers) {
1988 this.checkers = checkers;
1989 }
1990 void reset() throws InterruptedIOException {
1991 isEnd = false;
1992 InterruptedIOException e = null;
1993 for (RowChecker checker : checkers) {
1994 try {
1995 checker.reset();
1996 } catch (InterruptedIOException ex) {
1997 e = ex;
1998 }
1999 }
2000 if (e != null) {
2001 throw e;
2002 }
2003 }
2004 ReturnCode canTakeOperation(HRegionLocation loc, long rowSize) {
2005 if (isEnd) {
2006 return ReturnCode.END;
2007 }
2008 ReturnCode code = ReturnCode.INCLUDE;
2009 for (RowChecker checker : checkers) {
2010 switch (checker.canTakeOperation(loc, rowSize)) {
2011 case END:
2012 isEnd = true;
2013 code = ReturnCode.END;
2014 break;
2015 case SKIP:
2016 code = ReturnCode.SKIP;
2017 break;
2018 case INCLUDE:
2019 default:
2020 break;
2021 }
2022 if (code == ReturnCode.END) {
2023 break;
2024 }
2025 }
2026 for (RowChecker checker : checkers) {
2027 checker.notifyFinal(code, loc, rowSize);
2028 }
2029 return code;
2030 }
2031 }
2032
2033
2034
2035
2036 @VisibleForTesting
2037 interface RowChecker {
2038 enum ReturnCode {
2039
2040
2041
2042 INCLUDE,
2043
2044
2045
2046 SKIP,
2047
2048
2049
2050 END
2051 };
2052 ReturnCode canTakeOperation(HRegionLocation loc, long rowSize);
2053
2054
2055
2056
2057
2058 void notifyFinal(ReturnCode code, HRegionLocation loc, long rowSize);
2059
2060
2061
2062 void reset() throws InterruptedIOException ;
2063 }
2064
2065
2066
2067
2068
2069
2070 @VisibleForTesting
2071 static class SubmittedSizeChecker implements RowChecker {
2072 private final long maxHeapSizeSubmit;
2073 private long heapSize = 0;
2074 SubmittedSizeChecker(final long maxHeapSizeSubmit) {
2075 this.maxHeapSizeSubmit = maxHeapSizeSubmit;
2076 }
2077 @Override
2078 public ReturnCode canTakeOperation(HRegionLocation loc, long rowSize) {
2079 if (heapSize >= maxHeapSizeSubmit) {
2080 return ReturnCode.END;
2081 }
2082 return ReturnCode.INCLUDE;
2083 }
2084
2085 @Override
2086 public void notifyFinal(ReturnCode code, HRegionLocation loc, long rowSize) {
2087 if (code == ReturnCode.INCLUDE) {
2088 heapSize += rowSize;
2089 }
2090 }
2091
2092 @Override
2093 public void reset() {
2094 heapSize = 0;
2095 }
2096 }
2097
2098
2099
2100 @VisibleForTesting
2101 static class TaskCountChecker implements RowChecker {
2102 private static final long MAX_WAITING_TIME = 1000;
2103 private final Set<HRegionInfo> regionsIncluded = new HashSet<>();
2104 private final Set<ServerName> serversIncluded = new HashSet<>();
2105 private final int maxConcurrentTasksPerRegion;
2106 private final int maxTotalConcurrentTasks;
2107 private final int maxConcurrentTasksPerServer;
2108 private final Map<byte[], AtomicInteger> taskCounterPerRegion;
2109 private final Map<ServerName, AtomicInteger> taskCounterPerServer;
2110 private final Set<byte[]> busyRegions = new TreeSet<>(Bytes.BYTES_COMPARATOR);
2111 private final AtomicLong tasksInProgress;
2112 TaskCountChecker(final int maxTotalConcurrentTasks,
2113 final int maxConcurrentTasksPerServer,
2114 final int maxConcurrentTasksPerRegion,
2115 final AtomicLong tasksInProgress,
2116 final Map<ServerName, AtomicInteger> taskCounterPerServer,
2117 final Map<byte[], AtomicInteger> taskCounterPerRegion) {
2118 this.maxTotalConcurrentTasks = maxTotalConcurrentTasks;
2119 this.maxConcurrentTasksPerRegion = maxConcurrentTasksPerRegion;
2120 this.maxConcurrentTasksPerServer = maxConcurrentTasksPerServer;
2121 this.taskCounterPerRegion = taskCounterPerRegion;
2122 this.taskCounterPerServer = taskCounterPerServer;
2123 this.tasksInProgress = tasksInProgress;
2124 }
2125 @Override
2126 public void reset() throws InterruptedIOException {
2127
2128 waitForRegion();
2129 regionsIncluded.clear();
2130 serversIncluded.clear();
2131 busyRegions.clear();
2132 }
2133 private void waitForRegion() throws InterruptedIOException {
2134 if (busyRegions.isEmpty()) {
2135 return;
2136 }
2137 EnvironmentEdge ee = EnvironmentEdgeManager.getDelegate();
2138 final long start = ee.currentTime();
2139 while ((ee.currentTime() - start) <= MAX_WAITING_TIME) {
2140 for (byte[] region : busyRegions) {
2141 AtomicInteger count = taskCounterPerRegion.get(region);
2142 if (count == null || count.get() < maxConcurrentTasksPerRegion) {
2143 return;
2144 }
2145 }
2146 try {
2147 synchronized (tasksInProgress) {
2148 tasksInProgress.wait(10);
2149 }
2150 } catch (InterruptedException e) {
2151 throw new InterruptedIOException("Interrupted." +
2152 " tasksInProgress=" + tasksInProgress);
2153 }
2154 }
2155 }
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165 @Override
2166 public ReturnCode canTakeOperation(HRegionLocation loc, long rowSize) {
2167
2168 HRegionInfo regionInfo = loc.getRegionInfo();
2169 if (regionsIncluded.contains(regionInfo)) {
2170
2171 return ReturnCode.INCLUDE;
2172 }
2173 AtomicInteger regionCnt = taskCounterPerRegion.get(loc.getRegionInfo().getRegionName());
2174 if (regionCnt != null && regionCnt.get() >= maxConcurrentTasksPerRegion) {
2175
2176 return ReturnCode.SKIP;
2177 }
2178 int newServers = serversIncluded.size()
2179 + (serversIncluded.contains(loc.getServerName()) ? 0 : 1);
2180 if ((newServers + tasksInProgress.get()) > maxTotalConcurrentTasks) {
2181
2182 return ReturnCode.SKIP;
2183 }
2184 AtomicInteger serverCnt = taskCounterPerServer.get(loc.getServerName());
2185 if (serverCnt != null && serverCnt.get() >= maxConcurrentTasksPerServer) {
2186
2187 return ReturnCode.SKIP;
2188 }
2189 return ReturnCode.INCLUDE;
2190 }
2191
2192 @Override
2193 public void notifyFinal(ReturnCode code, HRegionLocation loc, long rowSize) {
2194 if (code == ReturnCode.INCLUDE) {
2195 regionsIncluded.add(loc.getRegionInfo());
2196 serversIncluded.add(loc.getServerName());
2197 }
2198 busyRegions.add(loc.getRegionInfo().getRegionName());
2199 }
2200 }
2201
2202
2203
2204
2205 @VisibleForTesting
2206 static class RequestSizeChecker implements RowChecker {
2207 private final long maxHeapSizePerRequest;
2208 private final Map<ServerName, Long> serverRequestSizes = new HashMap<>();
2209 RequestSizeChecker(final long maxHeapSizePerRequest) {
2210 this.maxHeapSizePerRequest = maxHeapSizePerRequest;
2211 }
2212 @Override
2213 public void reset() {
2214 serverRequestSizes.clear();
2215 }
2216 @Override
2217 public ReturnCode canTakeOperation(HRegionLocation loc, long rowSize) {
2218
2219 long currentRequestSize = serverRequestSizes.containsKey(loc.getServerName()) ?
2220 serverRequestSizes.get(loc.getServerName()) : 0L;
2221
2222 if (currentRequestSize == 0 || currentRequestSize + rowSize <= maxHeapSizePerRequest) {
2223 return ReturnCode.INCLUDE;
2224 }
2225 return ReturnCode.SKIP;
2226 }
2227
2228 @Override
2229 public void notifyFinal(ReturnCode code, HRegionLocation loc, long rowSize) {
2230 if (code == ReturnCode.INCLUDE) {
2231 long currentRequestSize = serverRequestSizes.containsKey(loc.getServerName()) ?
2232 serverRequestSizes.get(loc.getServerName()) : 0L;
2233 serverRequestSizes.put(loc.getServerName(), currentRequestSize + rowSize);
2234 }
2235 }
2236 }
2237
2238 public static class ListRowAccess<T> implements RowAccess<T> {
2239 private final List<T> data;
2240 ListRowAccess(final List<T> data) {
2241 this.data = data;
2242 }
2243
2244 @Override
2245 public int size() {
2246 return data.size();
2247 }
2248
2249 @Override
2250 public boolean isEmpty() {
2251 return data.isEmpty();
2252 }
2253
2254 @Override
2255 public Iterator<T> iterator() {
2256 return data.iterator();
2257 }
2258 }
2259 }