1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client;
20
21 import com.google.common.annotations.VisibleForTesting;
22
23 import java.io.IOException;
24 import java.io.InterruptedIOException;
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.CancellationException;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.atomic.AtomicBoolean;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.conf.Configuration;
38 import org.apache.hadoop.hbase.DoNotRetryIOException;
39 import org.apache.hadoop.hbase.HRegionInfo;
40 import org.apache.hadoop.hbase.RegionLocations;
41 import org.apache.hadoop.hbase.TableName;
42 import org.apache.hadoop.hbase.classification.InterfaceAudience;
43 import org.apache.hadoop.hbase.client.ScannerCallable.MoreResults;
44 import org.apache.hadoop.hbase.util.Pair;
45
46
47
48
49
50
51
52
53
54
55
56
57
58 @InterfaceAudience.Private
59 class ScannerCallableWithReplicas implements RetryingCallable<Result[]> {
60 private static final Log LOG = LogFactory.getLog(ScannerCallableWithReplicas.class);
61 volatile ScannerCallable currentScannerCallable;
62 AtomicBoolean replicaSwitched = new AtomicBoolean(false);
63 final ClusterConnection cConnection;
64 protected final ExecutorService pool;
65 protected final int timeBeforeReplicas;
66 private final Scan scan;
67 private final int retries;
68 private Result lastResult;
69 private final RpcRetryingCaller<Result[]> caller;
70 private final TableName tableName;
71 private Configuration conf;
72 private int scannerTimeout;
73 private Set<ScannerCallable> outstandingCallables = new HashSet<ScannerCallable>();
74 private boolean someRPCcancelled = false;
75 private int regionReplication = 0;
76
77 public ScannerCallableWithReplicas(TableName tableName, ClusterConnection cConnection,
78 ScannerCallable baseCallable, ExecutorService pool, int timeBeforeReplicas, Scan scan,
79 int retries, int scannerTimeout, int caching, Configuration conf,
80 RpcRetryingCaller<Result []> caller) {
81 this.currentScannerCallable = baseCallable;
82 this.cConnection = cConnection;
83 this.pool = pool;
84 if (timeBeforeReplicas < 0) {
85 throw new IllegalArgumentException("Invalid value of operation timeout on the primary");
86 }
87 this.timeBeforeReplicas = timeBeforeReplicas;
88 this.scan = scan;
89 this.retries = retries;
90 this.tableName = tableName;
91 this.conf = conf;
92 this.scannerTimeout = scannerTimeout;
93 this.caller = caller;
94 }
95
96 public void setClose() {
97 currentScannerCallable.setClose();
98 }
99
100 public void setRenew(boolean val) {
101 currentScannerCallable.setRenew(val);
102 }
103
104 public void setCaching(int caching) {
105 currentScannerCallable.setCaching(caching);
106 }
107
108 public int getCaching() {
109 return currentScannerCallable.getCaching();
110 }
111
112 public HRegionInfo getHRegionInfo() {
113 return currentScannerCallable.getHRegionInfo();
114 }
115
116 public MoreResults moreResultsInRegion() {
117 return currentScannerCallable.moreResultsInRegion();
118 }
119
120 public MoreResults moreResultsForScan() {
121 return currentScannerCallable.moreResultsForScan();
122 }
123
124 @Override
125 public Result [] call(int timeout) throws IOException {
126
127
128
129
130 if (currentScannerCallable != null && currentScannerCallable.closed) {
131
132
133 if (LOG.isTraceEnabled()) {
134 LOG.trace("Closing scanner id=" + currentScannerCallable.scannerId);
135 }
136 Result[] r = currentScannerCallable.call(timeout);
137 currentScannerCallable = null;
138 return r;
139 }
140
141
142
143
144
145
146
147
148
149
150 if (regionReplication <= 0) {
151 RegionLocations rl = null;
152 try {
153 rl = RpcRetryingCallerWithReadReplicas.getRegionLocations(true,
154 RegionReplicaUtil.DEFAULT_REPLICA_ID, cConnection, tableName,
155 currentScannerCallable.getRow());
156 } catch (RetriesExhaustedException | DoNotRetryIOException e) {
157
158
159 if (cConnection instanceof ConnectionManager.HConnectionImplementation) {
160 rl = ((ConnectionManager.HConnectionImplementation) cConnection)
161 .getCachedLocation(tableName, currentScannerCallable.getRow());
162 if (rl == null) {
163 throw e;
164 }
165 } else {
166
167 throw e;
168 }
169 }
170 regionReplication = rl.size();
171 }
172
173
174 ResultBoundedCompletionService<Pair<Result[], ScannerCallable>> cs =
175 new ResultBoundedCompletionService<Pair<Result[], ScannerCallable>>(
176 RpcRetryingCallerFactory.instantiate(ScannerCallableWithReplicas.this.conf), pool,
177 regionReplication * 5);
178
179 AtomicBoolean done = new AtomicBoolean(false);
180 replicaSwitched.set(false);
181
182 addCallsForCurrentReplica(cs);
183 int startIndex = 0;
184
185 try {
186
187 Future<Pair<Result[], ScannerCallable>> f = cs.poll(timeBeforeReplicas,
188 TimeUnit.MICROSECONDS);
189 if (f != null) {
190
191 Pair<Result[], ScannerCallable> r = f.get();
192 if (r != null && r.getSecond() != null) {
193 updateCurrentlyServingReplica(r.getSecond(), r.getFirst(), done, pool);
194 }
195 return r == null ? null : r.getFirst();
196 }
197 } catch (ExecutionException e) {
198
199 if (LOG.isDebugEnabled()) {
200 LOG.debug("Scan with primary region returns " + e.getCause());
201 }
202
203
204
205 if ((regionReplication == 1) || (scan.getConsistency() == Consistency.STRONG)) {
206
207 RpcRetryingCallerWithReadReplicas.throwEnrichedException(e, retries);
208 }
209
210 startIndex = 1;
211 } catch (CancellationException e) {
212 throw new InterruptedIOException(e.getMessage());
213 } catch (InterruptedException e) {
214 throw new InterruptedIOException(e.getMessage());
215 }
216
217
218 int endIndex = regionReplication;
219 if (scan.getConsistency() == Consistency.STRONG) {
220
221 endIndex = 1;
222 } else {
223
224 addCallsForOtherReplicas(cs, 0, regionReplication - 1);
225 }
226
227 try {
228 Future<Pair<Result[], ScannerCallable>> f = cs.pollForFirstSuccessfullyCompletedTask(timeout,
229 TimeUnit.MILLISECONDS, startIndex, endIndex);
230
231 if (f == null) {
232 throw new IOException("Failed to get result within timeout, timeout=" + timeout + "ms");
233 }
234 Pair<Result[], ScannerCallable> r = f.get();
235
236 if (r != null && r.getSecond() != null) {
237 updateCurrentlyServingReplica(r.getSecond(), r.getFirst(), done, pool);
238 }
239 return r == null ? null : r.getFirst();
240
241 } catch (ExecutionException e) {
242 RpcRetryingCallerWithReadReplicas.throwEnrichedException(e, retries);
243 } catch (CancellationException e) {
244 throw new InterruptedIOException(e.getMessage());
245 } catch (InterruptedException e) {
246 throw new InterruptedIOException(e.getMessage());
247 } finally {
248
249
250 cs.cancelAll();
251 }
252 LOG.error("Imposible? Arrive at an unreachable line...");
253 throw new IOException("Imposible? Arrive at an unreachable line...");
254 }
255
256 private void updateCurrentlyServingReplica(ScannerCallable scanner, Result[] result,
257 AtomicBoolean done, ExecutorService pool) {
258 if (done.compareAndSet(false, true)) {
259 if (currentScannerCallable != scanner) replicaSwitched.set(true);
260 currentScannerCallable = scanner;
261
262 if (result != null && result.length != 0) this.lastResult = result[result.length - 1];
263 if (LOG.isTraceEnabled()) {
264 LOG.trace("Setting current scanner as id=" + currentScannerCallable.scannerId +
265 " associated with replica=" + currentScannerCallable.getHRegionInfo().getReplicaId());
266 }
267
268 outstandingCallables.remove(scanner);
269 for (ScannerCallable s : outstandingCallables) {
270 if (LOG.isTraceEnabled()) {
271 LOG.trace("Closing scanner id=" + s.scannerId +
272 ", replica=" + s.getHRegionInfo().getRegionId() +
273 " because slow and replica=" +
274 this.currentScannerCallable.getHRegionInfo().getReplicaId() + " succeeded");
275 }
276
277
278
279 s.setClose();
280 final RetryingRPC r = new RetryingRPC(s);
281 pool.submit(new Callable<Void>(){
282 @Override
283 public Void call() throws Exception {
284 r.call(scannerTimeout);
285 return null;
286 }
287 });
288 }
289
290 outstandingCallables.clear();
291 }
292 }
293
294
295
296
297
298
299 public boolean switchedToADifferentReplica() {
300 return replicaSwitched.get();
301 }
302
303
304
305
306
307
308
309 public boolean isHeartbeatMessage() {
310 return currentScannerCallable != null && currentScannerCallable.isHeartbeatMessage();
311 }
312
313 public Cursor getCursor() {
314 return currentScannerCallable != null ? currentScannerCallable.getCursor() : null;
315 }
316
317 private void addCallsForCurrentReplica(
318 ResultBoundedCompletionService<Pair<Result[], ScannerCallable>> cs) {
319 RetryingRPC retryingOnReplica = new RetryingRPC(currentScannerCallable);
320 outstandingCallables.add(currentScannerCallable);
321 cs.submit(retryingOnReplica, scannerTimeout, currentScannerCallable.id);
322 }
323
324 private void addCallsForOtherReplicas(
325 ResultBoundedCompletionService<Pair<Result[], ScannerCallable>> cs, int min, int max) {
326
327 for (int id = min; id <= max; id++) {
328 if (currentScannerCallable.id == id) {
329 continue;
330 }
331 ScannerCallable s = currentScannerCallable.getScannerCallableForReplica(id);
332 setStartRowForReplicaCallable(s);
333 outstandingCallables.add(s);
334 RetryingRPC retryingOnReplica = new RetryingRPC(s);
335 cs.submit(retryingOnReplica, scannerTimeout, id);
336 }
337 }
338
339
340
341
342
343 private void setStartRowForReplicaCallable(ScannerCallable callable) {
344 if (this.lastResult == null || callable == null) {
345 return;
346 }
347
348
349
350
351
352
353
354 callable.getScan().withStartRow(this.lastResult.getRow(), this.lastResult.mayHaveMoreCellsInRow());
355 }
356
357 @VisibleForTesting
358 boolean isAnyRPCcancelled() {
359 return someRPCcancelled;
360 }
361
362 class RetryingRPC implements RetryingCallable<Pair<Result[], ScannerCallable>>, Cancellable {
363 final ScannerCallable callable;
364 RpcRetryingCaller<Result[]> caller;
365 private volatile boolean cancelled = false;
366
367 RetryingRPC(ScannerCallable callable) {
368 this.callable = callable;
369
370
371
372
373
374 this.caller = ScannerCallableWithReplicas.this.caller;
375 if (scan.getConsistency() == Consistency.TIMELINE) {
376 this.caller = RpcRetryingCallerFactory.instantiate(ScannerCallableWithReplicas.this.conf)
377 .<Result[]>newCaller();
378 }
379 }
380
381 @Override
382 public Pair<Result[], ScannerCallable> call(int callTimeout) throws IOException {
383
384
385 if (cancelled) {
386 return null;
387 }
388 Result[] res = this.caller.callWithoutRetries(this.callable, callTimeout);
389 return new Pair<Result[], ScannerCallable>(res, this.callable);
390 }
391
392 @Override
393 public void prepare(boolean reload) throws IOException {
394 if (cancelled) return;
395
396 if (Thread.interrupted()) {
397 throw new InterruptedIOException();
398 }
399
400 callable.prepare(reload);
401 }
402
403 @Override
404 public void throwable(Throwable t, boolean retrying) {
405 callable.throwable(t, retrying);
406 }
407
408 @Override
409 public String getExceptionMessageAdditionalDetail() {
410 return callable.getExceptionMessageAdditionalDetail();
411 }
412
413 @Override
414 public long sleep(long pause, int tries) {
415 return callable.sleep(pause, tries);
416 }
417
418 @Override
419 public void cancel() {
420 cancelled = true;
421 caller.cancel();
422 if (callable.getController() != null) {
423 callable.getController().startCancel();
424 }
425 someRPCcancelled = true;
426 }
427
428 @Override
429 public boolean isCancelled() {
430 return cancelled;
431 }
432 }
433
434 @Override
435 public void prepare(boolean reload) throws IOException {
436 }
437
438 @Override
439 public void throwable(Throwable t, boolean retrying) {
440 currentScannerCallable.throwable(t, retrying);
441 }
442
443 @Override
444 public String getExceptionMessageAdditionalDetail() {
445 return currentScannerCallable.getExceptionMessageAdditionalDetail();
446 }
447
448 @Override
449 public long sleep(long pause, int tries) {
450 return currentScannerCallable.sleep(pause, tries);
451 }
452 }