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