* @return the result iterators for the scan of each region
*/
@Override
public List<PeekingResultIterator> getIterators() throws SQLException {
boolean success = false;
final ConnectionQueryServices services = context.getConnection().getQueryServices();
ReadOnlyProps props = services.getProps();
int numSplits = splits.size();
List<PeekingResultIterator> iterators = new ArrayList<PeekingResultIterator>(numSplits);
List<Pair<KeyRange,Future<PeekingResultIterator>>> futures = new ArrayList<Pair<KeyRange,Future<PeekingResultIterator>>>(numSplits);
final UUID scanId = UUID.randomUUID();
try {
submitWork(scanId, splits, futures);
int timeoutMs = props.getInt(QueryServices.THREAD_TIMEOUT_MS_ATTRIB, DEFAULT_THREAD_TIMEOUT_MS);
final int factor = ScanUtil.isReversed(this.context.getScan()) ? -1 : 1;
// Sort futures by row key so that we have a predictable order we're getting rows back for scans.
// We're going to wait here until they're finished anyway and this makes testing much easier.
Collections.sort(futures, new Comparator<Pair<KeyRange,Future<PeekingResultIterator>>>() {
@Override
public int compare(Pair<KeyRange, Future<PeekingResultIterator>> o1, Pair<KeyRange, Future<PeekingResultIterator>> o2) {
return factor * Bytes.compareTo(o1.getFirst().getLowerRange(), o2.getFirst().getLowerRange());
}
});
boolean clearedCache = false;
byte[] tableName = tableRef.getTable().getPhysicalName().getBytes();
for (Pair<KeyRange,Future<PeekingResultIterator>> future : futures) {
try {
PeekingResultIterator iterator = future.getSecond().get(timeoutMs, TimeUnit.MILLISECONDS);
iterators.add(iterator);
} catch (ExecutionException e) {
try { // Rethrow as SQLException
throw ServerUtil.parseServerException(e);
} catch (StaleRegionBoundaryCacheException e2) { // Catch only to try to recover from region boundary cache being out of date
List<Pair<KeyRange,Future<PeekingResultIterator>>> newFutures = new ArrayList<Pair<KeyRange,Future<PeekingResultIterator>>>(2);
if (!clearedCache) { // Clear cache once so that we rejigger job based on new boundaries
services.clearTableRegionCache(tableName);
clearedCache = true;
}
List<KeyRange> allSplits = toKeyRanges(services.getAllTableRegions(tableName));
// Intersect what was the expected boundary with all new region boundaries and
// resubmit just this portion of work again
List<KeyRange> newSubSplits = KeyRange.intersect(Collections.singletonList(future.getFirst()), allSplits);
submitWork(scanId, newSubSplits, newFutures);
for (Pair<KeyRange,Future<PeekingResultIterator>> newFuture : newFutures) {