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 @VisibleForTesting
1313 long getActionsInProgress() {
1314 return actionsInProgress.get();
1315 }
1316
1317
1318
1319
1320
1321
1322
1323
1324 private void receiveMultiAction(MultiAction<Row> multiAction,
1325 ServerName server, MultiResponse responses, int numAttempt) {
1326 assert responses != null;
1327
1328
1329
1330
1331
1332
1333
1334 List<Action<Row>> toReplay = new ArrayList<Action<Row>>();
1335 Throwable throwable = null;
1336 int failureCount = 0;
1337 Retry canRetry = null;
1338
1339 Map<byte[], MultiResponse.RegionResult> results = responses.getResults();
1340 updateStats(server, results);
1341
1342 int failed = 0;
1343 int stopped = 0;
1344
1345 for (Map.Entry<byte[], List<Action<Row>>> regionEntry : multiAction.actions.entrySet()) {
1346 byte[] regionName = regionEntry.getKey();
1347 Throwable regionException = responses.getExceptions().get(regionName);
1348 if (tableName == null && regionException != null &&
1349 ClientExceptionsUtil.isMetaClearingException(regionException)) {
1350
1351
1352
1353 connection.clearCaches(server);
1354 }
1355 Map<Integer, Object> regionResults;
1356 if (results.containsKey(regionName)) {
1357 regionResults = results.get(regionName).result;
1358 } else {
1359 regionResults = Collections.emptyMap();
1360 }
1361 boolean regionFailureRegistered = false;
1362 for (Action<Row> sentAction : regionEntry.getValue()) {
1363 Object result = regionResults.get(sentAction.getOriginalIndex());
1364 if (result == null) {
1365 if (regionException == null) {
1366 LOG.error("Server sent us neither results nor exceptions for " + Bytes
1367 .toStringBinary(regionName) + ", numAttempt:" + numAttempt);
1368 regionException = new RuntimeException("Invalid response");
1369 }
1370
1371
1372 result = regionException;
1373 }
1374
1375 if (result instanceof Throwable) {
1376 Row row = sentAction.getAction();
1377 throwable = regionException != null ? regionException
1378 : ClientExceptionsUtil.findException(result);
1379
1380 if (!regionFailureRegistered) {
1381 regionFailureRegistered = true;
1382 try {
1383 connection.updateCachedLocations(
1384 tableName, regionName, row.getRow(), result, server);
1385 } catch (Throwable ex) {
1386
1387
1388 LOG.error("Couldn't update cached region locations: " + ex);
1389 }
1390 }
1391 if (canRetry == null) {
1392 errorsByServer.reportServerError(server);
1393
1394 canRetry =
1395 errorsByServer.canRetryMore(numAttempt) ? Retry.YES : Retry.NO_RETRIES_EXHAUSTED;
1396 }
1397 ++failureCount;
1398 switch (manageError(sentAction.getOriginalIndex(), row, canRetry, (Throwable) result,
1399 server)) {
1400 case YES:
1401 toReplay.add(sentAction);
1402 break;
1403 case NO_OTHER_SUCCEEDED:
1404 ++stopped;
1405 break;
1406 default:
1407 ++failed;
1408 break;
1409 }
1410 } else {
1411 if (callback != null) {
1412 try {
1413
1414
1415 this.callback.update(regionName, sentAction.getAction().getRow(), (CResult) result);
1416 } catch (Throwable t) {
1417 LOG.error("User callback threw an exception for "
1418 + Bytes.toStringBinary(regionName) + ", ignoring", t);
1419 }
1420 }
1421 setResult(sentAction, result);
1422 }
1423 }
1424 }
1425 if (toReplay.isEmpty()) {
1426 logNoResubmit(server, numAttempt, failureCount, throwable, failed, stopped);
1427 } else {
1428 resubmit(server, toReplay, numAttempt, failureCount, throwable);
1429 }
1430 }
1431
1432 private String createLog(int numAttempt, int failureCount, int replaySize, ServerName sn,
1433 Throwable error, long backOffTime, boolean willRetry, String startTime,
1434 int failed, int stopped) {
1435 StringBuilder sb = new StringBuilder();
1436 sb.append("#").append(id).append(", table=").append(tableName).append(", ")
1437 .append("attempt=").append(numAttempt)
1438 .append("/").append(numTries).append(" ");
1439
1440 if (failureCount > 0 || error != null){
1441 sb.append("failed=").append(failureCount).append("ops").append(", last exception: ").
1442 append(error == null ? "null" : error);
1443 } else {
1444 sb.append("succeeded");
1445 }
1446
1447 sb.append(" on ").append(sn).append(", tracking started ").append(startTime);
1448
1449 if (willRetry) {
1450 sb.append(", retrying after=").append(backOffTime).append("ms").
1451 append(", replay=").append(replaySize).append("ops");
1452 } else if (failureCount > 0) {
1453 if (stopped > 0) {
1454 sb.append("; not retrying ").append(stopped).append(" due to success from other replica");
1455 }
1456 if (failed > 0) {
1457 sb.append("; not retrying ").append(failed).append(" - final failure");
1458 }
1459
1460 }
1461
1462 return sb.toString();
1463 }
1464
1465
1466
1467
1468
1469
1470 private void setResult(Action<Row> action, Object result) {
1471 if (result == null) {
1472 throw new RuntimeException("Result cannot be null");
1473 }
1474 ReplicaResultState state = null;
1475 boolean isStale = !RegionReplicaUtil.isDefaultReplica(action.getReplicaId());
1476 int index = action.getOriginalIndex();
1477 if (results == null) {
1478 decActionCounter(index);
1479 return;
1480 } else if ((state = trySetResultSimple(
1481 index, action.getAction(), false, result, null, isStale)) == null) {
1482 return;
1483 }
1484 assert state != null;
1485
1486
1487
1488
1489
1490 synchronized (state) {
1491 if (state.callCount == 0) {
1492 return;
1493 }
1494 state.callCount = 0;
1495 }
1496 synchronized (replicaResultLock) {
1497 if (results[index] != state) {
1498 throw new AssertionError("We set the callCount but someone else replaced the result");
1499 }
1500 results[index] = result;
1501 }
1502
1503 decActionCounter(index);
1504 }
1505
1506
1507
1508
1509
1510
1511
1512
1513 private void setError(int index, Row row, Throwable throwable, ServerName server) {
1514 ReplicaResultState state = null;
1515 if (results == null) {
1516
1517
1518
1519 errors.add(throwable, row, server);
1520 decActionCounter(index);
1521 return;
1522 } else if ((state = trySetResultSimple(
1523 index, row, true, throwable, server, false)) == null) {
1524 return;
1525 }
1526 assert state != null;
1527 BatchErrors target = null;
1528 boolean isActionDone = false;
1529 synchronized (state) {
1530 switch (state.callCount) {
1531 case 0: return;
1532 case 1: {
1533 target = errors;
1534 isActionDone = true;
1535 break;
1536 }
1537 default: {
1538 assert state.callCount > 1;
1539 if (state.replicaErrors == null) {
1540 state.replicaErrors = new BatchErrors();
1541 }
1542 target = state.replicaErrors;
1543 break;
1544 }
1545 }
1546 --state.callCount;
1547 }
1548 target.add(throwable, row, server);
1549 if (isActionDone) {
1550 if (state.replicaErrors != null) {
1551 errors.merge(state.replicaErrors);
1552 }
1553
1554 synchronized (replicaResultLock) {
1555 if (results[index] != state) {
1556 throw new AssertionError("We set the callCount but someone else replaced the result");
1557 }
1558 results[index] = throwable;
1559 }
1560 decActionCounter(index);
1561 }
1562 }
1563
1564
1565
1566
1567
1568
1569
1570
1571 private boolean isActionComplete(int index, Row row) {
1572 if (!isReplicaGet(row)) return false;
1573 Object resObj = results[index];
1574 return (resObj != null) && (!(resObj instanceof ReplicaResultState)
1575 || ((ReplicaResultState)resObj).callCount == 0);
1576 }
1577
1578
1579
1580
1581
1582 private ReplicaResultState trySetResultSimple(int index, Row row, boolean isError,
1583 Object result, ServerName server, boolean isFromReplica) {
1584 Object resObj = null;
1585 if (!isReplicaGet(row)) {
1586 if (isFromReplica) {
1587 throw new AssertionError("Unexpected stale result for " + row);
1588 }
1589 results[index] = result;
1590 } else {
1591 synchronized (replicaResultLock) {
1592 if ((resObj = results[index]) == null) {
1593 if (isFromReplica) {
1594 throw new AssertionError("Unexpected stale result for " + row);
1595 }
1596 results[index] = result;
1597 }
1598 }
1599 }
1600
1601 ReplicaResultState rrs =
1602 (resObj instanceof ReplicaResultState) ? (ReplicaResultState)resObj : null;
1603 if (rrs == null && isError) {
1604
1605 errors.add((Throwable)result, row, server);
1606 }
1607
1608 if (resObj == null) {
1609
1610 decActionCounter(index);
1611 return null;
1612 }
1613 return rrs;
1614 }
1615
1616 private void decActionCounter(int index) {
1617 long actionsRemaining = actionsInProgress.decrementAndGet();
1618 if (actionsRemaining < 0) {
1619 String error = buildDetailedErrorMsg("Incorrect actions in progress", index);
1620 throw new AssertionError(error);
1621 } else if (actionsRemaining == 0) {
1622 synchronized (actionsInProgress) {
1623 actionsInProgress.notifyAll();
1624 }
1625 }
1626 }
1627
1628 private String buildDetailedErrorMsg(String string, int index) {
1629 StringBuilder error = new StringBuilder(string);
1630 error.append("; called for ").
1631 append(index).
1632 append(", actionsInProgress ").
1633 append(actionsInProgress.get()).
1634 append("; replica gets: ");
1635 if (replicaGetIndices != null) {
1636 for (int i = 0; i < replicaGetIndices.length; ++i) {
1637 error.append(replicaGetIndices[i]).append(", ");
1638 }
1639 } else {
1640 error.append(hasAnyReplicaGets ? "all" : "none");
1641 }
1642 error.append("; results ");
1643 if (results != null) {
1644 for (int i = 0; i < results.length; ++i) {
1645 Object o = results[i];
1646 error.append(((o == null) ? "null" : o.toString())).append(", ");
1647 }
1648 }
1649 return error.toString();
1650 }
1651
1652 @Override
1653 public void waitUntilDone() throws InterruptedIOException {
1654 try {
1655 waitUntilDone(Long.MAX_VALUE);
1656 } catch (InterruptedException iex) {
1657 throw new InterruptedIOException(iex.getMessage());
1658 } finally {
1659 if (callsInProgress != null) {
1660 for (PayloadCarryingServerCallable clb : callsInProgress) {
1661 clb.cancel();
1662 }
1663 }
1664 }
1665 }
1666
1667 private boolean waitUntilDone(long cutoff) throws InterruptedException {
1668 boolean hasWait = cutoff != Long.MAX_VALUE;
1669 long lastLog = EnvironmentEdgeManager.currentTime();
1670 long currentInProgress;
1671 while (0 != (currentInProgress = actionsInProgress.get())) {
1672 long now = EnvironmentEdgeManager.currentTime();
1673 if (hasWait && (now * 1000L) > cutoff) {
1674 return false;
1675 }
1676 if (!hasWait) {
1677 if (now > lastLog + 10000) {
1678 lastLog = now;
1679 LOG.info("#" + id + ", waiting for " + currentInProgress
1680 + " actions to finish on table: " + tableName);
1681 if (currentInProgress <= thresholdToLogUndoneTaskDetails) {
1682 logDetailsOfUndoneTasks(currentInProgress);
1683 }
1684 }
1685 }
1686 synchronized (actionsInProgress) {
1687 if (actionsInProgress.get() == 0) break;
1688 if (!hasWait) {
1689 actionsInProgress.wait(10);
1690 } else {
1691 long waitMicroSecond = Math.min(100000L, (cutoff - now * 1000L));
1692 TimeUnit.MICROSECONDS.timedWait(actionsInProgress, waitMicroSecond);
1693 }
1694 }
1695 }
1696 return true;
1697 }
1698
1699 @Override
1700 public boolean hasError() {
1701 return errors.hasErrors();
1702 }
1703
1704 @Override
1705 public List<? extends Row> getFailedOperations() {
1706 return errors.actions;
1707 }
1708
1709 @Override
1710 public RetriesExhaustedWithDetailsException getErrors() {
1711 return errors.makeException();
1712 }
1713
1714 @Override
1715 public Object[] getResults() throws InterruptedIOException {
1716 waitUntilDone();
1717 return results;
1718 }
1719
1720
1721
1722
1723 @VisibleForTesting
1724 protected MultiServerCallable<Row> createCallable(final ServerName server,
1725 TableName tableName, final MultiAction<Row> multi) {
1726 return new MultiServerCallable<Row>(connection, tableName, server,
1727 AsyncProcess.this.rpcFactory, multi, rpcTimeout, tracker);
1728 }
1729 }
1730
1731 private void updateStats(ServerName server, Map<byte[], MultiResponse.RegionResult> results) {
1732 boolean metrics = AsyncProcess.this.connection.getConnectionMetrics() != null;
1733 boolean stats = AsyncProcess.this.connection.getStatisticsTracker() != null;
1734 if (!stats && !metrics) {
1735 return;
1736 }
1737 for (Map.Entry<byte[], MultiResponse.RegionResult> regionStats : results.entrySet()) {
1738 byte[] regionName = regionStats.getKey();
1739 ClientProtos.RegionLoadStats stat = regionStats.getValue().getStat();
1740 ResultStatsUtil.updateStats(AsyncProcess.this.connection.getStatisticsTracker(), server,
1741 regionName, stat);
1742 ResultStatsUtil.updateStats(AsyncProcess.this.connection.getConnectionMetrics(),
1743 server, regionName, stat);
1744 }
1745 }
1746
1747 protected <CResult> AsyncRequestFutureImpl<CResult> createAsyncRequestFuture(
1748 TableName tableName, List<Action<Row>> actions, long nonceGroup, ExecutorService pool,
1749 Batch.Callback<CResult> callback, Object[] results, boolean needResults,
1750 PayloadCarryingServerCallable callable, int operationTimeout, int rpcTimeout) {
1751 return new AsyncRequestFutureImpl<CResult>(
1752 tableName, actions, nonceGroup, getPool(pool), needResults,
1753 results, callback, callable, operationTimeout, rpcTimeout);
1754 }
1755
1756 @VisibleForTesting
1757
1758 protected <CResult> AsyncRequestFutureImpl<CResult> createAsyncRequestFuture(
1759 TableName tableName, List<Action<Row>> actions, long nonceGroup, ExecutorService pool,
1760 Batch.Callback<CResult> callback, Object[] results, boolean needResults) {
1761 return createAsyncRequestFuture(
1762 tableName, actions, nonceGroup, pool, callback, results, needResults, null,
1763 operationTimeout, rpcTimeout);
1764 }
1765
1766
1767
1768
1769 @VisibleForTesting
1770 protected RpcRetryingCaller<MultiResponse> createCaller(PayloadCarryingServerCallable callable,
1771 int rpcTimeout) {
1772 return rpcCallerFactory.<MultiResponse> newCaller(rpcTimeout);
1773 }
1774
1775 @VisibleForTesting
1776
1777 void waitUntilDone() throws InterruptedIOException {
1778 waitForMaximumCurrentTasks(0, null);
1779 }
1780
1781
1782 private void waitForMaximumCurrentTasks(int max, String tableName)
1783 throws InterruptedIOException {
1784 waitForMaximumCurrentTasks(max, tasksInProgress, id, tableName);
1785 }
1786
1787
1788 @VisibleForTesting
1789 void waitForMaximumCurrentTasks(int max, final AtomicLong tasksInProgress, final long id,
1790 String tableName) throws InterruptedIOException {
1791 long lastLog = EnvironmentEdgeManager.currentTime();
1792 long currentInProgress, oldInProgress = Long.MAX_VALUE;
1793 while ((currentInProgress = tasksInProgress.get()) > max) {
1794 if (oldInProgress != currentInProgress) {
1795 long now = EnvironmentEdgeManager.currentTime();
1796 if (now > lastLog + 10000) {
1797 lastLog = now;
1798 LOG.info("#" + id + ", waiting for some tasks to finish. Expected max="
1799 + max + ", tasksInProgress=" + currentInProgress +
1800 " hasError=" + hasError() + (tableName == null ? "" : ", tableName=" + tableName));
1801 if (currentInProgress <= thresholdToLogUndoneTaskDetails) {
1802 logDetailsOfUndoneTasks(currentInProgress);
1803 }
1804 }
1805 }
1806 oldInProgress = currentInProgress;
1807 try {
1808 synchronized (tasksInProgress) {
1809 if (tasksInProgress.get() == oldInProgress) {
1810 tasksInProgress.wait(10);
1811 }
1812 }
1813 } catch (InterruptedException e) {
1814 throw new InterruptedIOException("#" + id + ", interrupted." +
1815 " currentNumberOfTask=" + currentInProgress);
1816 }
1817 }
1818 }
1819
1820 private void logDetailsOfUndoneTasks(long taskInProgress) {
1821 ArrayList<ServerName> servers = new ArrayList<ServerName>();
1822 for (Map.Entry<ServerName, AtomicInteger> entry : taskCounterPerServer.entrySet()) {
1823 if (entry.getValue().get() > 0) {
1824 servers.add(entry.getKey());
1825 }
1826 }
1827 LOG.info("Left over " + taskInProgress + " task(s) are processed on server(s): " + servers);
1828 if (taskInProgress <= THRESHOLD_TO_LOG_REGION_DETAILS) {
1829 ArrayList<String> regions = new ArrayList<String>();
1830 for (Map.Entry<byte[], AtomicInteger> entry : taskCounterPerRegion.entrySet()) {
1831 if (entry.getValue().get() > 0) {
1832 regions.add(Bytes.toString(entry.getKey()));
1833 }
1834 }
1835 LOG.info("Regions against which left over task(s) are processed: " + regions);
1836 }
1837 }
1838
1839
1840
1841
1842
1843
1844 public boolean hasError() {
1845 return globalErrors.hasErrors();
1846 }
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858 public RetriesExhaustedWithDetailsException waitForAllPreviousOpsAndReset(
1859 List<Row> failedRows, String tableName) throws InterruptedIOException {
1860 waitForMaximumCurrentTasks(0, tableName);
1861 if (!globalErrors.hasErrors()) {
1862 return null;
1863 }
1864 if (failedRows != null) {
1865 failedRows.addAll(globalErrors.actions);
1866 }
1867 RetriesExhaustedWithDetailsException result = globalErrors.makeException();
1868 globalErrors.clear();
1869 return result;
1870 }
1871
1872
1873
1874
1875 protected void incTaskCounters(Collection<byte[]> regions, ServerName sn) {
1876 tasksInProgress.incrementAndGet();
1877
1878 AtomicInteger serverCnt = taskCounterPerServer.get(sn);
1879 if (serverCnt == null) {
1880 taskCounterPerServer.putIfAbsent(sn, new AtomicInteger());
1881 serverCnt = taskCounterPerServer.get(sn);
1882 }
1883 serverCnt.incrementAndGet();
1884
1885 for (byte[] regBytes : regions) {
1886 AtomicInteger regionCnt = taskCounterPerRegion.get(regBytes);
1887 if (regionCnt == null) {
1888 regionCnt = new AtomicInteger();
1889 AtomicInteger oldCnt = taskCounterPerRegion.putIfAbsent(regBytes, regionCnt);
1890 if (oldCnt != null) {
1891 regionCnt = oldCnt;
1892 }
1893 }
1894 regionCnt.incrementAndGet();
1895 }
1896 }
1897
1898
1899
1900
1901 protected void decTaskCounters(Collection<byte[]> regions, ServerName sn) {
1902 for (byte[] regBytes : regions) {
1903 AtomicInteger regionCnt = taskCounterPerRegion.get(regBytes);
1904 regionCnt.decrementAndGet();
1905 }
1906
1907 taskCounterPerServer.get(sn).decrementAndGet();
1908 tasksInProgress.decrementAndGet();
1909 synchronized (tasksInProgress) {
1910 tasksInProgress.notifyAll();
1911 }
1912 }
1913
1914
1915
1916
1917
1918
1919
1920
1921 protected ConnectionManager.ServerErrorTracker createServerErrorTracker() {
1922 return new ConnectionManager.ServerErrorTracker(
1923 this.serverTrackerTimeout, this.numTries);
1924 }
1925
1926 private static boolean isReplicaGet(Row row) {
1927 return (row instanceof Get) && (((Get)row).getConsistency() == Consistency.TIMELINE);
1928 }
1929
1930
1931
1932
1933 private enum Retry {
1934 YES,
1935 NO_LOCATION_PROBLEM,
1936 NO_NOT_RETRIABLE,
1937 NO_RETRIES_EXHAUSTED,
1938 NO_OTHER_SUCCEEDED
1939 }
1940 }