package com.bigdata.relation.rule.eval.pipeline; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import com.bigdata.bop.IBindingSet; import com.bigdata.btree.UnisolatedReadWriteIndex; import com.bigdata.journal.ITx; import com.bigdata.journal.Journal; import com.bigdata.relation.accesspath.IAsynchronousIterator; import com.bigdata.relation.accesspath.IBuffer; import com.bigdata.relation.rule.IRule; import com.bigdata.relation.rule.eval.IJoinNexus; import com.bigdata.relation.rule.eval.ISolution; import com.bigdata.service.IBigdataFederation; /** * Implementation for local join execution on a {@link Journal}. * <p> * Note: Just like a nested subquery join, when used for mutation this must * read and write on the {@link ITx#UNISOLATED} indices and an * {@link UnisolatedReadWriteIndex} will be used to serialize exclusive access * to the unisolated index for writers while allowing readers concurrent access. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id: LocalJoinMasterTask.java 2625 2010-04-16 21:11:44Z mrpersonick * $ */ public class LocalJoinMasterTask extends JoinMasterTask { /** * @param rule * @param joinNexus * @param buffer */ public LocalJoinMasterTask(final IRule rule, final IJoinNexus joinNexus, final IBuffer<ISolution[]> buffer) { super(rule, joinNexus, buffer); if ((joinNexus.getIndexManager() instanceof IBigdataFederation<?>) && (((IBigdataFederation<?>) joinNexus.getIndexManager()) .isScaleOut())) { /* * This implementation can not be used with a scale-out * federation. */ throw new UnsupportedOperationException(); } } /** * Applies an initial {@link IBindingSet} to the first join dimension. * Intermediate {@link IBindingSet}s will propagate to each join * dimension. The final {@link IBindingSet}s will be generated by the * last join dimension and written on the {@link #getSolutionBuffer()}. * * @return The {@link Future} for the {@link LocalJoinTask} for each * join dimension. */ @Override protected List<Future<Void>> start() throws Exception { // source for each join dimension. final IAsynchronousIterator<IBindingSet[]>[] sources = new IAsynchronousIterator[tailCount]; // source for the 1st join dimension. sources[0] = newBindingSetIterator(joinNexus.newBindingSet(rule)); // Future for each JoinTask. final List<Future<Void>> futures = new ArrayList<Future<Void>>(tailCount); // the previous JoinTask and null iff this is the first join dimension. LocalJoinTask priorJoinTask = null; // for each predicate in the evaluate order. for (int orderIndex = 0; orderIndex < tailCount; orderIndex++) { // true iff this is the last JOIN in the evaluation order. final boolean lastJoin = orderIndex + 1 == tailCount; // source for this join dimension. final IAsynchronousIterator<IBindingSet[]> src = sources[orderIndex]; assert src != null : "No source: orderIndex=" + orderIndex + ", tailCount=" + tailCount + ", rule=" + rule; // create the local join task. final LocalJoinTask joinTask = new LocalJoinTask(/*indexName, */rule, joinNexus, order, orderIndex, this/* master */, masterUUID, src, getSolutionBuffer(), ruleState.getRequiredVars()); if (!lastJoin) { // source for the next join dimension. sources[orderIndex + 1] = joinTask.syncBuffer.iterator(); } /* * Submit the JoinTask. * * When the JoinTask for the 1st join dimension executes it will * consume the [initialBindingSet]. That bindingSet will be used to * obtain the first access path and merged with the elements drawn * from that access path. Intermediate bindingSets will be * propagated to the JoinTask for the next predicate in the * evaluation order. * * Note: This creates the FutureTasks in one pass and sets them on * the various references. It then goes through a second pass to * start the tasks running. This way the [priorJoinTask] (if any) * will always have its sinkFuture set before it begins to execute. */ final FutureTask<Void> ft = new FutureTask<Void>(joinTask); // Save reference to the Future. futures.add(ft); // Set the Future on the BlockingBuffer. if (!lastJoin) { joinTask.syncBuffer.setFuture(ft); } // Set the Future on the JoinTask for the previous join dimension. if (priorJoinTask != null) { priorJoinTask.setSinkFuture(ft); } priorJoinTask = joinTask; } // The JoinTasks will be run on this service. final ExecutorService executorService = joinNexus.getIndexManager().getExecutorService(); // Submit the JoinTask for execution. for(Future<Void> f : futures) { executorService.execute((FutureTask<Void>) f); } return futures; } }