*/
@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<byte[],Future<PeekingResultIterator>>> futures = new ArrayList<Pair<byte[],Future<PeekingResultIterator>>>(numSplits);
final UUID scanId = UUID.randomUUID();
try {
ExecutorService executor = services.getExecutor();
for (KeyRange split : splits) {
final Scan splitScan = new Scan(this.context.getScan());
// Intersect with existing start/stop key if the table is salted
// If not salted, we've already intersected it. If salted, we need
// to wait until now to intersect, as we're running parallel scans
// on all the possible regions here.
if (tableRef.getTable().getBucketNum() != null) {
KeyRange minMaxRange = context.getMinMaxRange();
if (minMaxRange != null) {
// Add salt byte based on current split, as minMaxRange won't have it
minMaxRange = SaltingUtil.addSaltByte(split.getLowerRange(), minMaxRange);
split = split.intersect(minMaxRange);
}
}
if (ScanUtil.intersectScanRange(splitScan, split.getLowerRange(), split.getUpperRange(), this.context.getScanRanges().useSkipScanFilter())) {
// Delay the swapping of start/stop row until row so we don't muck with the intersect logic
ScanUtil.swapStartStopRowIfReversed(splitScan);
Future<PeekingResultIterator> future =
executor.submit(new JobCallable<PeekingResultIterator>() {
@Override
public PeekingResultIterator call() throws Exception {
// TODO: different HTableInterfaces for each thread or the same is better?
long startTime = System.currentTimeMillis();
ResultIterator scanner = new TableResultIterator(context, tableRef, splitScan);
if (logger.isDebugEnabled()) {
logger.debug("Id: " + scanId + ", Time: " + (System.currentTimeMillis() - startTime) + "ms, Scan: " + splitScan);
}
return iteratorFactory.newIterator(scanner);
}
/**
* Defines the grouping for round robin behavior. All threads spawned to process
* this scan will be grouped together and time sliced with other simultaneously
* executing parallel scans.
*/
@Override
public Object getJobId() {
return ParallelIterators.this;
}
});
futures.add(new Pair<byte[],Future<PeekingResultIterator>>(split.getLowerRange(),future));
}
}
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 predicatble 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<byte[],Future<PeekingResultIterator>>>() {
@Override