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