package com.bigdata.bop.fed.shards;
import java.util.Arrays;
import java.util.Iterator;
import org.apache.log4j.Logger;
import com.bigdata.bop.IBindingSet;
import com.bigdata.mdi.IMetadataIndex;
import com.bigdata.mdi.PartitionLocator;
import com.bigdata.relation.accesspath.IBuffer;
import com.bigdata.striterator.IKeyOrder;
import com.bigdata.util.BytesUtil;
/**
* This does a locatorScan for each binding set. This is a general purpose
* technique, but it will issue one query to the {@link IMetadataIndex} per
* source {@link IBindingSet}.
*
* @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/457">
* "No such index" on cluster under concurrent query workload </a>
*/
class Algorithm_NestedLocatorScan<E extends IBindingSet, F> implements
IShardMapper<E, F> {
static transient private final Logger log = Logger
.getLogger(Algorithm_NestedLocatorScan.class);
private final MapBindingSetsOverShardsBuffer<E, F> op;
public Algorithm_NestedLocatorScan(
final MapBindingSetsOverShardsBuffer<E, F> op) {
this.op = op;
}
public void mapOverShards(final Bundle<F>[] bundles) {
/*
* Sort the binding sets in the chunk by the fromKey associated with
* each asBound predicate.
*/
Arrays.sort(bundles);
// The most recently discovered locator.
PartitionLocator current = null;
// The key order for [current]
IKeyOrder<?> currentKeyOrder = null;
// // The list of binding sets which are bound for the current locator.
// List<IBindingSet> list = new LinkedList<IBindingSet>();
final Iterator<Bundle<F>> bitr = Arrays.asList(bundles).iterator();
while(bitr.hasNext()) {
final Bundle<F> bundle = bitr.next();
if (current != null
&& currentKeyOrder == bundle.keyOrder // same s/o index
&& BytesUtil.rangeCheck(bundle.fromKey, current
.getLeftSeparatorKey(), current
.getRightSeparatorKey())
&& BytesUtil.rangeCheck(bundle.toKey, current
.getLeftSeparatorKey(), current
.getRightSeparatorKey())) {
/*
* Optimization when the bundle fits inside of the last index
* partition scanned (this optimization is only possible when
* the asBound predicate will be mapped onto a single index
* partition, but this is a very common case since we try to
* choose selective indices for access paths).
*
* Note: The bundle MUST be for the scale-out index associated
* with the last PartitionLocator. We enforce this constraint by
* tracking the IKeyOrder for the last PartitionLocator and
* verifying that the Bundle is associated with the same
* IKeyOrder.
*
* Note: Bundle#compareTo() is written to group together the
* [Bundle]s first by their IKeyOrder and then by their fromKey.
* That provides the maximum possibility of reuse of the last
* PartitionLocator. It also provides ordered within scale-out
* index partition locator scans.
*/
final IBuffer<IBindingSet[]> sink = op.getBuffer(current);
sink.add(new IBindingSet[] { bundle.bindingSet });
continue;
}
/*
* Locator scan for the index partitions for that predicate as
* bound.
*/
final Iterator<PartitionLocator> itr = op.locatorScan(
bundle.keyOrder, bundle.fromKey, bundle.toKey);
// Clear the old partition locator.
current = null;
// Update key order for the partition that we are scanning.
currentKeyOrder = bundle.keyOrder;
// Scan locators.
while (itr.hasNext()) {
final PartitionLocator locator = current = itr.next();
if (log.isTraceEnabled())
log.trace("adding bindingSet to buffer" + ": asBound="
+ bundle.asBound + ", partitionId="
+ locator.getPartitionId() + ", dataService="
+ locator.getDataServiceUUID() + ", bindingSet="
+ bundle.bindingSet);
final IBuffer<IBindingSet[]> sink = op.getBuffer(locator);
sink.add(new IBindingSet[] { bundle.bindingSet });
}
}
}
}